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