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