View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.impl.auth;
28  
29  import java.nio.charset.Charset;
30  import java.security.Key;
31  import java.security.MessageDigest;
32  import java.security.NoSuchAlgorithmException;
33  import java.security.cert.Certificate;
34  import java.security.cert.CertificateEncodingException;
35  import java.util.Arrays;
36  import java.util.Locale;
37  import java.util.Random;
38  
39  import javax.crypto.Cipher;
40  import javax.crypto.spec.SecretKeySpec;
41  
42  import org.apache.commons.codec.binary.Base64;
43  import org.apache.http.Consts;
44  
45  /**
46   * Provides an implementation for NTLMv1, NTLMv2, and NTLM2 Session forms of the NTLM
47   * authentication protocol.
48   *
49   * @since 4.1
50   */
51  final class NTLMEngineImpl implements NTLMEngine {
52  
53      /** Unicode encoding */
54      private static final Charset UNICODE_LITTLE_UNMARKED = Charset.forName("UnicodeLittleUnmarked");
55      /** Character encoding */
56      private static final Charset DEFAULT_CHARSET = Consts.ASCII;
57  
58      // Flags we use; descriptions according to:
59      // http://davenport.sourceforge.net/ntlm.html
60      // and
61      // http://msdn.microsoft.com/en-us/library/cc236650%28v=prot.20%29.aspx
62      // [MS-NLMP] section 2.2.2.5
63      static final int FLAG_REQUEST_UNICODE_ENCODING = 0x00000001;      // Unicode string encoding requested
64      static final int FLAG_REQUEST_OEM_ENCODING = 0x00000002;      // OEM string encoding requested
65      static final int FLAG_REQUEST_TARGET = 0x00000004;                      // Requests target field
66      static final int FLAG_REQUEST_SIGN = 0x00000010;  // Requests all messages have a signature attached, in NEGOTIATE message.
67      static final int FLAG_REQUEST_SEAL = 0x00000020;  // Request key exchange for message confidentiality in NEGOTIATE message.  MUST be used in conjunction with 56BIT.
68      static final int FLAG_REQUEST_LAN_MANAGER_KEY = 0x00000080;    // Request Lan Manager key instead of user session key
69      static final int FLAG_REQUEST_NTLMv1 = 0x00000200; // Request NTLMv1 security.  MUST be set in NEGOTIATE and CHALLENGE both
70      static final int FLAG_DOMAIN_PRESENT = 0x00001000;        // Domain is present in message
71      static final int FLAG_WORKSTATION_PRESENT = 0x00002000;   // Workstation is present in message
72      static final int FLAG_REQUEST_ALWAYS_SIGN = 0x00008000;   // Requests a signature block on all messages.  Overridden by REQUEST_SIGN and REQUEST_SEAL.
73      static final int FLAG_REQUEST_NTLM2_SESSION = 0x00080000; // From server in challenge, requesting NTLM2 session security
74      static final int FLAG_REQUEST_VERSION = 0x02000000;       // Request protocol version
75      static final int FLAG_TARGETINFO_PRESENT = 0x00800000;    // From server in challenge message, indicating targetinfo is present
76      static final int FLAG_REQUEST_128BIT_KEY_EXCH = 0x20000000; // Request explicit 128-bit key exchange
77      static final int FLAG_REQUEST_EXPLICIT_KEY_EXCH = 0x40000000;     // Request explicit key exchange
78      static final int FLAG_REQUEST_56BIT_ENCRYPTION = 0x80000000;      // Must be used in conjunction with SEAL
79  
80      // Attribute-value identifiers (AvId)
81      // according to [MS-NLMP] section 2.2.2.1
82      static final int MSV_AV_EOL = 0x0000; // Indicates that this is the last AV_PAIR in the list.
83      static final int MSV_AV_NB_COMPUTER_NAME = 0x0001; // The server's NetBIOS computer name.
84      static final int MSV_AV_NB_DOMAIN_NAME = 0x0002; // The server's NetBIOS domain name.
85      static final int MSV_AV_DNS_COMPUTER_NAME = 0x0003; // The fully qualified domain name (FQDN) of the computer.
86      static final int MSV_AV_DNS_DOMAIN_NAME = 0x0004; // The FQDN of the domain.
87      static final int MSV_AV_DNS_TREE_NAME = 0x0005; // The FQDN of the forest.
88      static final int MSV_AV_FLAGS = 0x0006; // A 32-bit value indicating server or client configuration.
89      static final int MSV_AV_TIMESTAMP = 0x0007; // server local time
90      static final int MSV_AV_SINGLE_HOST = 0x0008; // A Single_Host_Data structure.
91      static final int MSV_AV_TARGET_NAME = 0x0009; // The SPN of the target server.
92      static final int MSV_AV_CHANNEL_BINDINGS = 0x000A; // A channel bindings hash.
93  
94      static final int MSV_AV_FLAGS_ACCOUNT_AUTH_CONSTAINED = 0x00000001; // Indicates to the client that the account authentication is constrained.
95      static final int MSV_AV_FLAGS_MIC = 0x00000002; // Indicates that the client is providing message integrity in the MIC field in the AUTHENTICATE_MESSAGE.
96      static final int MSV_AV_FLAGS_UNTRUSTED_TARGET_SPN = 0x00000004; // Indicates that the client is providing a target SPN generated from an untrusted source.
97  
98      /** Secure random generator */
99      private static final java.security.SecureRandom RND_GEN;
100     static {
101         java.security.SecureRandom rnd = null;
102         try {
103             rnd = java.security.SecureRandom.getInstance("SHA1PRNG");
104         } catch (final Exception ignore) {
105         }
106         RND_GEN = rnd;
107     }
108 
109     /** The signature string as bytes in the default encoding */
110     private static final byte[] SIGNATURE = getNullTerminatedAsciiString("NTLMSSP");
111 
112     // Key derivation magic strings for the SIGNKEY algorithm defined in
113     // [MS-NLMP] section 3.4.5.2
114     private static final byte[] SIGN_MAGIC_SERVER = getNullTerminatedAsciiString(
115         "session key to server-to-client signing key magic constant");
116     private static final byte[] SIGN_MAGIC_CLIENT = getNullTerminatedAsciiString(
117         "session key to client-to-server signing key magic constant");
118     private static final byte[] SEAL_MAGIC_SERVER = getNullTerminatedAsciiString(
119         "session key to server-to-client sealing key magic constant");
120     private static final byte[] SEAL_MAGIC_CLIENT = getNullTerminatedAsciiString(
121         "session key to client-to-server sealing key magic constant");
122 
123     // prefix for GSS API channel binding
124     private static final byte[] MAGIC_TLS_SERVER_ENDPOINT = "tls-server-end-point:".getBytes(Consts.ASCII);
125 
126     private static byte[] getNullTerminatedAsciiString( final String source )
127     {
128         final byte[] bytesWithoutNull = source.getBytes(Consts.ASCII);
129         final byte[] target = new byte[bytesWithoutNull.length + 1];
130         System.arraycopy(bytesWithoutNull, 0, target, 0, bytesWithoutNull.length);
131         target[bytesWithoutNull.length] = (byte) 0x00;
132         return target;
133     }
134 
135     private static final String TYPE_1_MESSAGE = new Type1Message().getResponse();
136 
137     NTLMEngineImpl() {
138     }
139 
140     /**
141      * Creates the first message (type 1 message) in the NTLM authentication
142      * sequence. This message includes the user name, domain and host for the
143      * authentication session.
144      *
145      * @param host
146      *            the computer name of the host requesting authentication.
147      * @param domain
148      *            The domain to authenticate with.
149      * @return String the message to add to the HTTP request header.
150      */
151     static String getType1Message(final String host, final String domain) {
152         // For compatibility reason do not include domain and host in type 1 message
153         //return new Type1Message(domain, host).getResponse();
154         return TYPE_1_MESSAGE;
155     }
156 
157     /**
158      * Creates the type 3 message using the given server nonce. The type 3
159      * message includes all the information for authentication, host, domain,
160      * username and the result of encrypting the nonce sent by the server using
161      * the user's password as the key.
162      *
163      * @param user
164      *            The user name. This should not include the domain name.
165      * @param password
166      *            The password.
167      * @param host
168      *            The host that is originating the authentication request.
169      * @param domain
170      *            The domain to authenticate within.
171      * @param nonce
172      *            the 8 byte array the server sent.
173      * @return The type 3 message.
174      * @throws NTLMEngineException
175      *             If {@encrypt(byte[],byte[])} fails.
176      */
177     static String getType3Message(final String user, final String password, final String host, final String domain,
178             final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation)
179             throws NTLMEngineException {
180         return new Type3Message(domain, host, user, password, nonce, type2Flags, target,
181                 targetInformation).getResponse();
182     }
183 
184     /**
185      * Creates the type 3 message using the given server nonce. The type 3
186      * message includes all the information for authentication, host, domain,
187      * username and the result of encrypting the nonce sent by the server using
188      * the user's password as the key.
189      *
190      * @param user
191      *            The user name. This should not include the domain name.
192      * @param password
193      *            The password.
194      * @param host
195      *            The host that is originating the authentication request.
196      * @param domain
197      *            The domain to authenticate within.
198      * @param nonce
199      *            the 8 byte array the server sent.
200      * @return The type 3 message.
201      * @throws NTLMEngineException
202      *             If {@encrypt(byte[],byte[])} fails.
203      */
204     static String getType3Message(final String user, final String password, final String host, final String domain,
205             final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation,
206             final Certificate peerServerCertificate, final byte[] type1Message, final byte[] type2Message)
207             throws NTLMEngineException {
208         return new Type3Message(domain, host, user, password, nonce, type2Flags, target,
209                 targetInformation, peerServerCertificate, type1Message, type2Message).getResponse();
210     }
211 
212     private static int readULong(final byte[] src, final int index) {
213         if (src.length < index + 4) {
214             return 0;
215         }
216         return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8)
217                 | ((src[index + 2] & 0xff) << 16) | ((src[index + 3] & 0xff) << 24);
218     }
219 
220     private static int readUShort(final byte[] src, final int index) {
221         if (src.length < index + 2) {
222             return 0;
223         }
224         return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8);
225     }
226 
227     private static byte[] readSecurityBuffer(final byte[] src, final int index) {
228         final int length = readUShort(src, index);
229         final int offset = readULong(src, index + 4);
230         if (src.length < offset + length) {
231             return new byte[length];
232         }
233         final byte[] buffer = new byte[length];
234         System.arraycopy(src, offset, buffer, 0, length);
235         return buffer;
236     }
237 
238     /** Calculate a challenge block */
239     private static byte[] makeRandomChallenge(final Random random) {
240         final byte[] rval = new byte[8];
241         synchronized (random) {
242             random.nextBytes(rval);
243         }
244         return rval;
245     }
246 
247     /** Calculate a 16-byte secondary key */
248     private static byte[] makeSecondaryKey(final Random random) {
249         final byte[] rval = new byte[16];
250         synchronized (random) {
251             random.nextBytes(rval);
252         }
253         return rval;
254     }
255 
256     protected static class CipherGen {
257 
258         protected final Random random;
259         protected final long currentTime;
260 
261         protected final String domain;
262         protected final String user;
263         protected final String password;
264         protected final byte[] challenge;
265         protected final String target;
266         protected final byte[] targetInformation;
267 
268         // Information we can generate but may be passed in (for testing)
269         protected byte[] clientChallenge;
270         protected byte[] clientChallenge2;
271         protected byte[] secondaryKey;
272         protected byte[] timestamp;
273 
274         // Stuff we always generate
275         protected byte[] lmHash = null;
276         protected byte[] lmResponse = null;
277         protected byte[] ntlmHash = null;
278         protected byte[] ntlmResponse = null;
279         protected byte[] ntlmv2Hash = null;
280         protected byte[] lmv2Hash = null;
281         protected byte[] lmv2Response = null;
282         protected byte[] ntlmv2Blob = null;
283         protected byte[] ntlmv2Response = null;
284         protected byte[] ntlm2SessionResponse = null;
285         protected byte[] lm2SessionResponse = null;
286         protected byte[] lmUserSessionKey = null;
287         protected byte[] ntlmUserSessionKey = null;
288         protected byte[] ntlmv2UserSessionKey = null;
289         protected byte[] ntlm2SessionResponseUserSessionKey = null;
290         protected byte[] lanManagerSessionKey = null;
291 
292         /**
293          * @deprecated Use {@link CipherGen#CipherGen(Random, long, String, String, String, byte[], String, byte[], byte[], byte[], byte[], byte[])}
294          */
295         @Deprecated
296         public CipherGen(final String domain, final String user, final String password,
297             final byte[] challenge, final String target, final byte[] targetInformation,
298             final byte[] clientChallenge, final byte[] clientChallenge2,
299             final byte[] secondaryKey, final byte[] timestamp) {
300             this(RND_GEN, System.currentTimeMillis(),
301                 domain, user, password, challenge, target, targetInformation,
302                 clientChallenge, clientChallenge2,
303                 secondaryKey, timestamp);
304         }
305 
306         public CipherGen(final Random random, final long currentTime,
307             final String domain, final String user, final String password,
308             final byte[] challenge, final String target, final byte[] targetInformation,
309             final byte[] clientChallenge, final byte[] clientChallenge2,
310             final byte[] secondaryKey, final byte[] timestamp) {
311             this.random = random;
312             this.currentTime = currentTime;
313 
314             this.domain = domain;
315             this.target = target;
316             this.user = user;
317             this.password = password;
318             this.challenge = challenge;
319             this.targetInformation = targetInformation;
320             this.clientChallenge = clientChallenge;
321             this.clientChallenge2 = clientChallenge2;
322             this.secondaryKey = secondaryKey;
323             this.timestamp = timestamp;
324         }
325 
326         /**
327          * @deprecated Use {@link CipherGen#CipherGen(Random, long, String, String, String, byte[], String, byte[], byte[], byte[], byte[], byte[])}
328          */
329         @Deprecated
330         public CipherGen(final String domain,
331             final String user,
332             final String password,
333             final byte[] challenge,
334             final String target,
335             final byte[] targetInformation) {
336             this(RND_GEN, System.currentTimeMillis(), domain, user, password, challenge, target, targetInformation);
337         }
338 
339         public CipherGen(final Random random, final long currentTime,
340             final String domain,
341             final String user,
342             final String password,
343             final byte[] challenge,
344             final String target,
345             final byte[] targetInformation) {
346             this(random, currentTime, domain, user, password, challenge, target, targetInformation, null, null, null, null);
347         }
348 
349         /** Calculate and return client challenge */
350         public byte[] getClientChallenge()
351             throws NTLMEngineException {
352             if (clientChallenge == null) {
353                 clientChallenge = makeRandomChallenge(random);
354             }
355             return clientChallenge;
356         }
357 
358         /** Calculate and return second client challenge */
359         public byte[] getClientChallenge2()
360             throws NTLMEngineException {
361             if (clientChallenge2 == null) {
362                 clientChallenge2 = makeRandomChallenge(random);
363             }
364             return clientChallenge2;
365         }
366 
367         /** Calculate and return random secondary key */
368         public byte[] getSecondaryKey()
369             throws NTLMEngineException {
370             if (secondaryKey == null) {
371                 secondaryKey = makeSecondaryKey(random);
372             }
373             return secondaryKey;
374         }
375 
376         /** Calculate and return the LMHash */
377         public byte[] getLMHash()
378             throws NTLMEngineException {
379             if (lmHash == null) {
380                 lmHash = lmHash(password);
381             }
382             return lmHash;
383         }
384 
385         /** Calculate and return the LMResponse */
386         public byte[] getLMResponse()
387             throws NTLMEngineException {
388             if (lmResponse == null) {
389                 lmResponse = lmResponse(getLMHash(),challenge);
390             }
391             return lmResponse;
392         }
393 
394         /** Calculate and return the NTLMHash */
395         public byte[] getNTLMHash()
396             throws NTLMEngineException {
397             if (ntlmHash == null) {
398                 ntlmHash = ntlmHash(password);
399             }
400             return ntlmHash;
401         }
402 
403         /** Calculate and return the NTLMResponse */
404         public byte[] getNTLMResponse()
405             throws NTLMEngineException {
406             if (ntlmResponse == null) {
407                 ntlmResponse = lmResponse(getNTLMHash(),challenge);
408             }
409             return ntlmResponse;
410         }
411 
412         /** Calculate the LMv2 hash */
413         public byte[] getLMv2Hash()
414             throws NTLMEngineException {
415             if (lmv2Hash == null) {
416                 lmv2Hash = lmv2Hash(domain, user, getNTLMHash());
417             }
418             return lmv2Hash;
419         }
420 
421         /** Calculate the NTLMv2 hash */
422         public byte[] getNTLMv2Hash()
423             throws NTLMEngineException {
424             if (ntlmv2Hash == null) {
425                 ntlmv2Hash = ntlmv2Hash(domain, user, getNTLMHash());
426             }
427             return ntlmv2Hash;
428         }
429 
430         /** Calculate a timestamp */
431         public byte[] getTimestamp() {
432             if (timestamp == null) {
433                 long time = this.currentTime;
434                 time += 11644473600000l; // milliseconds from January 1, 1601 -> epoch.
435                 time *= 10000; // tenths of a microsecond.
436                 // convert to little-endian byte array.
437                 timestamp = new byte[8];
438                 for (int i = 0; i < 8; i++) {
439                     timestamp[i] = (byte) time;
440                     time >>>= 8;
441                 }
442             }
443             return timestamp;
444         }
445 
446         /** Calculate the NTLMv2Blob */
447         public byte[] getNTLMv2Blob()
448             throws NTLMEngineException {
449             if (ntlmv2Blob == null) {
450                 ntlmv2Blob = createBlob(getClientChallenge2(), targetInformation, getTimestamp());
451             }
452             return ntlmv2Blob;
453         }
454 
455         /** Calculate the NTLMv2Response */
456         public byte[] getNTLMv2Response()
457             throws NTLMEngineException {
458             if (ntlmv2Response == null) {
459                 ntlmv2Response = lmv2Response(getNTLMv2Hash(),challenge,getNTLMv2Blob());
460             }
461             return ntlmv2Response;
462         }
463 
464         /** Calculate the LMv2Response */
465         public byte[] getLMv2Response()
466             throws NTLMEngineException {
467             if (lmv2Response == null) {
468                 lmv2Response = lmv2Response(getLMv2Hash(),challenge,getClientChallenge());
469             }
470             return lmv2Response;
471         }
472 
473         /** Get NTLM2SessionResponse */
474         public byte[] getNTLM2SessionResponse()
475             throws NTLMEngineException {
476             if (ntlm2SessionResponse == null) {
477                 ntlm2SessionResponse = ntlm2SessionResponse(getNTLMHash(),challenge,getClientChallenge());
478             }
479             return ntlm2SessionResponse;
480         }
481 
482         /** Calculate and return LM2 session response */
483         public byte[] getLM2SessionResponse()
484             throws NTLMEngineException {
485             if (lm2SessionResponse == null) {
486                 final byte[] clntChallenge = getClientChallenge();
487                 lm2SessionResponse = new byte[24];
488                 System.arraycopy(clntChallenge, 0, lm2SessionResponse, 0, clntChallenge.length);
489                 Arrays.fill(lm2SessionResponse, clntChallenge.length, lm2SessionResponse.length, (byte) 0x00);
490             }
491             return lm2SessionResponse;
492         }
493 
494         /** Get LMUserSessionKey */
495         public byte[] getLMUserSessionKey()
496             throws NTLMEngineException {
497             if (lmUserSessionKey == null) {
498                 lmUserSessionKey = new byte[16];
499                 System.arraycopy(getLMHash(), 0, lmUserSessionKey, 0, 8);
500                 Arrays.fill(lmUserSessionKey, 8, 16, (byte) 0x00);
501             }
502             return lmUserSessionKey;
503         }
504 
505         /** Get NTLMUserSessionKey */
506         public byte[] getNTLMUserSessionKey()
507             throws NTLMEngineException {
508             if (ntlmUserSessionKey == null) {
509                 final MD4 md4 = new MD4();
510                 md4.update(getNTLMHash());
511                 ntlmUserSessionKey = md4.getOutput();
512             }
513             return ntlmUserSessionKey;
514         }
515 
516         /** GetNTLMv2UserSessionKey */
517         public byte[] getNTLMv2UserSessionKey()
518             throws NTLMEngineException {
519             if (ntlmv2UserSessionKey == null) {
520                 final byte[] ntlmv2hash = getNTLMv2Hash();
521                 final byte[] truncatedResponse = new byte[16];
522                 System.arraycopy(getNTLMv2Response(), 0, truncatedResponse, 0, 16);
523                 ntlmv2UserSessionKey = hmacMD5(truncatedResponse, ntlmv2hash);
524             }
525             return ntlmv2UserSessionKey;
526         }
527 
528         /** Get NTLM2SessionResponseUserSessionKey */
529         public byte[] getNTLM2SessionResponseUserSessionKey()
530             throws NTLMEngineException {
531             if (ntlm2SessionResponseUserSessionKey == null) {
532                 final byte[] ntlm2SessionResponseNonce = getLM2SessionResponse();
533                 final byte[] sessionNonce = new byte[challenge.length + ntlm2SessionResponseNonce.length];
534                 System.arraycopy(challenge, 0, sessionNonce, 0, challenge.length);
535                 System.arraycopy(ntlm2SessionResponseNonce, 0, sessionNonce, challenge.length, ntlm2SessionResponseNonce.length);
536                 ntlm2SessionResponseUserSessionKey = hmacMD5(sessionNonce,getNTLMUserSessionKey());
537             }
538             return ntlm2SessionResponseUserSessionKey;
539         }
540 
541         /** Get LAN Manager session key */
542         public byte[] getLanManagerSessionKey()
543             throws NTLMEngineException {
544             if (lanManagerSessionKey == null) {
545                 try {
546                     final byte[] keyBytes = new byte[14];
547                     System.arraycopy(getLMHash(), 0, keyBytes, 0, 8);
548                     Arrays.fill(keyBytes, 8, keyBytes.length, (byte)0xbd);
549                     final Key lowKey = createDESKey(keyBytes, 0);
550                     final Key highKey = createDESKey(keyBytes, 7);
551                     final byte[] truncatedResponse = new byte[8];
552                     System.arraycopy(getLMResponse(), 0, truncatedResponse, 0, truncatedResponse.length);
553                     Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
554                     des.init(Cipher.ENCRYPT_MODE, lowKey);
555                     final byte[] lowPart = des.doFinal(truncatedResponse);
556                     des = Cipher.getInstance("DES/ECB/NoPadding");
557                     des.init(Cipher.ENCRYPT_MODE, highKey);
558                     final byte[] highPart = des.doFinal(truncatedResponse);
559                     lanManagerSessionKey = new byte[16];
560                     System.arraycopy(lowPart, 0, lanManagerSessionKey, 0, lowPart.length);
561                     System.arraycopy(highPart, 0, lanManagerSessionKey, lowPart.length, highPart.length);
562                 } catch (final Exception e) {
563                     throw new NTLMEngineException(e.getMessage(), e);
564                 }
565             }
566             return lanManagerSessionKey;
567         }
568     }
569 
570     /** Calculates HMAC-MD5 */
571     static byte[] hmacMD5(final byte[] value, final byte[] key)
572         throws NTLMEngineException {
573         final HMACMD5 hmacMD5 = new HMACMD5(key);
574         hmacMD5.update(value);
575         return hmacMD5.getOutput();
576     }
577 
578     /** Calculates RC4 */
579     static byte[] RC4(final byte[] value, final byte[] key)
580         throws NTLMEngineException {
581         try {
582             final Cipher rc4 = Cipher.getInstance("RC4");
583             rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "RC4"));
584             return rc4.doFinal(value);
585         } catch (final Exception e) {
586             throw new NTLMEngineException(e.getMessage(), e);
587         }
588     }
589 
590     /**
591      * Calculates the NTLM2 Session Response for the given challenge, using the
592      * specified password and client challenge.
593      *
594      * @return The NTLM2 Session Response. This is placed in the NTLM response
595      *         field of the Type 3 message; the LM response field contains the
596      *         client challenge, null-padded to 24 bytes.
597      */
598     static byte[] ntlm2SessionResponse(final byte[] ntlmHash, final byte[] challenge,
599             final byte[] clientChallenge) throws NTLMEngineException {
600         try {
601             final MessageDigest md5 = getMD5();
602             md5.update(challenge);
603             md5.update(clientChallenge);
604             final byte[] digest = md5.digest();
605 
606             final byte[] sessionHash = new byte[8];
607             System.arraycopy(digest, 0, sessionHash, 0, 8);
608             return lmResponse(ntlmHash, sessionHash);
609         } catch (final Exception e) {
610             if (e instanceof NTLMEngineException) {
611                 throw (NTLMEngineException) e;
612             }
613             throw new NTLMEngineException(e.getMessage(), e);
614         }
615     }
616 
617     /**
618      * Creates the LM Hash of the user's password.
619      *
620      * @param password
621      *            The password.
622      *
623      * @return The LM Hash of the given password, used in the calculation of the
624      *         LM Response.
625      */
626     private static byte[] lmHash(final String password) throws NTLMEngineException {
627         try {
628             final byte[] oemPassword = password.toUpperCase(Locale.ROOT).getBytes(Consts.ASCII);
629             final int length = Math.min(oemPassword.length, 14);
630             final byte[] keyBytes = new byte[14];
631             System.arraycopy(oemPassword, 0, keyBytes, 0, length);
632             final Key lowKey = createDESKey(keyBytes, 0);
633             final Key highKey = createDESKey(keyBytes, 7);
634             final byte[] magicConstant = "KGS!@#$%".getBytes(Consts.ASCII);
635             final Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
636             des.init(Cipher.ENCRYPT_MODE, lowKey);
637             final byte[] lowHash = des.doFinal(magicConstant);
638             des.init(Cipher.ENCRYPT_MODE, highKey);
639             final byte[] highHash = des.doFinal(magicConstant);
640             final byte[] lmHash = new byte[16];
641             System.arraycopy(lowHash, 0, lmHash, 0, 8);
642             System.arraycopy(highHash, 0, lmHash, 8, 8);
643             return lmHash;
644         } catch (final Exception e) {
645             throw new NTLMEngineException(e.getMessage(), e);
646         }
647     }
648 
649     /**
650      * Creates the NTLM Hash of the user's password.
651      *
652      * @param password
653      *            The password.
654      *
655      * @return The NTLM Hash of the given password, used in the calculation of
656      *         the NTLM Response and the NTLMv2 and LMv2 Hashes.
657      */
658     private static byte[] ntlmHash(final String password) throws NTLMEngineException {
659         if (UNICODE_LITTLE_UNMARKED == null) {
660             throw new NTLMEngineException("Unicode not supported");
661         }
662         final byte[] unicodePassword = password.getBytes(UNICODE_LITTLE_UNMARKED);
663         final MD4 md4 = new MD4();
664         md4.update(unicodePassword);
665         return md4.getOutput();
666     }
667 
668     /**
669      * Creates the LMv2 Hash of the user's password.
670      *
671      * @return The LMv2 Hash, used in the calculation of the NTLMv2 and LMv2
672      *         Responses.
673      */
674     private static byte[] lmv2Hash(final String domain, final String user, final byte[] ntlmHash)
675             throws NTLMEngineException {
676         if (UNICODE_LITTLE_UNMARKED == null) {
677             throw new NTLMEngineException("Unicode not supported");
678         }
679         final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
680         // Upper case username, upper case domain!
681         hmacMD5.update(user.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
682         if (domain != null) {
683             hmacMD5.update(domain.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
684         }
685         return hmacMD5.getOutput();
686     }
687 
688     /**
689      * Creates the NTLMv2 Hash of the user's password.
690      *
691      * @return The NTLMv2 Hash, used in the calculation of the NTLMv2 and LMv2
692      *         Responses.
693      */
694     private static byte[] ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash)
695             throws NTLMEngineException {
696         if (UNICODE_LITTLE_UNMARKED == null) {
697             throw new NTLMEngineException("Unicode not supported");
698         }
699         final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
700         // Upper case username, mixed case target!!
701         hmacMD5.update(user.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
702         if (domain != null) {
703             hmacMD5.update(domain.getBytes(UNICODE_LITTLE_UNMARKED));
704         }
705         return hmacMD5.getOutput();
706     }
707 
708     /**
709      * Creates the LM Response from the given hash and Type 2 challenge.
710      *
711      * @param hash
712      *            The LM or NTLM Hash.
713      * @param challenge
714      *            The server challenge from the Type 2 message.
715      *
716      * @return The response (either LM or NTLM, depending on the provided hash).
717      */
718     private static byte[] lmResponse(final byte[] hash, final byte[] challenge) throws NTLMEngineException {
719         try {
720             final byte[] keyBytes = new byte[21];
721             System.arraycopy(hash, 0, keyBytes, 0, 16);
722             final Key lowKey = createDESKey(keyBytes, 0);
723             final Key middleKey = createDESKey(keyBytes, 7);
724             final Key highKey = createDESKey(keyBytes, 14);
725             final Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
726             des.init(Cipher.ENCRYPT_MODE, lowKey);
727             final byte[] lowResponse = des.doFinal(challenge);
728             des.init(Cipher.ENCRYPT_MODE, middleKey);
729             final byte[] middleResponse = des.doFinal(challenge);
730             des.init(Cipher.ENCRYPT_MODE, highKey);
731             final byte[] highResponse = des.doFinal(challenge);
732             final byte[] lmResponse = new byte[24];
733             System.arraycopy(lowResponse, 0, lmResponse, 0, 8);
734             System.arraycopy(middleResponse, 0, lmResponse, 8, 8);
735             System.arraycopy(highResponse, 0, lmResponse, 16, 8);
736             return lmResponse;
737         } catch (final Exception e) {
738             throw new NTLMEngineException(e.getMessage(), e);
739         }
740     }
741 
742     /**
743      * Creates the LMv2 Response from the given hash, client data, and Type 2
744      * challenge.
745      *
746      * @param hash
747      *            The NTLMv2 Hash.
748      * @param clientData
749      *            The client data (blob or client challenge).
750      * @param challenge
751      *            The server challenge from the Type 2 message.
752      *
753      * @return The response (either NTLMv2 or LMv2, depending on the client
754      *         data).
755      */
756     private static byte[] lmv2Response(final byte[] hash, final byte[] challenge, final byte[] clientData) {
757         final HMACMD5 hmacMD5 = new HMACMD5(hash);
758         hmacMD5.update(challenge);
759         hmacMD5.update(clientData);
760         final byte[] mac = hmacMD5.getOutput();
761         final byte[] lmv2Response = new byte[mac.length + clientData.length];
762         System.arraycopy(mac, 0, lmv2Response, 0, mac.length);
763         System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length);
764         return lmv2Response;
765     }
766 
767     static enum Mode
768     {
769         CLIENT, SERVER;
770     }
771 
772     static class Handle
773     {
774         final private byte[] exportedSessionKey;
775         private byte[] signingKey;
776         private byte[] sealingKey;
777         private final Cipher rc4;
778         final Mode mode;
779         final private boolean isConnection;
780         int sequenceNumber = 0;
781 
782 
783         Handle( final byte[] exportedSessionKey, final Mode mode, final boolean isConnection )
784             throws NTLMEngineException
785         {
786             this.exportedSessionKey = exportedSessionKey;
787             this.isConnection = isConnection;
788             this.mode = mode;
789             try
790             {
791                 final MessageDigest signMd5 = getMD5();
792                 final MessageDigest sealMd5 = getMD5();
793                 signMd5.update( exportedSessionKey );
794                 sealMd5.update( exportedSessionKey );
795                 if ( mode == Mode.CLIENT )
796                 {
797                     signMd5.update( SIGN_MAGIC_CLIENT );
798                     sealMd5.update( SEAL_MAGIC_CLIENT );
799                 }
800                 else
801                 {
802                     signMd5.update( SIGN_MAGIC_SERVER );
803                     sealMd5.update( SEAL_MAGIC_SERVER );
804                 }
805                 signingKey = signMd5.digest();
806                 sealingKey = sealMd5.digest();
807             }
808             catch ( final Exception e )
809             {
810                 throw new NTLMEngineException( e.getMessage(), e );
811             }
812             rc4 = initCipher();
813         }
814 
815         public byte[] getSigningKey()
816         {
817             return signingKey;
818         }
819 
820 
821         public byte[] getSealingKey()
822         {
823             return sealingKey;
824         }
825 
826         private Cipher initCipher() throws NTLMEngineException
827         {
828             final Cipher cipher;
829             try
830             {
831                 cipher = Cipher.getInstance( "RC4" );
832                 if ( mode == Mode.CLIENT )
833                 {
834                     cipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec( sealingKey, "RC4" ) );
835                 }
836                 else
837                 {
838                     cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( sealingKey, "RC4" ) );
839                 }
840             }
841             catch ( final Exception e )
842             {
843                 throw new NTLMEngineException( e.getMessage(), e );
844             }
845             return cipher;
846         }
847 
848 
849         private void advanceMessageSequence() throws NTLMEngineException
850         {
851             if ( !isConnection )
852             {
853                 final MessageDigest sealMd5 = getMD5();
854                 sealMd5.update( sealingKey );
855                 final byte[] seqNumBytes = new byte[4];
856                 writeULong( seqNumBytes, sequenceNumber, 0 );
857                 sealMd5.update( seqNumBytes );
858                 sealingKey = sealMd5.digest();
859                 initCipher();
860             }
861             sequenceNumber++;
862         }
863 
864         private byte[] encrypt( final byte[] data )
865         {
866             return rc4.update( data );
867         }
868 
869         private byte[] decrypt( final byte[] data )
870         {
871             return rc4.update( data );
872         }
873 
874         private byte[] computeSignature( final byte[] message )
875         {
876             final byte[] sig = new byte[16];
877 
878             // version
879             sig[0] = 0x01;
880             sig[1] = 0x00;
881             sig[2] = 0x00;
882             sig[3] = 0x00;
883 
884             // HMAC (first 8 bytes)
885             final HMACMD5 hmacMD5 = new HMACMD5( signingKey );
886             hmacMD5.update( encodeLong( sequenceNumber ) );
887             hmacMD5.update( message );
888             final byte[] hmac = hmacMD5.getOutput();
889             final byte[] trimmedHmac = new byte[8];
890             System.arraycopy( hmac, 0, trimmedHmac, 0, 8 );
891             final byte[] encryptedHmac = encrypt( trimmedHmac );
892             System.arraycopy( encryptedHmac, 0, sig, 4, 8 );
893 
894             // sequence number
895             encodeLong( sig, 12, sequenceNumber );
896 
897             return sig;
898         }
899 
900         private boolean validateSignature( final byte[] signature, final byte message[] )
901         {
902             final byte[] computedSignature = computeSignature( message );
903             //            log.info( "SSSSS validateSignature("+seqNumber+")\n"
904             //                + "  received: " + DebugUtil.dump( signature ) + "\n"
905             //                + "  computed: " + DebugUtil.dump( computedSignature ) );
906             return Arrays.equals( signature, computedSignature );
907         }
908 
909         public byte[] signAndEncryptMessage( final byte[] cleartextMessage ) throws NTLMEngineException
910         {
911             final byte[] encryptedMessage = encrypt( cleartextMessage );
912             final byte[] signature = computeSignature( cleartextMessage );
913             final byte[] outMessage = new byte[signature.length + encryptedMessage.length];
914             System.arraycopy( signature, 0, outMessage, 0, signature.length );
915             System.arraycopy( encryptedMessage, 0, outMessage, signature.length, encryptedMessage.length );
916             advanceMessageSequence();
917             return outMessage;
918         }
919 
920         public byte[] decryptAndVerifySignedMessage( final byte[] inMessage ) throws NTLMEngineException
921         {
922             final byte[] signature = new byte[16];
923             System.arraycopy( inMessage, 0, signature, 0, signature.length );
924             final byte[] encryptedMessage = new byte[inMessage.length - 16];
925             System.arraycopy( inMessage, 16, encryptedMessage, 0, encryptedMessage.length );
926             final byte[] cleartextMessage = decrypt( encryptedMessage );
927             if ( !validateSignature( signature, cleartextMessage ) )
928             {
929                 throw new NTLMEngineException( "Wrong signature" );
930             }
931             advanceMessageSequence();
932             return cleartextMessage;
933         }
934 
935     }
936 
937     private static byte[] encodeLong( final int value )
938     {
939         final byte[] enc = new byte[4];
940         encodeLong( enc, 0, value );
941         return enc;
942     }
943 
944     private static void encodeLong( final byte[] buf, final int offset, final int value )
945     {
946         buf[offset + 0] = ( byte ) ( value & 0xff );
947         buf[offset + 1] = ( byte ) ( value >> 8 & 0xff );
948         buf[offset + 2] = ( byte ) ( value >> 16 & 0xff );
949         buf[offset + 3] = ( byte ) ( value >> 24 & 0xff );
950     }
951 
952     /**
953      * Creates the NTLMv2 blob from the given target information block and
954      * client challenge.
955      *
956      * @param targetInformation
957      *            The target information block from the Type 2 message.
958      * @param clientChallenge
959      *            The random 8-byte client challenge.
960      *
961      * @return The blob, used in the calculation of the NTLMv2 Response.
962      */
963     private static byte[] createBlob(final byte[] clientChallenge, final byte[] targetInformation, final byte[] timestamp) {
964         final byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 };
965         final byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
966         final byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
967         final byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
968         final byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8
969                 + unknown1.length + targetInformation.length + unknown2.length];
970         int offset = 0;
971         System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
972         offset += blobSignature.length;
973         System.arraycopy(reserved, 0, blob, offset, reserved.length);
974         offset += reserved.length;
975         System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
976         offset += timestamp.length;
977         System.arraycopy(clientChallenge, 0, blob, offset, 8);
978         offset += 8;
979         System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
980         offset += unknown1.length;
981         System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length);
982         offset += targetInformation.length;
983         System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
984         offset += unknown2.length;
985         return blob;
986     }
987 
988     /**
989      * Creates a DES encryption key from the given key material.
990      *
991      * @param bytes
992      *            A byte array containing the DES key material.
993      * @param offset
994      *            The offset in the given byte array at which the 7-byte key
995      *            material starts.
996      *
997      * @return A DES encryption key created from the key material starting at
998      *         the specified offset in the given byte array.
999      */
1000     private static Key createDESKey(final byte[] bytes, final int offset) {
1001         final byte[] keyBytes = new byte[7];
1002         System.arraycopy(bytes, offset, keyBytes, 0, 7);
1003         final byte[] material = new byte[8];
1004         material[0] = keyBytes[0];
1005         material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
1006         material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
1007         material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
1008         material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
1009         material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
1010         material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
1011         material[7] = (byte) (keyBytes[6] << 1);
1012         oddParity(material);
1013         return new SecretKeySpec(material, "DES");
1014     }
1015 
1016     /**
1017      * Applies odd parity to the given byte array.
1018      *
1019      * @param bytes
1020      *            The data whose parity bits are to be adjusted for odd parity.
1021      */
1022     private static void oddParity(final byte[] bytes) {
1023         for (int i = 0; i < bytes.length; i++) {
1024             final byte b = bytes[i];
1025             final boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3)
1026                     ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0;
1027             if (needsParity) {
1028                 bytes[i] |= (byte) 0x01;
1029             } else {
1030                 bytes[i] &= (byte) 0xfe;
1031             }
1032         }
1033     }
1034 
1035     /**
1036      * Find the character set based on the flags.
1037      * @param flags is the flags.
1038      * @return the character set.
1039      */
1040     private static Charset getCharset(final int flags) throws NTLMEngineException
1041     {
1042         if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0) {
1043             return DEFAULT_CHARSET;
1044         }
1045         if (UNICODE_LITTLE_UNMARKED == null) {
1046             throw new NTLMEngineException( "Unicode not supported" );
1047         }
1048         return UNICODE_LITTLE_UNMARKED;
1049     }
1050 
1051     /** Strip dot suffix from a name */
1052     private static String stripDotSuffix(final String value) {
1053         if (value == null) {
1054             return null;
1055         }
1056         final int index = value.indexOf('.');
1057         if (index != -1) {
1058             return value.substring(0, index);
1059         }
1060         return value;
1061     }
1062 
1063     /** Convert host to standard form */
1064     private static String convertHost(final String host) {
1065         return stripDotSuffix(host);
1066     }
1067 
1068     /** Convert domain to standard form */
1069     private static String convertDomain(final String domain) {
1070         return stripDotSuffix(domain);
1071     }
1072 
1073     /** NTLM message generation, base class */
1074     static class NTLMMessage {
1075         /** The current response */
1076         protected byte[] messageContents = null;
1077 
1078         /** The current output position */
1079         protected int currentOutputPosition = 0;
1080 
1081         /** Constructor to use when message contents are not yet known */
1082         NTLMMessage() {
1083         }
1084 
1085         /** Constructor taking a string */
1086         NTLMMessage(final String messageBody, final int expectedType) throws NTLMEngineException {
1087             this(Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET)), expectedType);
1088         }
1089 
1090         /** Constructor to use when message bytes are known */
1091         NTLMMessage(final byte[] message, final int expectedType) throws NTLMEngineException {
1092             messageContents = message;
1093             // Look for NTLM message
1094             if (messageContents.length < SIGNATURE.length) {
1095                 throw new NTLMEngineException("NTLM message decoding error - packet too short");
1096             }
1097             int i = 0;
1098             while (i < SIGNATURE.length) {
1099                 if (messageContents[i] != SIGNATURE[i]) {
1100                     throw new NTLMEngineException(
1101                             "NTLM message expected - instead got unrecognized bytes");
1102                 }
1103                 i++;
1104             }
1105 
1106             // Check to be sure there's a type 2 message indicator next
1107             final int type = readULong(SIGNATURE.length);
1108             if (type != expectedType) {
1109                 throw new NTLMEngineException("NTLM type " + Integer.toString(expectedType)
1110                         + " message expected - instead got type " + Integer.toString(type));
1111             }
1112 
1113             currentOutputPosition = messageContents.length;
1114         }
1115 
1116         /**
1117          * Get the length of the signature and flags, so calculations can adjust
1118          * offsets accordingly.
1119          */
1120         protected int getPreambleLength() {
1121             return SIGNATURE.length + 4;
1122         }
1123 
1124         /** Get the message length */
1125         protected int getMessageLength() {
1126             return currentOutputPosition;
1127         }
1128 
1129         /** Read a byte from a position within the message buffer */
1130         protected byte readByte(final int position) throws NTLMEngineException {
1131             if (messageContents.length < position + 1) {
1132                 throw new NTLMEngineException("NTLM: Message too short");
1133             }
1134             return messageContents[position];
1135         }
1136 
1137         /** Read a bunch of bytes from a position in the message buffer */
1138         protected void readBytes(final byte[] buffer, final int position) throws NTLMEngineException {
1139             if (messageContents.length < position + buffer.length) {
1140                 throw new NTLMEngineException("NTLM: Message too short");
1141             }
1142             System.arraycopy(messageContents, position, buffer, 0, buffer.length);
1143         }
1144 
1145         /** Read a ushort from a position within the message buffer */
1146         protected int readUShort(final int position) throws NTLMEngineException {
1147             return NTLMEngineImpl.readUShort(messageContents, position);
1148         }
1149 
1150         /** Read a ulong from a position within the message buffer */
1151         protected int readULong(final int position) throws NTLMEngineException {
1152             return NTLMEngineImpl.readULong(messageContents, position);
1153         }
1154 
1155         /** Read a security buffer from a position within the message buffer */
1156         protected byte[] readSecurityBuffer(final int position) throws NTLMEngineException {
1157             return NTLMEngineImpl.readSecurityBuffer(messageContents, position);
1158         }
1159 
1160         /**
1161          * Prepares the object to create a response of the given length.
1162          *
1163          * @param maxlength
1164          *            the maximum length of the response to prepare,
1165          *            including the type and the signature (which this method
1166          *            adds).
1167          */
1168         protected void prepareResponse(final int maxlength, final int messageType) {
1169             messageContents = new byte[maxlength];
1170             currentOutputPosition = 0;
1171             addBytes(SIGNATURE);
1172             addULong(messageType);
1173         }
1174 
1175         /**
1176          * Adds the given byte to the response.
1177          *
1178          * @param b
1179          *            the byte to add.
1180          */
1181         protected void addByte(final byte b) {
1182             messageContents[currentOutputPosition] = b;
1183             currentOutputPosition++;
1184         }
1185 
1186         /**
1187          * Adds the given bytes to the response.
1188          *
1189          * @param bytes
1190          *            the bytes to add.
1191          */
1192         protected void addBytes(final byte[] bytes) {
1193             if (bytes == null) {
1194                 return;
1195             }
1196             for (final byte b : bytes) {
1197                 messageContents[currentOutputPosition] = b;
1198                 currentOutputPosition++;
1199             }
1200         }
1201 
1202         /** Adds a USHORT to the response */
1203         protected void addUShort(final int value) {
1204             addByte((byte) (value & 0xff));
1205             addByte((byte) (value >> 8 & 0xff));
1206         }
1207 
1208         /** Adds a ULong to the response */
1209         protected void addULong(final int value) {
1210             addByte((byte) (value & 0xff));
1211             addByte((byte) (value >> 8 & 0xff));
1212             addByte((byte) (value >> 16 & 0xff));
1213             addByte((byte) (value >> 24 & 0xff));
1214         }
1215 
1216         /**
1217          * Returns the response that has been generated after shrinking the
1218          * array if required and base64 encodes the response.
1219          *
1220          * @return The response as above.
1221          */
1222         public String getResponse() {
1223             return new String(Base64.encodeBase64(getBytes()), Consts.ASCII);
1224         }
1225 
1226         public byte[] getBytes() {
1227             if (messageContents == null) {
1228                 buildMessage();
1229             }
1230             final byte[] resp;
1231             if ( messageContents.length > currentOutputPosition ) {
1232                 final byte[] tmp = new byte[currentOutputPosition];
1233                 System.arraycopy( messageContents, 0, tmp, 0, currentOutputPosition );
1234                 messageContents = tmp;
1235             }
1236             return messageContents;
1237         }
1238 
1239         protected void buildMessage() {
1240             throw new RuntimeException("Message builder not implemented for "+getClass().getName());
1241         }
1242     }
1243 
1244     /** Type 1 message assembly class */
1245     static class Type1Message extends NTLMMessage {
1246 
1247         private final byte[] hostBytes;
1248         private final byte[] domainBytes;
1249         private final int flags;
1250 
1251         Type1Message(final String domain, final String host) throws NTLMEngineException {
1252             this(domain, host, null);
1253         }
1254 
1255         Type1Message(final String domain, final String host, final Integer flags) throws NTLMEngineException {
1256             super();
1257             this.flags = ((flags == null)?getDefaultFlags():flags);
1258 
1259             // Strip off domain name from the host!
1260             final String unqualifiedHost = convertHost(host);
1261             // Use only the base domain name!
1262             final String unqualifiedDomain = convertDomain(domain);
1263 
1264             hostBytes = unqualifiedHost != null ?
1265                     unqualifiedHost.getBytes(UNICODE_LITTLE_UNMARKED) : null;
1266             domainBytes = unqualifiedDomain != null ?
1267                     unqualifiedDomain.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED) : null;
1268         }
1269 
1270         Type1Message() {
1271             super();
1272             hostBytes = null;
1273             domainBytes = null;
1274             flags = getDefaultFlags();
1275         }
1276 
1277         private int getDefaultFlags() {
1278             return
1279                 //FLAG_WORKSTATION_PRESENT |
1280                 //FLAG_DOMAIN_PRESENT |
1281 
1282                 // Required flags
1283                 //FLAG_REQUEST_LAN_MANAGER_KEY |
1284                 FLAG_REQUEST_NTLMv1 |
1285                 FLAG_REQUEST_NTLM2_SESSION |
1286 
1287                 // Protocol version request
1288                 FLAG_REQUEST_VERSION |
1289 
1290                 // Recommended privacy settings
1291                 FLAG_REQUEST_ALWAYS_SIGN |
1292                 //FLAG_REQUEST_SEAL |
1293                 //FLAG_REQUEST_SIGN |
1294 
1295                 // These must be set according to documentation, based on use of SEAL above
1296                 FLAG_REQUEST_128BIT_KEY_EXCH |
1297                 FLAG_REQUEST_56BIT_ENCRYPTION |
1298                 //FLAG_REQUEST_EXPLICIT_KEY_EXCH |
1299 
1300                 FLAG_REQUEST_UNICODE_ENCODING;
1301 
1302         }
1303 
1304         /**
1305          * Getting the response involves building the message before returning
1306          * it
1307          */
1308         @Override
1309         protected void buildMessage() {
1310             int domainBytesLength = 0;
1311             if ( domainBytes != null ) {
1312                 domainBytesLength = domainBytes.length;
1313             }
1314             int hostBytesLength = 0;
1315             if ( hostBytes != null ) {
1316                 hostBytesLength = hostBytes.length;
1317             }
1318 
1319             // Now, build the message. Calculate its length first, including
1320             // signature or type.
1321             final int finalLength = 32 + 8 + hostBytesLength + domainBytesLength;
1322 
1323             // Set up the response. This will initialize the signature, message
1324             // type, and flags.
1325             prepareResponse(finalLength, 1);
1326 
1327             // Flags. These are the complete set of flags we support.
1328             addULong(flags);
1329 
1330             // Domain length (two times).
1331             addUShort(domainBytesLength);
1332             addUShort(domainBytesLength);
1333 
1334             // Domain offset.
1335             addULong(hostBytesLength + 32 + 8);
1336 
1337             // Host length (two times).
1338             addUShort(hostBytesLength);
1339             addUShort(hostBytesLength);
1340 
1341             // Host offset (always 32 + 8).
1342             addULong(32 + 8);
1343 
1344             // Version
1345             addUShort(0x0105);
1346             // Build
1347             addULong(2600);
1348             // NTLM revision
1349             addUShort(0x0f00);
1350 
1351             // Host (workstation) String.
1352             if (hostBytes != null) {
1353                 addBytes(hostBytes);
1354             }
1355             // Domain String.
1356             if (domainBytes != null) {
1357                 addBytes(domainBytes);
1358             }
1359         }
1360 
1361     }
1362 
1363     /** Type 2 message class */
1364     static class Type2Message extends NTLMMessage {
1365         protected final byte[] challenge;
1366         protected String target;
1367         protected byte[] targetInfo;
1368         protected final int flags;
1369 
1370         Type2Message(final String messageBody) throws NTLMEngineException {
1371             this(Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET)));
1372         }
1373 
1374         Type2Message(final byte[] message) throws NTLMEngineException {
1375             super(message, 2);
1376 
1377             // Type 2 message is laid out as follows:
1378             // First 8 bytes: NTLMSSP[0]
1379             // Next 4 bytes: Ulong, value 2
1380             // Next 8 bytes, starting at offset 12: target field (2 ushort lengths, 1 ulong offset)
1381             // Next 4 bytes, starting at offset 20: Flags, e.g. 0x22890235
1382             // Next 8 bytes, starting at offset 24: Challenge
1383             // Next 8 bytes, starting at offset 32: ??? (8 bytes of zeros)
1384             // Next 8 bytes, starting at offset 40: targetinfo field (2 ushort lengths, 1 ulong offset)
1385             // Next 2 bytes, major/minor version number (e.g. 0x05 0x02)
1386             // Next 8 bytes, build number
1387             // Next 2 bytes, protocol version number (e.g. 0x00 0x0f)
1388             // Next, various text fields, and a ushort of value 0 at the end
1389 
1390             // Parse out the rest of the info we need from the message
1391             // The nonce is the 8 bytes starting from the byte in position 24.
1392             challenge = new byte[8];
1393             readBytes(challenge, 24);
1394 
1395             flags = readULong(20);
1396 
1397             // Do the target!
1398             target = null;
1399             // The TARGET_DESIRED flag is said to not have understood semantics
1400             // in Type2 messages, so use the length of the packet to decide
1401             // how to proceed instead
1402             if (getMessageLength() >= 12 + 8) {
1403                 final byte[] bytes = readSecurityBuffer(12);
1404                 if (bytes.length != 0) {
1405                     target = new String(bytes, getCharset(flags));
1406                 }
1407             }
1408 
1409             // Do the target info!
1410             targetInfo = null;
1411             // TARGET_DESIRED flag cannot be relied on, so use packet length
1412             if (getMessageLength() >= 40 + 8) {
1413                 final byte[] bytes = readSecurityBuffer(40);
1414                 if (bytes.length != 0) {
1415                     targetInfo = bytes;
1416                 }
1417             }
1418         }
1419 
1420         /** Retrieve the challenge */
1421         byte[] getChallenge() {
1422             return challenge;
1423         }
1424 
1425         /** Retrieve the target */
1426         String getTarget() {
1427             return target;
1428         }
1429 
1430         /** Retrieve the target info */
1431         byte[] getTargetInfo() {
1432             return targetInfo;
1433         }
1434 
1435         /** Retrieve the response flags */
1436         int getFlags() {
1437             return flags;
1438         }
1439 
1440     }
1441 
1442     /** Type 3 message assembly class */
1443     static class Type3Message extends NTLMMessage {
1444         // For mic computation
1445         protected final byte[] type1Message;
1446         protected final byte[] type2Message;
1447         // Response flags from the type2 message
1448         protected final int type2Flags;
1449 
1450         protected final byte[] domainBytes;
1451         protected final byte[] hostBytes;
1452         protected final byte[] userBytes;
1453 
1454         protected byte[] lmResp;
1455         protected byte[] ntResp;
1456         protected final byte[] sessionKey;
1457         protected final byte[] exportedSessionKey;
1458 
1459         protected final boolean computeMic;
1460 
1461         /** More primitive constructor: don't include cert or previous messages.
1462         */
1463         Type3Message(final String domain,
1464             final String host,
1465             final String user,
1466             final String password,
1467             final byte[] nonce,
1468             final int type2Flags,
1469             final String target,
1470             final byte[] targetInformation)
1471             throws NTLMEngineException {
1472             this(domain, host, user, password, nonce, type2Flags, target, targetInformation, null, null, null);
1473         }
1474 
1475         /** More primitive constructor: don't include cert or previous messages.
1476         */
1477         Type3Message(final Random random, final long currentTime,
1478             final String domain,
1479             final String host,
1480             final String user,
1481             final String password,
1482             final byte[] nonce,
1483             final int type2Flags,
1484             final String target,
1485             final byte[] targetInformation)
1486             throws NTLMEngineException {
1487             this(random, currentTime, domain, host, user, password, nonce, type2Flags, target, targetInformation, null, null, null);
1488         }
1489 
1490         /** Constructor. Pass the arguments we will need */
1491         Type3Message(final String domain,
1492             final String host,
1493             final String user,
1494             final String password,
1495             final byte[] nonce,
1496             final int type2Flags,
1497             final String target,
1498             final byte[] targetInformation,
1499             final Certificate peerServerCertificate,
1500             final byte[] type1Message,
1501             final byte[] type2Message)
1502             throws NTLMEngineException {
1503             this(RND_GEN, System.currentTimeMillis(), domain, host, user, password, nonce, type2Flags, target, targetInformation, peerServerCertificate, type1Message, type2Message);
1504         }
1505 
1506         /** Constructor. Pass the arguments we will need */
1507         Type3Message(final Random random, final long currentTime,
1508             final String domain,
1509             final String host,
1510             final String user,
1511             final String password,
1512             final byte[] nonce,
1513             final int type2Flags,
1514             final String target,
1515             final byte[] targetInformation,
1516             final Certificate peerServerCertificate,
1517             final byte[] type1Message,
1518             final byte[] type2Message)
1519             throws NTLMEngineException {
1520 
1521             if (random == null) {
1522                 throw new NTLMEngineException("Random generator not available");
1523             }
1524 
1525             // Save the flags
1526             this.type2Flags = type2Flags;
1527             this.type1Message = type1Message;
1528             this.type2Message = type2Message;
1529 
1530             // Strip off domain name from the host!
1531             final String unqualifiedHost = convertHost(host);
1532             // Use only the base domain name!
1533             final String unqualifiedDomain = convertDomain(domain);
1534 
1535             byte[] responseTargetInformation = targetInformation;
1536             if (peerServerCertificate != null) {
1537                 responseTargetInformation = addGssMicAvsToTargetInfo(targetInformation, peerServerCertificate);
1538                 computeMic = true;
1539             } else {
1540                 computeMic = false;
1541             }
1542 
1543              // Create a cipher generator class.  Use domain BEFORE it gets modified!
1544             final CipherGen gen = new CipherGen(random, currentTime,
1545                 unqualifiedDomain,
1546                 user,
1547                 password,
1548                 nonce,
1549                 target,
1550                 responseTargetInformation);
1551 
1552             // Use the new code to calculate the responses, including v2 if that
1553             // seems warranted.
1554             byte[] userSessionKey;
1555             try {
1556                 // This conditional may not work on Windows Server 2008 R2 and above, where it has not yet
1557                 // been tested
1558                 if (((type2Flags & FLAG_TARGETINFO_PRESENT) != 0) &&
1559                     targetInformation != null && target != null) {
1560                     // NTLMv2
1561                     ntResp = gen.getNTLMv2Response();
1562                     lmResp = gen.getLMv2Response();
1563                     if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1564                         userSessionKey = gen.getLanManagerSessionKey();
1565                     } else {
1566                         userSessionKey = gen.getNTLMv2UserSessionKey();
1567                     }
1568                 } else {
1569                     // NTLMv1
1570                     if ((type2Flags & FLAG_REQUEST_NTLM2_SESSION) != 0) {
1571                         // NTLM2 session stuff is requested
1572                         ntResp = gen.getNTLM2SessionResponse();
1573                         lmResp = gen.getLM2SessionResponse();
1574                         if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1575                             userSessionKey = gen.getLanManagerSessionKey();
1576                         } else {
1577                             userSessionKey = gen.getNTLM2SessionResponseUserSessionKey();
1578                         }
1579                     } else {
1580                         ntResp = gen.getNTLMResponse();
1581                         lmResp = gen.getLMResponse();
1582                         if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1583                             userSessionKey = gen.getLanManagerSessionKey();
1584                         } else {
1585                             userSessionKey = gen.getNTLMUserSessionKey();
1586                         }
1587                     }
1588                 }
1589             } catch (final NTLMEngineException e) {
1590                 // This likely means we couldn't find the MD4 hash algorithm -
1591                 // fail back to just using LM
1592                 ntResp = new byte[0];
1593                 lmResp = gen.getLMResponse();
1594                 if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1595                     userSessionKey = gen.getLanManagerSessionKey();
1596                 } else {
1597                     userSessionKey = gen.getLMUserSessionKey();
1598                 }
1599             }
1600 
1601             if ((type2Flags & FLAG_REQUEST_SIGN) != 0) {
1602                 if ((type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) != 0) {
1603                     exportedSessionKey = gen.getSecondaryKey();
1604                     sessionKey = RC4(exportedSessionKey, userSessionKey);
1605                 } else {
1606                     sessionKey = userSessionKey;
1607                     exportedSessionKey = sessionKey;
1608                 }
1609             } else {
1610                 if (computeMic) {
1611                     throw new NTLMEngineException("Cannot sign/seal: no exported session key");
1612                 }
1613                 sessionKey = null;
1614                 exportedSessionKey = null;
1615             }
1616             final Charset charset = getCharset(type2Flags);
1617             hostBytes = unqualifiedHost != null ? unqualifiedHost.getBytes(charset) : null;
1618              domainBytes = unqualifiedDomain != null ? unqualifiedDomain
1619                 .toUpperCase(Locale.ROOT).getBytes(charset) : null;
1620             userBytes = user.getBytes(charset);
1621         }
1622 
1623         public byte[] getEncryptedRandomSessionKey() {
1624             return sessionKey;
1625         }
1626 
1627         public byte[] getExportedSessionKey() {
1628             return exportedSessionKey;
1629         }
1630 
1631         /** Assemble the response */
1632         @Override
1633         protected void buildMessage() {
1634             final int ntRespLen = ntResp.length;
1635             final int lmRespLen = lmResp.length;
1636 
1637             final int domainLen = domainBytes != null ? domainBytes.length : 0;
1638             final int hostLen = hostBytes != null ? hostBytes.length: 0;
1639             final int userLen = userBytes.length;
1640             final int sessionKeyLen;
1641             if (sessionKey != null) {
1642                 sessionKeyLen = sessionKey.length;
1643             } else {
1644                 sessionKeyLen = 0;
1645             }
1646 
1647             // Calculate the layout within the packet
1648             final int lmRespOffset = 72 + // allocate space for the version
1649                 ( computeMic ? 16 : 0 ); // and MIC
1650             final int ntRespOffset = lmRespOffset + lmRespLen;
1651             final int domainOffset = ntRespOffset + ntRespLen;
1652             final int userOffset = domainOffset + domainLen;
1653             final int hostOffset = userOffset + userLen;
1654             final int sessionKeyOffset = hostOffset + hostLen;
1655             final int finalLength = sessionKeyOffset + sessionKeyLen;
1656 
1657             // Start the response. Length includes signature and type
1658             prepareResponse(finalLength, 3);
1659 
1660             // LM Resp Length (twice)
1661             addUShort(lmRespLen);
1662             addUShort(lmRespLen);
1663 
1664             // LM Resp Offset
1665             addULong(lmRespOffset);
1666 
1667             // NT Resp Length (twice)
1668             addUShort(ntRespLen);
1669             addUShort(ntRespLen);
1670 
1671             // NT Resp Offset
1672             addULong(ntRespOffset);
1673 
1674             // Domain length (twice)
1675             addUShort(domainLen);
1676             addUShort(domainLen);
1677 
1678             // Domain offset.
1679             addULong(domainOffset);
1680 
1681             // User Length (twice)
1682             addUShort(userLen);
1683             addUShort(userLen);
1684 
1685             // User offset
1686             addULong(userOffset);
1687 
1688             // Host length (twice)
1689             addUShort(hostLen);
1690             addUShort(hostLen);
1691 
1692             // Host offset
1693             addULong(hostOffset);
1694 
1695             // Session key length (twice)
1696             addUShort(sessionKeyLen);
1697             addUShort(sessionKeyLen);
1698 
1699             // Session key offset
1700             addULong(sessionKeyOffset);
1701 
1702             // Flags.
1703             addULong(
1704                     /*
1705                     //FLAG_WORKSTATION_PRESENT |
1706                     //FLAG_DOMAIN_PRESENT |
1707 
1708                     // Required flags
1709                     (type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) |
1710                     (type2Flags & FLAG_REQUEST_NTLMv1) |
1711                     (type2Flags & FLAG_REQUEST_NTLM2_SESSION) |
1712 
1713                     // Protocol version request
1714                     FLAG_REQUEST_VERSION |
1715 
1716                     // Recommended privacy settings
1717                     (type2Flags & FLAG_REQUEST_ALWAYS_SIGN) |
1718                     (type2Flags & FLAG_REQUEST_SEAL) |
1719                     (type2Flags & FLAG_REQUEST_SIGN) |
1720 
1721                     // These must be set according to documentation, based on use of SEAL above
1722                     (type2Flags & FLAG_REQUEST_128BIT_KEY_EXCH) |
1723                     (type2Flags & FLAG_REQUEST_56BIT_ENCRYPTION) |
1724                     (type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) |
1725 
1726                     (type2Flags & FLAG_TARGETINFO_PRESENT) |
1727                     (type2Flags & FLAG_REQUEST_UNICODE_ENCODING) |
1728                     (type2Flags & FLAG_REQUEST_TARGET)
1729                         */
1730                 type2Flags
1731             );
1732 
1733             // Version
1734             addUShort(0x0105);
1735             // Build
1736             addULong(2600);
1737             // NTLM revision
1738             addUShort(0x0f00);
1739 
1740             int micPosition = -1;
1741             if ( computeMic ) {
1742                 micPosition = currentOutputPosition;
1743                 currentOutputPosition += 16;
1744             }
1745 
1746             // Add the actual data
1747             addBytes(lmResp);
1748             addBytes(ntResp);
1749             addBytes(domainBytes);
1750             addBytes(userBytes);
1751             addBytes(hostBytes);
1752             if (sessionKey != null) {
1753                 addBytes(sessionKey);
1754             }
1755 
1756             // Write the mic back into its slot in the message
1757 
1758             if (computeMic) {
1759                 // Computation of message integrity code (MIC) as specified in [MS-NLMP] section 3.2.5.1.2.
1760                 final HMACMD5 hmacMD5 = new HMACMD5( exportedSessionKey );
1761                 hmacMD5.update( type1Message );
1762                 hmacMD5.update( type2Message );
1763                 hmacMD5.update( messageContents );
1764                 final byte[] mic = hmacMD5.getOutput();
1765                 System.arraycopy( mic, 0, messageContents, micPosition, mic.length );
1766             }
1767         }
1768 
1769         /**
1770          * Add GSS channel binding hash and MIC flag to the targetInfo.
1771          * Looks like this is needed if we want to use exported session key for GSS wrapping.
1772          */
1773         private byte[] addGssMicAvsToTargetInfo( final byte[] originalTargetInfo,
1774             final Certificate peerServerCertificate ) throws NTLMEngineException
1775         {
1776             final byte[] newTargetInfo = new byte[originalTargetInfo.length + 8 + 20];
1777             final int appendLength = originalTargetInfo.length - 4; // last tag is MSV_AV_EOL, do not copy that
1778             System.arraycopy( originalTargetInfo, 0, newTargetInfo, 0, appendLength );
1779             writeUShort( newTargetInfo, MSV_AV_FLAGS, appendLength );
1780             writeUShort( newTargetInfo, 4, appendLength + 2 );
1781             writeULong( newTargetInfo, MSV_AV_FLAGS_MIC, appendLength + 4 );
1782             writeUShort( newTargetInfo, MSV_AV_CHANNEL_BINDINGS, appendLength + 8 );
1783             writeUShort( newTargetInfo, 16, appendLength + 10 );
1784 
1785             final byte[] channelBindingsHash;
1786             try
1787             {
1788                 final byte[] certBytes = peerServerCertificate.getEncoded();
1789                 final MessageDigest sha256 = MessageDigest.getInstance( "SHA-256" );
1790                 final byte[] certHashBytes = sha256.digest( certBytes );
1791                 final byte[] channelBindingStruct = new byte[16 + 4 + MAGIC_TLS_SERVER_ENDPOINT.length
1792                     + certHashBytes.length];
1793                 writeULong( channelBindingStruct, 0x00000035, 16 );
1794                 System.arraycopy( MAGIC_TLS_SERVER_ENDPOINT, 0, channelBindingStruct, 20,
1795                     MAGIC_TLS_SERVER_ENDPOINT.length );
1796                 System.arraycopy( certHashBytes, 0, channelBindingStruct, 20 + MAGIC_TLS_SERVER_ENDPOINT.length,
1797                     certHashBytes.length );
1798                 final MessageDigest md5 = getMD5();
1799                 channelBindingsHash = md5.digest( channelBindingStruct );
1800             }
1801             catch ( final CertificateEncodingException e )
1802             {
1803                 throw new NTLMEngineException( e.getMessage(), e );
1804             }
1805             catch ( final NoSuchAlgorithmException e )
1806             {
1807                 throw new NTLMEngineException( e.getMessage(), e );
1808             }
1809 
1810             System.arraycopy( channelBindingsHash, 0, newTargetInfo, appendLength + 12, 16 );
1811             return newTargetInfo;
1812          }
1813 
1814     }
1815 
1816     static void writeUShort(final byte[] buffer, final int value, final int offset) {
1817         buffer[offset] = ( byte ) ( value & 0xff );
1818         buffer[offset + 1] = ( byte ) ( value >> 8 & 0xff );
1819     }
1820 
1821     static void writeULong(final byte[] buffer, final int value, final int offset) {
1822         buffer[offset] = (byte) (value & 0xff);
1823         buffer[offset + 1] = (byte) (value >> 8 & 0xff);
1824         buffer[offset + 2] = (byte) (value >> 16 & 0xff);
1825         buffer[offset + 3] = (byte) (value >> 24 & 0xff);
1826     }
1827 
1828     static int F(final int x, final int y, final int z) {
1829         return ((x & y) | (~x & z));
1830     }
1831 
1832     static int G(final int x, final int y, final int z) {
1833         return ((x & y) | (x & z) | (y & z));
1834     }
1835 
1836     static int H(final int x, final int y, final int z) {
1837         return (x ^ y ^ z);
1838     }
1839 
1840     static int rotintlft(final int val, final int numbits) {
1841         return ((val << numbits) | (val >>> (32 - numbits)));
1842     }
1843 
1844     static MessageDigest getMD5() {
1845         try {
1846             return MessageDigest.getInstance("MD5");
1847         } catch (final NoSuchAlgorithmException ex) {
1848             throw new RuntimeException("MD5 message digest doesn't seem to exist - fatal error: "+ex.getMessage(), ex);
1849         }
1850     }
1851 
1852     /**
1853      * Cryptography support - MD4. The following class was based loosely on the
1854      * RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java.
1855      * Code correctness was verified by looking at MD4.java from the jcifs
1856      * library (http://jcifs.samba.org). It was massaged extensively to the
1857      * final form found here by Karl Wright (kwright@metacarta.com).
1858      */
1859     static class MD4 {
1860         protected int A = 0x67452301;
1861         protected int B = 0xefcdab89;
1862         protected int C = 0x98badcfe;
1863         protected int D = 0x10325476;
1864         protected long count = 0L;
1865         protected final byte[] dataBuffer = new byte[64];
1866 
1867         MD4() {
1868         }
1869 
1870         void update(final byte[] input) {
1871             // We always deal with 512 bits at a time. Correspondingly, there is
1872             // a buffer 64 bytes long that we write data into until it gets
1873             // full.
1874             int curBufferPos = (int) (count & 63L);
1875             int inputIndex = 0;
1876             while (input.length - inputIndex + curBufferPos >= dataBuffer.length) {
1877                 // We have enough data to do the next step. Do a partial copy
1878                 // and a transform, updating inputIndex and curBufferPos
1879                 // accordingly
1880                 final int transferAmt = dataBuffer.length - curBufferPos;
1881                 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1882                 count += transferAmt;
1883                 curBufferPos = 0;
1884                 inputIndex += transferAmt;
1885                 processBuffer();
1886             }
1887 
1888             // If there's anything left, copy it into the buffer and leave it.
1889             // We know there's not enough left to process.
1890             if (inputIndex < input.length) {
1891                 final int transferAmt = input.length - inputIndex;
1892                 System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1893                 count += transferAmt;
1894                 curBufferPos += transferAmt;
1895             }
1896         }
1897 
1898         byte[] getOutput() {
1899             // Feed pad/length data into engine. This must round out the input
1900             // to a multiple of 512 bits.
1901             final int bufferIndex = (int) (count & 63L);
1902             final int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
1903             final byte[] postBytes = new byte[padLen + 8];
1904             // Leading 0x80, specified amount of zero padding, then length in
1905             // bits.
1906             postBytes[0] = (byte) 0x80;
1907             // Fill out the last 8 bytes with the length
1908             for (int i = 0; i < 8; i++) {
1909                 postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i));
1910             }
1911 
1912             // Update the engine
1913             update(postBytes);
1914 
1915             // Calculate final result
1916             final byte[] result = new byte[16];
1917             writeULong(result, A, 0);
1918             writeULong(result, B, 4);
1919             writeULong(result, C, 8);
1920             writeULong(result, D, 12);
1921             return result;
1922         }
1923 
1924         protected void processBuffer() {
1925             // Convert current buffer to 16 ulongs
1926             final int[] d = new int[16];
1927 
1928             for (int i = 0; i < 16; i++) {
1929                 d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8)
1930                         + ((dataBuffer[i * 4 + 2] & 0xff) << 16)
1931                         + ((dataBuffer[i * 4 + 3] & 0xff) << 24);
1932             }
1933 
1934             // Do a round of processing
1935             final int AA = A;
1936             final int BB = B;
1937             final int CC = C;
1938             final int DD = D;
1939             round1(d);
1940             round2(d);
1941             round3(d);
1942             A += AA;
1943             B += BB;
1944             C += CC;
1945             D += DD;
1946 
1947         }
1948 
1949         protected void round1(final int[] d) {
1950             A = rotintlft((A + F(B, C, D) + d[0]), 3);
1951             D = rotintlft((D + F(A, B, C) + d[1]), 7);
1952             C = rotintlft((C + F(D, A, B) + d[2]), 11);
1953             B = rotintlft((B + F(C, D, A) + d[3]), 19);
1954 
1955             A = rotintlft((A + F(B, C, D) + d[4]), 3);
1956             D = rotintlft((D + F(A, B, C) + d[5]), 7);
1957             C = rotintlft((C + F(D, A, B) + d[6]), 11);
1958             B = rotintlft((B + F(C, D, A) + d[7]), 19);
1959 
1960             A = rotintlft((A + F(B, C, D) + d[8]), 3);
1961             D = rotintlft((D + F(A, B, C) + d[9]), 7);
1962             C = rotintlft((C + F(D, A, B) + d[10]), 11);
1963             B = rotintlft((B + F(C, D, A) + d[11]), 19);
1964 
1965             A = rotintlft((A + F(B, C, D) + d[12]), 3);
1966             D = rotintlft((D + F(A, B, C) + d[13]), 7);
1967             C = rotintlft((C + F(D, A, B) + d[14]), 11);
1968             B = rotintlft((B + F(C, D, A) + d[15]), 19);
1969         }
1970 
1971         protected void round2(final int[] d) {
1972             A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3);
1973             D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5);
1974             C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9);
1975             B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13);
1976 
1977             A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3);
1978             D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5);
1979             C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9);
1980             B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13);
1981 
1982             A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3);
1983             D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5);
1984             C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9);
1985             B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13);
1986 
1987             A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3);
1988             D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5);
1989             C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9);
1990             B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13);
1991 
1992         }
1993 
1994         protected void round3(final int[] d) {
1995             A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3);
1996             D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9);
1997             C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11);
1998             B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15);
1999 
2000             A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3);
2001             D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9);
2002             C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11);
2003             B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15);
2004 
2005             A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3);
2006             D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9);
2007             C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11);
2008             B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15);
2009 
2010             A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3);
2011             D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9);
2012             C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11);
2013             B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15);
2014 
2015         }
2016 
2017     }
2018 
2019     /**
2020      * Cryptography support - HMACMD5 - algorithmically based on various web
2021      * resources by Karl Wright
2022      */
2023     static class HMACMD5 {
2024         protected final byte[] ipad;
2025         protected final byte[] opad;
2026         protected final MessageDigest md5;
2027 
2028         HMACMD5(final byte[] input) {
2029             byte[] key = input;
2030             md5 = getMD5();
2031 
2032             // Initialize the pad buffers with the key
2033             ipad = new byte[64];
2034             opad = new byte[64];
2035 
2036             int keyLength = key.length;
2037             if (keyLength > 64) {
2038                 // Use MD5 of the key instead, as described in RFC 2104
2039                 md5.update(key);
2040                 key = md5.digest();
2041                 keyLength = key.length;
2042             }
2043             int i = 0;
2044             while (i < keyLength) {
2045                 ipad[i] = (byte) (key[i] ^ (byte) 0x36);
2046                 opad[i] = (byte) (key[i] ^ (byte) 0x5c);
2047                 i++;
2048             }
2049             while (i < 64) {
2050                 ipad[i] = (byte) 0x36;
2051                 opad[i] = (byte) 0x5c;
2052                 i++;
2053             }
2054 
2055             // Very important: processChallenge the digest with the ipad buffer
2056             md5.reset();
2057             md5.update(ipad);
2058 
2059         }
2060 
2061         /** Grab the current digest. This is the "answer". */
2062         byte[] getOutput() {
2063             final byte[] digest = md5.digest();
2064             md5.update(opad);
2065             return md5.digest(digest);
2066         }
2067 
2068         /** Update by adding a complete array */
2069         void update(final byte[] input) {
2070             md5.update(input);
2071         }
2072 
2073         /** Update the algorithm */
2074         void update(final byte[] input, final int offset, final int length) {
2075             md5.update(input, offset, length);
2076         }
2077 
2078     }
2079 
2080     @Override
2081     public String generateType1Msg(
2082             final String domain,
2083             final String workstation) throws NTLMEngineException {
2084         return getType1Message(workstation, domain);
2085     }
2086 
2087     @Override
2088     public String generateType3Msg(
2089             final String username,
2090             final String password,
2091             final String domain,
2092             final String workstation,
2093             final String challenge) throws NTLMEngineException {
2094         final Type2Message t2m = new Type2Message(challenge);
2095         return getType3Message(
2096                 username,
2097                 password,
2098                 workstation,
2099                 domain,
2100                 t2m.getChallenge(),
2101                 t2m.getFlags(),
2102                 t2m.getTarget(),
2103                 t2m.getTargetInfo());
2104     }
2105 
2106 }