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