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