001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027package org.apache.http.impl.auth;
028
029import java.io.UnsupportedEncodingException;
030import java.nio.charset.Charset;
031import java.security.Key;
032import java.security.MessageDigest;
033import java.util.Arrays;
034import java.util.Locale;
035
036import javax.crypto.Cipher;
037import javax.crypto.spec.SecretKeySpec;
038
039import org.apache.commons.codec.binary.Base64;
040import org.apache.http.Consts;
041import org.apache.http.util.CharsetUtils;
042import org.apache.http.util.EncodingUtils;
043
044/**
045 * Provides an implementation for NTLMv1, NTLMv2, and NTLM2 Session forms of the NTLM
046 * authentication protocol.
047 *
048 * @since 4.1
049 */
050final class NTLMEngineImpl implements NTLMEngine {
051
052    /** Unicode encoding */
053    private static final Charset UNICODE_LITTLE_UNMARKED = CharsetUtils.lookup("UnicodeLittleUnmarked");
054    /** Character encoding */
055    private static final Charset DEFAULT_CHARSET = Consts.ASCII;
056
057    // Flags we use; descriptions according to:
058    // http://davenport.sourceforge.net/ntlm.html
059    // and
060    // http://msdn.microsoft.com/en-us/library/cc236650%28v=prot.20%29.aspx
061    protected static final int FLAG_REQUEST_UNICODE_ENCODING = 0x00000001;      // Unicode string encoding requested
062    protected static final int FLAG_REQUEST_TARGET = 0x00000004;                      // Requests target field
063    protected static final int FLAG_REQUEST_SIGN = 0x00000010;  // Requests all messages have a signature attached, in NEGOTIATE message.
064    protected static final int FLAG_REQUEST_SEAL = 0x00000020;  // Request key exchange for message confidentiality in NEGOTIATE message.  MUST be used in conjunction with 56BIT.
065    protected static final int FLAG_REQUEST_LAN_MANAGER_KEY = 0x00000080;    // Request Lan Manager key instead of user session key
066    protected static final int FLAG_REQUEST_NTLMv1 = 0x00000200; // Request NTLMv1 security.  MUST be set in NEGOTIATE and CHALLENGE both
067    protected static final int FLAG_DOMAIN_PRESENT = 0x00001000;        // Domain is present in message
068    protected static final int FLAG_WORKSTATION_PRESENT = 0x00002000;   // Workstation is present in message
069    protected static final int FLAG_REQUEST_ALWAYS_SIGN = 0x00008000;   // Requests a signature block on all messages.  Overridden by REQUEST_SIGN and REQUEST_SEAL.
070    protected static final int FLAG_REQUEST_NTLM2_SESSION = 0x00080000; // From server in challenge, requesting NTLM2 session security
071    protected static final int FLAG_REQUEST_VERSION = 0x02000000;       // Request protocol version
072    protected static final int FLAG_TARGETINFO_PRESENT = 0x00800000;    // From server in challenge message, indicating targetinfo is present
073    protected static final int FLAG_REQUEST_128BIT_KEY_EXCH = 0x20000000; // Request explicit 128-bit key exchange
074    protected static final int FLAG_REQUEST_EXPLICIT_KEY_EXCH = 0x40000000;     // Request explicit key exchange
075    protected static final int FLAG_REQUEST_56BIT_ENCRYPTION = 0x80000000;      // Must be used in conjunction with SEAL
076
077
078    /** Secure random generator */
079    private static final java.security.SecureRandom RND_GEN;
080    static {
081        java.security.SecureRandom rnd = null;
082        try {
083            rnd = java.security.SecureRandom.getInstance("SHA1PRNG");
084        } catch (final Exception ignore) {
085        }
086        RND_GEN = rnd;
087    }
088
089    /** The signature string as bytes in the default encoding */
090    private static final byte[] SIGNATURE;
091
092    static {
093        final byte[] bytesWithoutNull = "NTLMSSP".getBytes(Consts.ASCII);
094        SIGNATURE = new byte[bytesWithoutNull.length + 1];
095        System.arraycopy(bytesWithoutNull, 0, SIGNATURE, 0, bytesWithoutNull.length);
096        SIGNATURE[bytesWithoutNull.length] = (byte) 0x00;
097    }
098
099    private static final String TYPE_1_MESSAGE = new Type1Message().getResponse();
100
101    /**
102     * Returns the response for the given message.
103     *
104     * @param message
105     *            the message that was received from the server.
106     * @param username
107     *            the username to authenticate with.
108     * @param password
109     *            the password to authenticate with.
110     * @param host
111     *            The host.
112     * @param domain
113     *            the NT domain to authenticate in.
114     * @return The response.
115     * @throws org.apache.http.HttpException
116     *             If the messages cannot be retrieved.
117     */
118    static String getResponseFor(final String message, final String username, final String password,
119            final String host, final String domain) throws NTLMEngineException {
120
121        final String response;
122        if (message == null || message.trim().equals("")) {
123            response = getType1Message(host, domain);
124        } else {
125            final Type2Message t2m = new Type2Message(message);
126            response = getType3Message(username, password, host, domain, t2m.getChallenge(), t2m
127                    .getFlags(), t2m.getTarget(), t2m.getTargetInfo());
128        }
129        return response;
130    }
131
132    /**
133     * Creates the first message (type 1 message) in the NTLM authentication
134     * sequence. This message includes the user name, domain and host for the
135     * authentication session.
136     *
137     * @param host
138     *            the computer name of the host requesting authentication.
139     * @param domain
140     *            The domain to authenticate with.
141     * @return String the message to add to the HTTP request header.
142     */
143    static String getType1Message(final String host, final String domain) throws NTLMEngineException {
144        // For compatibility reason do not include domain and host in type 1 message
145        //return new Type1Message(domain, host).getResponse();
146        return TYPE_1_MESSAGE;
147    }
148
149    /**
150     * Creates the type 3 message using the given server nonce. The type 3
151     * message includes all the information for authentication, host, domain,
152     * username and the result of encrypting the nonce sent by the server using
153     * the user's password as the key.
154     *
155     * @param user
156     *            The user name. This should not include the domain name.
157     * @param password
158     *            The password.
159     * @param host
160     *            The host that is originating the authentication request.
161     * @param domain
162     *            The domain to authenticate within.
163     * @param nonce
164     *            the 8 byte array the server sent.
165     * @return The type 3 message.
166     * @throws NTLMEngineException
167     *             If {@encrypt(byte[],byte[])} fails.
168     */
169    static String getType3Message(final String user, final String password, final String host, final String domain,
170            final byte[] nonce, final int type2Flags, final String target, final byte[] targetInformation)
171            throws NTLMEngineException {
172        return new Type3Message(domain, host, user, password, nonce, type2Flags, target,
173                targetInformation).getResponse();
174    }
175
176    /** Strip dot suffix from a name */
177    private static String stripDotSuffix(final String value) {
178        if (value == null) {
179            return null;
180        }
181        final int index = value.indexOf(".");
182        if (index != -1) {
183            return value.substring(0, index);
184        }
185        return value;
186    }
187
188    /** Convert host to standard form */
189    private static String convertHost(final String host) {
190        return stripDotSuffix(host);
191    }
192
193    /** Convert domain to standard form */
194    private static String convertDomain(final String domain) {
195        return stripDotSuffix(domain);
196    }
197
198    private static int readULong(final byte[] src, final int index) throws NTLMEngineException {
199        if (src.length < index + 4) {
200            throw new NTLMEngineException("NTLM authentication - buffer too small for DWORD");
201        }
202        return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8)
203                | ((src[index + 2] & 0xff) << 16) | ((src[index + 3] & 0xff) << 24);
204    }
205
206    private static int readUShort(final byte[] src, final int index) throws NTLMEngineException {
207        if (src.length < index + 2) {
208            throw new NTLMEngineException("NTLM authentication - buffer too small for WORD");
209        }
210        return (src[index] & 0xff) | ((src[index + 1] & 0xff) << 8);
211    }
212
213    private static byte[] readSecurityBuffer(final byte[] src, final int index) throws NTLMEngineException {
214        final int length = readUShort(src, index);
215        final int offset = readULong(src, index + 4);
216        if (src.length < offset + length) {
217            throw new NTLMEngineException(
218                    "NTLM authentication - buffer too small for data item");
219        }
220        final byte[] buffer = new byte[length];
221        System.arraycopy(src, offset, buffer, 0, length);
222        return buffer;
223    }
224
225    /** Calculate a challenge block */
226    private static byte[] makeRandomChallenge() throws NTLMEngineException {
227        if (RND_GEN == null) {
228            throw new NTLMEngineException("Random generator not available");
229        }
230        final byte[] rval = new byte[8];
231        synchronized (RND_GEN) {
232            RND_GEN.nextBytes(rval);
233        }
234        return rval;
235    }
236
237    /** Calculate a 16-byte secondary key */
238    private static byte[] makeSecondaryKey() throws NTLMEngineException {
239        if (RND_GEN == null) {
240            throw new NTLMEngineException("Random generator not available");
241        }
242        final byte[] rval = new byte[16];
243        synchronized (RND_GEN) {
244            RND_GEN.nextBytes(rval);
245        }
246        return rval;
247    }
248
249    protected static class CipherGen {
250
251        protected final String domain;
252        protected final String user;
253        protected final String password;
254        protected final byte[] challenge;
255        protected final String target;
256        protected final byte[] targetInformation;
257
258        // Information we can generate but may be passed in (for testing)
259        protected byte[] clientChallenge;
260        protected byte[] clientChallenge2;
261        protected byte[] secondaryKey;
262        protected byte[] timestamp;
263
264        // Stuff we always generate
265        protected byte[] lmHash = null;
266        protected byte[] lmResponse = null;
267        protected byte[] ntlmHash = null;
268        protected byte[] ntlmResponse = null;
269        protected byte[] ntlmv2Hash = null;
270        protected byte[] lmv2Hash = null;
271        protected byte[] lmv2Response = null;
272        protected byte[] ntlmv2Blob = null;
273        protected byte[] ntlmv2Response = null;
274        protected byte[] ntlm2SessionResponse = null;
275        protected byte[] lm2SessionResponse = null;
276        protected byte[] lmUserSessionKey = null;
277        protected byte[] ntlmUserSessionKey = null;
278        protected byte[] ntlmv2UserSessionKey = null;
279        protected byte[] ntlm2SessionResponseUserSessionKey = null;
280        protected byte[] lanManagerSessionKey = null;
281
282        public CipherGen(final String domain, final String user, final String password,
283            final byte[] challenge, final String target, final byte[] targetInformation,
284            final byte[] clientChallenge, final byte[] clientChallenge2,
285            final byte[] secondaryKey, final byte[] timestamp) {
286            this.domain = domain;
287            this.target = target;
288            this.user = user;
289            this.password = password;
290            this.challenge = challenge;
291            this.targetInformation = targetInformation;
292            this.clientChallenge = clientChallenge;
293            this.clientChallenge2 = clientChallenge2;
294            this.secondaryKey = secondaryKey;
295            this.timestamp = timestamp;
296        }
297
298        public CipherGen(final String domain, final String user, final String password,
299            final byte[] challenge, final String target, final byte[] targetInformation) {
300            this(domain, user, password, challenge, target, targetInformation, null, null, null, null);
301        }
302
303        /** Calculate and return client challenge */
304        public byte[] getClientChallenge()
305            throws NTLMEngineException {
306            if (clientChallenge == null) {
307                clientChallenge = makeRandomChallenge();
308            }
309            return clientChallenge;
310        }
311
312        /** Calculate and return second client challenge */
313        public byte[] getClientChallenge2()
314            throws NTLMEngineException {
315            if (clientChallenge2 == null) {
316                clientChallenge2 = makeRandomChallenge();
317            }
318            return clientChallenge2;
319        }
320
321        /** Calculate and return random secondary key */
322        public byte[] getSecondaryKey()
323            throws NTLMEngineException {
324            if (secondaryKey == null) {
325                secondaryKey = makeSecondaryKey();
326            }
327            return secondaryKey;
328        }
329
330        /** Calculate and return the LMHash */
331        public byte[] getLMHash()
332            throws NTLMEngineException {
333            if (lmHash == null) {
334                lmHash = lmHash(password);
335            }
336            return lmHash;
337        }
338
339        /** Calculate and return the LMResponse */
340        public byte[] getLMResponse()
341            throws NTLMEngineException {
342            if (lmResponse == null) {
343                lmResponse = lmResponse(getLMHash(),challenge);
344            }
345            return lmResponse;
346        }
347
348        /** Calculate and return the NTLMHash */
349        public byte[] getNTLMHash()
350            throws NTLMEngineException {
351            if (ntlmHash == null) {
352                ntlmHash = ntlmHash(password);
353            }
354            return ntlmHash;
355        }
356
357        /** Calculate and return the NTLMResponse */
358        public byte[] getNTLMResponse()
359            throws NTLMEngineException {
360            if (ntlmResponse == null) {
361                ntlmResponse = lmResponse(getNTLMHash(),challenge);
362            }
363            return ntlmResponse;
364        }
365
366        /** Calculate the LMv2 hash */
367        public byte[] getLMv2Hash()
368            throws NTLMEngineException {
369            if (lmv2Hash == null) {
370                lmv2Hash = lmv2Hash(domain, user, getNTLMHash());
371            }
372            return lmv2Hash;
373        }
374
375        /** Calculate the NTLMv2 hash */
376        public byte[] getNTLMv2Hash()
377            throws NTLMEngineException {
378            if (ntlmv2Hash == null) {
379                ntlmv2Hash = ntlmv2Hash(domain, user, getNTLMHash());
380            }
381            return ntlmv2Hash;
382        }
383
384        /** Calculate a timestamp */
385        public byte[] getTimestamp() {
386            if (timestamp == null) {
387                long time = System.currentTimeMillis();
388                time += 11644473600000l; // milliseconds from January 1, 1601 -> epoch.
389                time *= 10000; // tenths of a microsecond.
390                // convert to little-endian byte array.
391                timestamp = new byte[8];
392                for (int i = 0; i < 8; i++) {
393                    timestamp[i] = (byte) time;
394                    time >>>= 8;
395                }
396            }
397            return timestamp;
398        }
399
400        /** Calculate the NTLMv2Blob */
401        public byte[] getNTLMv2Blob()
402            throws NTLMEngineException {
403            if (ntlmv2Blob == null) {
404                ntlmv2Blob = createBlob(getClientChallenge2(), targetInformation, getTimestamp());
405            }
406            return ntlmv2Blob;
407        }
408
409        /** Calculate the NTLMv2Response */
410        public byte[] getNTLMv2Response()
411            throws NTLMEngineException {
412            if (ntlmv2Response == null) {
413                ntlmv2Response = lmv2Response(getNTLMv2Hash(),challenge,getNTLMv2Blob());
414            }
415            return ntlmv2Response;
416        }
417
418        /** Calculate the LMv2Response */
419        public byte[] getLMv2Response()
420            throws NTLMEngineException {
421            if (lmv2Response == null) {
422                lmv2Response = lmv2Response(getLMv2Hash(),challenge,getClientChallenge());
423            }
424            return lmv2Response;
425        }
426
427        /** Get NTLM2SessionResponse */
428        public byte[] getNTLM2SessionResponse()
429            throws NTLMEngineException {
430            if (ntlm2SessionResponse == null) {
431                ntlm2SessionResponse = ntlm2SessionResponse(getNTLMHash(),challenge,getClientChallenge());
432            }
433            return ntlm2SessionResponse;
434        }
435
436        /** Calculate and return LM2 session response */
437        public byte[] getLM2SessionResponse()
438            throws NTLMEngineException {
439            if (lm2SessionResponse == null) {
440                final byte[] clntChallenge = getClientChallenge();
441                lm2SessionResponse = new byte[24];
442                System.arraycopy(clntChallenge, 0, lm2SessionResponse, 0, clntChallenge.length);
443                Arrays.fill(lm2SessionResponse, clntChallenge.length, lm2SessionResponse.length, (byte) 0x00);
444            }
445            return lm2SessionResponse;
446        }
447
448        /** Get LMUserSessionKey */
449        public byte[] getLMUserSessionKey()
450            throws NTLMEngineException {
451            if (lmUserSessionKey == null) {
452                lmUserSessionKey = new byte[16];
453                System.arraycopy(getLMHash(), 0, lmUserSessionKey, 0, 8);
454                Arrays.fill(lmUserSessionKey, 8, 16, (byte) 0x00);
455            }
456            return lmUserSessionKey;
457        }
458
459        /** Get NTLMUserSessionKey */
460        public byte[] getNTLMUserSessionKey()
461            throws NTLMEngineException {
462            if (ntlmUserSessionKey == null) {
463                final MD4 md4 = new MD4();
464                md4.update(getNTLMHash());
465                ntlmUserSessionKey = md4.getOutput();
466            }
467            return ntlmUserSessionKey;
468        }
469
470        /** GetNTLMv2UserSessionKey */
471        public byte[] getNTLMv2UserSessionKey()
472            throws NTLMEngineException {
473            if (ntlmv2UserSessionKey == null) {
474                final byte[] ntlmv2hash = getNTLMv2Hash();
475                final byte[] truncatedResponse = new byte[16];
476                System.arraycopy(getNTLMv2Response(), 0, truncatedResponse, 0, 16);
477                ntlmv2UserSessionKey = hmacMD5(truncatedResponse, ntlmv2hash);
478            }
479            return ntlmv2UserSessionKey;
480        }
481
482        /** Get NTLM2SessionResponseUserSessionKey */
483        public byte[] getNTLM2SessionResponseUserSessionKey()
484            throws NTLMEngineException {
485            if (ntlm2SessionResponseUserSessionKey == null) {
486                final byte[] ntlm2SessionResponseNonce = getLM2SessionResponse();
487                final byte[] sessionNonce = new byte[challenge.length + ntlm2SessionResponseNonce.length];
488                System.arraycopy(challenge, 0, sessionNonce, 0, challenge.length);
489                System.arraycopy(ntlm2SessionResponseNonce, 0, sessionNonce, challenge.length, ntlm2SessionResponseNonce.length);
490                ntlm2SessionResponseUserSessionKey = hmacMD5(sessionNonce,getNTLMUserSessionKey());
491            }
492            return ntlm2SessionResponseUserSessionKey;
493        }
494
495        /** Get LAN Manager session key */
496        public byte[] getLanManagerSessionKey()
497            throws NTLMEngineException {
498            if (lanManagerSessionKey == null) {
499                try {
500                    final byte[] keyBytes = new byte[14];
501                    System.arraycopy(getLMHash(), 0, keyBytes, 0, 8);
502                    Arrays.fill(keyBytes, 8, keyBytes.length, (byte)0xbd);
503                    final Key lowKey = createDESKey(keyBytes, 0);
504                    final Key highKey = createDESKey(keyBytes, 7);
505                    final byte[] truncatedResponse = new byte[8];
506                    System.arraycopy(getLMResponse(), 0, truncatedResponse, 0, truncatedResponse.length);
507                    Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
508                    des.init(Cipher.ENCRYPT_MODE, lowKey);
509                    final byte[] lowPart = des.doFinal(truncatedResponse);
510                    des = Cipher.getInstance("DES/ECB/NoPadding");
511                    des.init(Cipher.ENCRYPT_MODE, highKey);
512                    final byte[] highPart = des.doFinal(truncatedResponse);
513                    lanManagerSessionKey = new byte[16];
514                    System.arraycopy(lowPart, 0, lanManagerSessionKey, 0, lowPart.length);
515                    System.arraycopy(highPart, 0, lanManagerSessionKey, lowPart.length, highPart.length);
516                } catch (final Exception e) {
517                    throw new NTLMEngineException(e.getMessage(), e);
518                }
519            }
520            return lanManagerSessionKey;
521        }
522    }
523
524    /** Calculates HMAC-MD5 */
525    static byte[] hmacMD5(final byte[] value, final byte[] key)
526        throws NTLMEngineException {
527        final HMACMD5 hmacMD5 = new HMACMD5(key);
528        hmacMD5.update(value);
529        return hmacMD5.getOutput();
530    }
531
532    /** Calculates RC4 */
533    static byte[] RC4(final byte[] value, final byte[] key)
534        throws NTLMEngineException {
535        try {
536            final Cipher rc4 = Cipher.getInstance("RC4");
537            rc4.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "RC4"));
538            return rc4.doFinal(value);
539        } catch (final Exception e) {
540            throw new NTLMEngineException(e.getMessage(), e);
541        }
542    }
543
544    /**
545     * Calculates the NTLM2 Session Response for the given challenge, using the
546     * specified password and client challenge.
547     *
548     * @return The NTLM2 Session Response. This is placed in the NTLM response
549     *         field of the Type 3 message; the LM response field contains the
550     *         client challenge, null-padded to 24 bytes.
551     */
552    static byte[] ntlm2SessionResponse(final byte[] ntlmHash, final byte[] challenge,
553            final byte[] clientChallenge) throws NTLMEngineException {
554        try {
555            final MessageDigest md5 = MessageDigest.getInstance("MD5");
556            md5.update(challenge);
557            md5.update(clientChallenge);
558            final byte[] digest = md5.digest();
559
560            final byte[] sessionHash = new byte[8];
561            System.arraycopy(digest, 0, sessionHash, 0, 8);
562            return lmResponse(ntlmHash, sessionHash);
563        } catch (final Exception e) {
564            if (e instanceof NTLMEngineException) {
565                throw (NTLMEngineException) e;
566            }
567            throw new NTLMEngineException(e.getMessage(), e);
568        }
569    }
570
571    /**
572     * Creates the LM Hash of the user's password.
573     *
574     * @param password
575     *            The password.
576     *
577     * @return The LM Hash of the given password, used in the calculation of the
578     *         LM Response.
579     */
580    private static byte[] lmHash(final String password) throws NTLMEngineException {
581        try {
582            final byte[] oemPassword = password.toUpperCase(Locale.ROOT).getBytes(Consts.ASCII);
583            final int length = Math.min(oemPassword.length, 14);
584            final byte[] keyBytes = new byte[14];
585            System.arraycopy(oemPassword, 0, keyBytes, 0, length);
586            final Key lowKey = createDESKey(keyBytes, 0);
587            final Key highKey = createDESKey(keyBytes, 7);
588            final byte[] magicConstant = "KGS!@#$%".getBytes(Consts.ASCII);
589            final Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
590            des.init(Cipher.ENCRYPT_MODE, lowKey);
591            final byte[] lowHash = des.doFinal(magicConstant);
592            des.init(Cipher.ENCRYPT_MODE, highKey);
593            final byte[] highHash = des.doFinal(magicConstant);
594            final byte[] lmHash = new byte[16];
595            System.arraycopy(lowHash, 0, lmHash, 0, 8);
596            System.arraycopy(highHash, 0, lmHash, 8, 8);
597            return lmHash;
598        } catch (final Exception e) {
599            throw new NTLMEngineException(e.getMessage(), e);
600        }
601    }
602
603    /**
604     * Creates the NTLM Hash of the user's password.
605     *
606     * @param password
607     *            The password.
608     *
609     * @return The NTLM Hash of the given password, used in the calculation of
610     *         the NTLM Response and the NTLMv2 and LMv2 Hashes.
611     */
612    private static byte[] ntlmHash(final String password) throws NTLMEngineException {
613        if (UNICODE_LITTLE_UNMARKED == null) {
614            throw new NTLMEngineException("Unicode not supported");
615        }
616        final byte[] unicodePassword = password.getBytes(UNICODE_LITTLE_UNMARKED);
617        final MD4 md4 = new MD4();
618        md4.update(unicodePassword);
619        return md4.getOutput();
620    }
621
622    /**
623     * Creates the LMv2 Hash of the user's password.
624     *
625     * @return The LMv2 Hash, used in the calculation of the NTLMv2 and LMv2
626     *         Responses.
627     */
628    private static byte[] lmv2Hash(final String domain, final String user, final byte[] ntlmHash)
629            throws NTLMEngineException {
630        if (UNICODE_LITTLE_UNMARKED == null) {
631            throw new NTLMEngineException("Unicode not supported");
632        }
633        final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
634        // Upper case username, upper case domain!
635        hmacMD5.update(user.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
636        if (domain != null) {
637            hmacMD5.update(domain.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
638        }
639        return hmacMD5.getOutput();
640    }
641
642    /**
643     * Creates the NTLMv2 Hash of the user's password.
644     *
645     * @return The NTLMv2 Hash, used in the calculation of the NTLMv2 and LMv2
646     *         Responses.
647     */
648    private static byte[] ntlmv2Hash(final String domain, final String user, final byte[] ntlmHash)
649            throws NTLMEngineException {
650        if (UNICODE_LITTLE_UNMARKED == null) {
651            throw new NTLMEngineException("Unicode not supported");
652        }
653        final HMACMD5 hmacMD5 = new HMACMD5(ntlmHash);
654        // Upper case username, mixed case target!!
655        hmacMD5.update(user.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED));
656        if (domain != null) {
657            hmacMD5.update(domain.getBytes(UNICODE_LITTLE_UNMARKED));
658        }
659        return hmacMD5.getOutput();
660    }
661
662    /**
663     * Creates the LM Response from the given hash and Type 2 challenge.
664     *
665     * @param hash
666     *            The LM or NTLM Hash.
667     * @param challenge
668     *            The server challenge from the Type 2 message.
669     *
670     * @return The response (either LM or NTLM, depending on the provided hash).
671     */
672    private static byte[] lmResponse(final byte[] hash, final byte[] challenge) throws NTLMEngineException {
673        try {
674            final byte[] keyBytes = new byte[21];
675            System.arraycopy(hash, 0, keyBytes, 0, 16);
676            final Key lowKey = createDESKey(keyBytes, 0);
677            final Key middleKey = createDESKey(keyBytes, 7);
678            final Key highKey = createDESKey(keyBytes, 14);
679            final Cipher des = Cipher.getInstance("DES/ECB/NoPadding");
680            des.init(Cipher.ENCRYPT_MODE, lowKey);
681            final byte[] lowResponse = des.doFinal(challenge);
682            des.init(Cipher.ENCRYPT_MODE, middleKey);
683            final byte[] middleResponse = des.doFinal(challenge);
684            des.init(Cipher.ENCRYPT_MODE, highKey);
685            final byte[] highResponse = des.doFinal(challenge);
686            final byte[] lmResponse = new byte[24];
687            System.arraycopy(lowResponse, 0, lmResponse, 0, 8);
688            System.arraycopy(middleResponse, 0, lmResponse, 8, 8);
689            System.arraycopy(highResponse, 0, lmResponse, 16, 8);
690            return lmResponse;
691        } catch (final Exception e) {
692            throw new NTLMEngineException(e.getMessage(), e);
693        }
694    }
695
696    /**
697     * Creates the LMv2 Response from the given hash, client data, and Type 2
698     * challenge.
699     *
700     * @param hash
701     *            The NTLMv2 Hash.
702     * @param clientData
703     *            The client data (blob or client challenge).
704     * @param challenge
705     *            The server challenge from the Type 2 message.
706     *
707     * @return The response (either NTLMv2 or LMv2, depending on the client
708     *         data).
709     */
710    private static byte[] lmv2Response(final byte[] hash, final byte[] challenge, final byte[] clientData)
711            throws NTLMEngineException {
712        final HMACMD5 hmacMD5 = new HMACMD5(hash);
713        hmacMD5.update(challenge);
714        hmacMD5.update(clientData);
715        final byte[] mac = hmacMD5.getOutput();
716        final byte[] lmv2Response = new byte[mac.length + clientData.length];
717        System.arraycopy(mac, 0, lmv2Response, 0, mac.length);
718        System.arraycopy(clientData, 0, lmv2Response, mac.length, clientData.length);
719        return lmv2Response;
720    }
721
722    /**
723     * Creates the NTLMv2 blob from the given target information block and
724     * client challenge.
725     *
726     * @param targetInformation
727     *            The target information block from the Type 2 message.
728     * @param clientChallenge
729     *            The random 8-byte client challenge.
730     *
731     * @return The blob, used in the calculation of the NTLMv2 Response.
732     */
733    private static byte[] createBlob(final byte[] clientChallenge, final byte[] targetInformation, final byte[] timestamp) {
734        final byte[] blobSignature = new byte[] { (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x00 };
735        final byte[] reserved = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
736        final byte[] unknown1 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
737        final byte[] unknown2 = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
738        final byte[] blob = new byte[blobSignature.length + reserved.length + timestamp.length + 8
739                + unknown1.length + targetInformation.length + unknown2.length];
740        int offset = 0;
741        System.arraycopy(blobSignature, 0, blob, offset, blobSignature.length);
742        offset += blobSignature.length;
743        System.arraycopy(reserved, 0, blob, offset, reserved.length);
744        offset += reserved.length;
745        System.arraycopy(timestamp, 0, blob, offset, timestamp.length);
746        offset += timestamp.length;
747        System.arraycopy(clientChallenge, 0, blob, offset, 8);
748        offset += 8;
749        System.arraycopy(unknown1, 0, blob, offset, unknown1.length);
750        offset += unknown1.length;
751        System.arraycopy(targetInformation, 0, blob, offset, targetInformation.length);
752        offset += targetInformation.length;
753        System.arraycopy(unknown2, 0, blob, offset, unknown2.length);
754        offset += unknown2.length;
755        return blob;
756    }
757
758    /**
759     * Creates a DES encryption key from the given key material.
760     *
761     * @param bytes
762     *            A byte array containing the DES key material.
763     * @param offset
764     *            The offset in the given byte array at which the 7-byte key
765     *            material starts.
766     *
767     * @return A DES encryption key created from the key material starting at
768     *         the specified offset in the given byte array.
769     */
770    private static Key createDESKey(final byte[] bytes, final int offset) {
771        final byte[] keyBytes = new byte[7];
772        System.arraycopy(bytes, offset, keyBytes, 0, 7);
773        final byte[] material = new byte[8];
774        material[0] = keyBytes[0];
775        material[1] = (byte) (keyBytes[0] << 7 | (keyBytes[1] & 0xff) >>> 1);
776        material[2] = (byte) (keyBytes[1] << 6 | (keyBytes[2] & 0xff) >>> 2);
777        material[3] = (byte) (keyBytes[2] << 5 | (keyBytes[3] & 0xff) >>> 3);
778        material[4] = (byte) (keyBytes[3] << 4 | (keyBytes[4] & 0xff) >>> 4);
779        material[5] = (byte) (keyBytes[4] << 3 | (keyBytes[5] & 0xff) >>> 5);
780        material[6] = (byte) (keyBytes[5] << 2 | (keyBytes[6] & 0xff) >>> 6);
781        material[7] = (byte) (keyBytes[6] << 1);
782        oddParity(material);
783        return new SecretKeySpec(material, "DES");
784    }
785
786    /**
787     * Applies odd parity to the given byte array.
788     *
789     * @param bytes
790     *            The data whose parity bits are to be adjusted for odd parity.
791     */
792    private static void oddParity(final byte[] bytes) {
793        for (int i = 0; i < bytes.length; i++) {
794            final byte b = bytes[i];
795            final boolean needsParity = (((b >>> 7) ^ (b >>> 6) ^ (b >>> 5) ^ (b >>> 4) ^ (b >>> 3)
796                    ^ (b >>> 2) ^ (b >>> 1)) & 0x01) == 0;
797            if (needsParity) {
798                bytes[i] |= (byte) 0x01;
799            } else {
800                bytes[i] &= (byte) 0xfe;
801            }
802        }
803    }
804
805    /** NTLM message generation, base class */
806    static class NTLMMessage {
807        /** The current response */
808        private byte[] messageContents = null;
809
810        /** The current output position */
811        private int currentOutputPosition = 0;
812
813        /** Constructor to use when message contents are not yet known */
814        NTLMMessage() {
815        }
816
817        /** Constructor to use when message contents are known */
818        NTLMMessage(final String messageBody, final int expectedType) throws NTLMEngineException {
819            messageContents = Base64.decodeBase64(messageBody.getBytes(DEFAULT_CHARSET));
820            // Look for NTLM message
821            if (messageContents.length < SIGNATURE.length) {
822                throw new NTLMEngineException("NTLM message decoding error - packet too short");
823            }
824            int i = 0;
825            while (i < SIGNATURE.length) {
826                if (messageContents[i] != SIGNATURE[i]) {
827                    throw new NTLMEngineException(
828                            "NTLM message expected - instead got unrecognized bytes");
829                }
830                i++;
831            }
832
833            // Check to be sure there's a type 2 message indicator next
834            final int type = readULong(SIGNATURE.length);
835            if (type != expectedType) {
836                throw new NTLMEngineException("NTLM type " + Integer.toString(expectedType)
837                        + " message expected - instead got type " + Integer.toString(type));
838            }
839
840            currentOutputPosition = messageContents.length;
841        }
842
843        /**
844         * Get the length of the signature and flags, so calculations can adjust
845         * offsets accordingly.
846         */
847        protected int getPreambleLength() {
848            return SIGNATURE.length + 4;
849        }
850
851        /** Get the message length */
852        protected int getMessageLength() {
853            return currentOutputPosition;
854        }
855
856        /** Read a byte from a position within the message buffer */
857        protected byte readByte(final int position) throws NTLMEngineException {
858            if (messageContents.length < position + 1) {
859                throw new NTLMEngineException("NTLM: Message too short");
860            }
861            return messageContents[position];
862        }
863
864        /** Read a bunch of bytes from a position in the message buffer */
865        protected void readBytes(final byte[] buffer, final int position) throws NTLMEngineException {
866            if (messageContents.length < position + buffer.length) {
867                throw new NTLMEngineException("NTLM: Message too short");
868            }
869            System.arraycopy(messageContents, position, buffer, 0, buffer.length);
870        }
871
872        /** Read a ushort from a position within the message buffer */
873        protected int readUShort(final int position) throws NTLMEngineException {
874            return NTLMEngineImpl.readUShort(messageContents, position);
875        }
876
877        /** Read a ulong from a position within the message buffer */
878        protected int readULong(final int position) throws NTLMEngineException {
879            return NTLMEngineImpl.readULong(messageContents, position);
880        }
881
882        /** Read a security buffer from a position within the message buffer */
883        protected byte[] readSecurityBuffer(final int position) throws NTLMEngineException {
884            return NTLMEngineImpl.readSecurityBuffer(messageContents, position);
885        }
886
887        /**
888         * Prepares the object to create a response of the given length.
889         *
890         * @param maxlength
891         *            the maximum length of the response to prepare, not
892         *            including the type and the signature (which this method
893         *            adds).
894         */
895        protected void prepareResponse(final int maxlength, final int messageType) {
896            messageContents = new byte[maxlength];
897            currentOutputPosition = 0;
898            addBytes(SIGNATURE);
899            addULong(messageType);
900        }
901
902        /**
903         * Adds the given byte to the response.
904         *
905         * @param b
906         *            the byte to add.
907         */
908        protected void addByte(final byte b) {
909            messageContents[currentOutputPosition] = b;
910            currentOutputPosition++;
911        }
912
913        /**
914         * Adds the given bytes to the response.
915         *
916         * @param bytes
917         *            the bytes to add.
918         */
919        protected void addBytes(final byte[] bytes) {
920            if (bytes == null) {
921                return;
922            }
923            for (final byte b : bytes) {
924                messageContents[currentOutputPosition] = b;
925                currentOutputPosition++;
926            }
927        }
928
929        /** Adds a USHORT to the response */
930        protected void addUShort(final int value) {
931            addByte((byte) (value & 0xff));
932            addByte((byte) (value >> 8 & 0xff));
933        }
934
935        /** Adds a ULong to the response */
936        protected void addULong(final int value) {
937            addByte((byte) (value & 0xff));
938            addByte((byte) (value >> 8 & 0xff));
939            addByte((byte) (value >> 16 & 0xff));
940            addByte((byte) (value >> 24 & 0xff));
941        }
942
943        /**
944         * Returns the response that has been generated after shrinking the
945         * array if required and base64 encodes the response.
946         *
947         * @return The response as above.
948         */
949        String getResponse() {
950            final byte[] resp;
951            if (messageContents.length > currentOutputPosition) {
952                final byte[] tmp = new byte[currentOutputPosition];
953                System.arraycopy(messageContents, 0, tmp, 0, currentOutputPosition);
954                resp = tmp;
955            } else {
956                resp = messageContents;
957            }
958            return EncodingUtils.getAsciiString(Base64.encodeBase64(resp));
959        }
960
961    }
962
963    /** Type 1 message assembly class */
964    static class Type1Message extends NTLMMessage {
965
966        private final byte[] hostBytes;
967        private final byte[] domainBytes;
968
969        Type1Message(final String domain, final String host) throws NTLMEngineException {
970            super();
971            if (UNICODE_LITTLE_UNMARKED == null) {
972                throw new NTLMEngineException("Unicode not supported");
973            }
974            // Strip off domain name from the host!
975            final String unqualifiedHost = convertHost(host);
976            // Use only the base domain name!
977            final String unqualifiedDomain = convertDomain(domain);
978
979            hostBytes = unqualifiedHost != null ?
980                    unqualifiedHost.getBytes(UNICODE_LITTLE_UNMARKED) : null;
981            domainBytes = unqualifiedDomain != null ?
982                    unqualifiedDomain.toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED) : null;
983        }
984
985        Type1Message() {
986            super();
987            hostBytes = null;
988            domainBytes = null;
989        }
990        /**
991         * Getting the response involves building the message before returning
992         * it
993         */
994        @Override
995        String getResponse() {
996            // Now, build the message. Calculate its length first, including
997            // signature or type.
998            final int finalLength = 32 + 8 /*+ hostBytes.length + domainBytes.length */;
999
1000            // Set up the response. This will initialize the signature, message
1001            // type, and flags.
1002            prepareResponse(finalLength, 1);
1003
1004            // Flags. These are the complete set of flags we support.
1005            addULong(
1006                    //FLAG_WORKSTATION_PRESENT |
1007                    //FLAG_DOMAIN_PRESENT |
1008
1009                    // Required flags
1010                    //FLAG_REQUEST_LAN_MANAGER_KEY |
1011                    FLAG_REQUEST_NTLMv1 |
1012                    FLAG_REQUEST_NTLM2_SESSION |
1013
1014                    // Protocol version request
1015                    FLAG_REQUEST_VERSION |
1016
1017                    // Recommended privacy settings
1018                    FLAG_REQUEST_ALWAYS_SIGN |
1019                    //FLAG_REQUEST_SEAL |
1020                    //FLAG_REQUEST_SIGN |
1021
1022                    // These must be set according to documentation, based on use of SEAL above
1023                    FLAG_REQUEST_128BIT_KEY_EXCH |
1024                    FLAG_REQUEST_56BIT_ENCRYPTION |
1025                    //FLAG_REQUEST_EXPLICIT_KEY_EXCH |
1026
1027                    FLAG_REQUEST_UNICODE_ENCODING);
1028
1029            // Domain length (two times).
1030            addUShort(/*domainBytes.length*/0);
1031            addUShort(/*domainBytes.length*/0);
1032
1033            // Domain offset.
1034            addULong(/*hostBytes.length +*/ 32 + 8);
1035
1036            // Host length (two times).
1037            addUShort(/*hostBytes.length*/0);
1038            addUShort(/*hostBytes.length*/0);
1039
1040            // Host offset (always 32 + 8).
1041            addULong(32 + 8);
1042
1043            // Version
1044            addUShort(0x0105);
1045            // Build
1046            addULong(2600);
1047            // NTLM revision
1048            addUShort(0x0f00);
1049
1050            // Host (workstation) String.
1051            if (hostBytes != null) {
1052                addBytes(hostBytes);
1053            }
1054            // Domain String.
1055            if (domainBytes != null) {
1056                addBytes(domainBytes);
1057            }
1058
1059            return super.getResponse();
1060        }
1061
1062    }
1063
1064    /** Type 2 message class */
1065    static class Type2Message extends NTLMMessage {
1066        protected byte[] challenge;
1067        protected String target;
1068        protected byte[] targetInfo;
1069        protected int flags;
1070
1071        Type2Message(final String message) throws NTLMEngineException {
1072            super(message, 2);
1073
1074            // Type 2 message is laid out as follows:
1075            // First 8 bytes: NTLMSSP[0]
1076            // Next 4 bytes: Ulong, value 2
1077            // Next 8 bytes, starting at offset 12: target field (2 ushort lengths, 1 ulong offset)
1078            // Next 4 bytes, starting at offset 20: Flags, e.g. 0x22890235
1079            // Next 8 bytes, starting at offset 24: Challenge
1080            // Next 8 bytes, starting at offset 32: ??? (8 bytes of zeros)
1081            // Next 8 bytes, starting at offset 40: targetinfo field (2 ushort lengths, 1 ulong offset)
1082            // Next 2 bytes, major/minor version number (e.g. 0x05 0x02)
1083            // Next 8 bytes, build number
1084            // Next 2 bytes, protocol version number (e.g. 0x00 0x0f)
1085            // Next, various text fields, and a ushort of value 0 at the end
1086
1087            // Parse out the rest of the info we need from the message
1088            // The nonce is the 8 bytes starting from the byte in position 24.
1089            challenge = new byte[8];
1090            readBytes(challenge, 24);
1091
1092            flags = readULong(20);
1093
1094            if ((flags & FLAG_REQUEST_UNICODE_ENCODING) == 0) {
1095                throw new NTLMEngineException(
1096                        "NTLM type 2 message indicates no support for Unicode. Flags are: "
1097                                + Integer.toString(flags));
1098            }
1099
1100            // Do the target!
1101            target = null;
1102            // The TARGET_DESIRED flag is said to not have understood semantics
1103            // in Type2 messages, so use the length of the packet to decide
1104            // how to proceed instead
1105            if (getMessageLength() >= 12 + 8) {
1106                final byte[] bytes = readSecurityBuffer(12);
1107                if (bytes.length != 0) {
1108                    try {
1109                        target = new String(bytes, "UnicodeLittleUnmarked");
1110                    } catch (final UnsupportedEncodingException e) {
1111                        throw new NTLMEngineException(e.getMessage(), e);
1112                    }
1113                }
1114            }
1115
1116            // Do the target info!
1117            targetInfo = null;
1118            // TARGET_DESIRED flag cannot be relied on, so use packet length
1119            if (getMessageLength() >= 40 + 8) {
1120                final byte[] bytes = readSecurityBuffer(40);
1121                if (bytes.length != 0) {
1122                    targetInfo = bytes;
1123                }
1124            }
1125        }
1126
1127        /** Retrieve the challenge */
1128        byte[] getChallenge() {
1129            return challenge;
1130        }
1131
1132        /** Retrieve the target */
1133        String getTarget() {
1134            return target;
1135        }
1136
1137        /** Retrieve the target info */
1138        byte[] getTargetInfo() {
1139            return targetInfo;
1140        }
1141
1142        /** Retrieve the response flags */
1143        int getFlags() {
1144            return flags;
1145        }
1146
1147    }
1148
1149    /** Type 3 message assembly class */
1150    static class Type3Message extends NTLMMessage {
1151        // Response flags from the type2 message
1152        protected int type2Flags;
1153
1154        protected byte[] domainBytes;
1155        protected byte[] hostBytes;
1156        protected byte[] userBytes;
1157
1158        protected byte[] lmResp;
1159        protected byte[] ntResp;
1160        protected byte[] sessionKey;
1161
1162
1163        /** Constructor. Pass the arguments we will need */
1164        Type3Message(final String domain, final String host, final String user, final String password, final byte[] nonce,
1165                final int type2Flags, final String target, final byte[] targetInformation)
1166                throws NTLMEngineException {
1167            // Save the flags
1168            this.type2Flags = type2Flags;
1169
1170            // Strip off domain name from the host!
1171            final String unqualifiedHost = convertHost(host);
1172            // Use only the base domain name!
1173            final String unqualifiedDomain = convertDomain(domain);
1174
1175            // Create a cipher generator class.  Use domain BEFORE it gets modified!
1176            final CipherGen gen = new CipherGen(unqualifiedDomain, user, password, nonce, target, targetInformation);
1177
1178            // Use the new code to calculate the responses, including v2 if that
1179            // seems warranted.
1180            byte[] userSessionKey;
1181            try {
1182                // This conditional may not work on Windows Server 2008 R2 and above, where it has not yet
1183                // been tested
1184                if (((type2Flags & FLAG_TARGETINFO_PRESENT) != 0) &&
1185                    targetInformation != null && target != null) {
1186                    // NTLMv2
1187                    ntResp = gen.getNTLMv2Response();
1188                    lmResp = gen.getLMv2Response();
1189                    if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1190                        userSessionKey = gen.getLanManagerSessionKey();
1191                    } else {
1192                        userSessionKey = gen.getNTLMv2UserSessionKey();
1193                    }
1194                } else {
1195                    // NTLMv1
1196                    if ((type2Flags & FLAG_REQUEST_NTLM2_SESSION) != 0) {
1197                        // NTLM2 session stuff is requested
1198                        ntResp = gen.getNTLM2SessionResponse();
1199                        lmResp = gen.getLM2SessionResponse();
1200                        if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1201                            userSessionKey = gen.getLanManagerSessionKey();
1202                        } else {
1203                            userSessionKey = gen.getNTLM2SessionResponseUserSessionKey();
1204                        }
1205                    } else {
1206                        ntResp = gen.getNTLMResponse();
1207                        lmResp = gen.getLMResponse();
1208                        if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1209                            userSessionKey = gen.getLanManagerSessionKey();
1210                        } else {
1211                            userSessionKey = gen.getNTLMUserSessionKey();
1212                        }
1213                    }
1214                }
1215            } catch (final NTLMEngineException e) {
1216                // This likely means we couldn't find the MD4 hash algorithm -
1217                // fail back to just using LM
1218                ntResp = new byte[0];
1219                lmResp = gen.getLMResponse();
1220                if ((type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) != 0) {
1221                    userSessionKey = gen.getLanManagerSessionKey();
1222                } else {
1223                    userSessionKey = gen.getLMUserSessionKey();
1224                }
1225            }
1226
1227            if ((type2Flags & FLAG_REQUEST_SIGN) != 0) {
1228                if ((type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) != 0) {
1229                    sessionKey = RC4(gen.getSecondaryKey(), userSessionKey);
1230                } else {
1231                    sessionKey = userSessionKey;
1232                }
1233            } else {
1234                sessionKey = null;
1235            }
1236            if (UNICODE_LITTLE_UNMARKED == null) {
1237                throw new NTLMEngineException("Unicode not supported");
1238            }
1239            hostBytes = unqualifiedHost != null ? unqualifiedHost.getBytes(UNICODE_LITTLE_UNMARKED) : null;
1240            domainBytes = unqualifiedDomain != null ? unqualifiedDomain
1241                    .toUpperCase(Locale.ROOT).getBytes(UNICODE_LITTLE_UNMARKED) : null;
1242            userBytes = user.getBytes(UNICODE_LITTLE_UNMARKED);
1243        }
1244
1245        /** Assemble the response */
1246        @Override
1247        String getResponse() {
1248            final int ntRespLen = ntResp.length;
1249            final int lmRespLen = lmResp.length;
1250
1251            final int domainLen = domainBytes != null ? domainBytes.length : 0;
1252            final int hostLen = hostBytes != null ? hostBytes.length: 0;
1253            final int userLen = userBytes.length;
1254            final int sessionKeyLen;
1255            if (sessionKey != null) {
1256                sessionKeyLen = sessionKey.length;
1257            } else {
1258                sessionKeyLen = 0;
1259            }
1260
1261            // Calculate the layout within the packet
1262            final int lmRespOffset = 72;  // allocate space for the version
1263            final int ntRespOffset = lmRespOffset + lmRespLen;
1264            final int domainOffset = ntRespOffset + ntRespLen;
1265            final int userOffset = domainOffset + domainLen;
1266            final int hostOffset = userOffset + userLen;
1267            final int sessionKeyOffset = hostOffset + hostLen;
1268            final int finalLength = sessionKeyOffset + sessionKeyLen;
1269
1270            // Start the response. Length includes signature and type
1271            prepareResponse(finalLength, 3);
1272
1273            // LM Resp Length (twice)
1274            addUShort(lmRespLen);
1275            addUShort(lmRespLen);
1276
1277            // LM Resp Offset
1278            addULong(lmRespOffset);
1279
1280            // NT Resp Length (twice)
1281            addUShort(ntRespLen);
1282            addUShort(ntRespLen);
1283
1284            // NT Resp Offset
1285            addULong(ntRespOffset);
1286
1287            // Domain length (twice)
1288            addUShort(domainLen);
1289            addUShort(domainLen);
1290
1291            // Domain offset.
1292            addULong(domainOffset);
1293
1294            // User Length (twice)
1295            addUShort(userLen);
1296            addUShort(userLen);
1297
1298            // User offset
1299            addULong(userOffset);
1300
1301            // Host length (twice)
1302            addUShort(hostLen);
1303            addUShort(hostLen);
1304
1305            // Host offset
1306            addULong(hostOffset);
1307
1308            // Session key length (twice)
1309            addUShort(sessionKeyLen);
1310            addUShort(sessionKeyLen);
1311
1312            // Session key offset
1313            addULong(sessionKeyOffset);
1314
1315            // Flags.
1316            addULong(
1317                    //FLAG_WORKSTATION_PRESENT |
1318                    //FLAG_DOMAIN_PRESENT |
1319
1320                    // Required flags
1321                    (type2Flags & FLAG_REQUEST_LAN_MANAGER_KEY) |
1322                    (type2Flags & FLAG_REQUEST_NTLMv1) |
1323                    (type2Flags & FLAG_REQUEST_NTLM2_SESSION) |
1324
1325                    // Protocol version request
1326                    FLAG_REQUEST_VERSION |
1327
1328                    // Recommended privacy settings
1329                    (type2Flags & FLAG_REQUEST_ALWAYS_SIGN) |
1330                    (type2Flags & FLAG_REQUEST_SEAL) |
1331                    (type2Flags & FLAG_REQUEST_SIGN) |
1332
1333                    // These must be set according to documentation, based on use of SEAL above
1334                    (type2Flags & FLAG_REQUEST_128BIT_KEY_EXCH) |
1335                    (type2Flags & FLAG_REQUEST_56BIT_ENCRYPTION) |
1336                    (type2Flags & FLAG_REQUEST_EXPLICIT_KEY_EXCH) |
1337
1338                    (type2Flags & FLAG_TARGETINFO_PRESENT) |
1339                    (type2Flags & FLAG_REQUEST_UNICODE_ENCODING) |
1340                    (type2Flags & FLAG_REQUEST_TARGET)
1341            );
1342
1343            // Version
1344            addUShort(0x0105);
1345            // Build
1346            addULong(2600);
1347            // NTLM revision
1348            addUShort(0x0f00);
1349
1350            // Add the actual data
1351            addBytes(lmResp);
1352            addBytes(ntResp);
1353            addBytes(domainBytes);
1354            addBytes(userBytes);
1355            addBytes(hostBytes);
1356            if (sessionKey != null) {
1357                addBytes(sessionKey);
1358            }
1359
1360            return super.getResponse();
1361        }
1362    }
1363
1364    static void writeULong(final byte[] buffer, final int value, final int offset) {
1365        buffer[offset] = (byte) (value & 0xff);
1366        buffer[offset + 1] = (byte) (value >> 8 & 0xff);
1367        buffer[offset + 2] = (byte) (value >> 16 & 0xff);
1368        buffer[offset + 3] = (byte) (value >> 24 & 0xff);
1369    }
1370
1371    static int F(final int x, final int y, final int z) {
1372        return ((x & y) | (~x & z));
1373    }
1374
1375    static int G(final int x, final int y, final int z) {
1376        return ((x & y) | (x & z) | (y & z));
1377    }
1378
1379    static int H(final int x, final int y, final int z) {
1380        return (x ^ y ^ z);
1381    }
1382
1383    static int rotintlft(final int val, final int numbits) {
1384        return ((val << numbits) | (val >>> (32 - numbits)));
1385    }
1386
1387    /**
1388     * Cryptography support - MD4. The following class was based loosely on the
1389     * RFC and on code found at http://www.cs.umd.edu/~harry/jotp/src/md.java.
1390     * Code correctness was verified by looking at MD4.java from the jcifs
1391     * library (http://jcifs.samba.org). It was massaged extensively to the
1392     * final form found here by Karl Wright (kwright@metacarta.com).
1393     */
1394    static class MD4 {
1395        protected int A = 0x67452301;
1396        protected int B = 0xefcdab89;
1397        protected int C = 0x98badcfe;
1398        protected int D = 0x10325476;
1399        protected long count = 0L;
1400        protected byte[] dataBuffer = new byte[64];
1401
1402        MD4() {
1403        }
1404
1405        void update(final byte[] input) {
1406            // We always deal with 512 bits at a time. Correspondingly, there is
1407            // a buffer 64 bytes long that we write data into until it gets
1408            // full.
1409            int curBufferPos = (int) (count & 63L);
1410            int inputIndex = 0;
1411            while (input.length - inputIndex + curBufferPos >= dataBuffer.length) {
1412                // We have enough data to do the next step. Do a partial copy
1413                // and a transform, updating inputIndex and curBufferPos
1414                // accordingly
1415                final int transferAmt = dataBuffer.length - curBufferPos;
1416                System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1417                count += transferAmt;
1418                curBufferPos = 0;
1419                inputIndex += transferAmt;
1420                processBuffer();
1421            }
1422
1423            // If there's anything left, copy it into the buffer and leave it.
1424            // We know there's not enough left to process.
1425            if (inputIndex < input.length) {
1426                final int transferAmt = input.length - inputIndex;
1427                System.arraycopy(input, inputIndex, dataBuffer, curBufferPos, transferAmt);
1428                count += transferAmt;
1429                curBufferPos += transferAmt;
1430            }
1431        }
1432
1433        byte[] getOutput() {
1434            // Feed pad/length data into engine. This must round out the input
1435            // to a multiple of 512 bits.
1436            final int bufferIndex = (int) (count & 63L);
1437            final int padLen = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
1438            final byte[] postBytes = new byte[padLen + 8];
1439            // Leading 0x80, specified amount of zero padding, then length in
1440            // bits.
1441            postBytes[0] = (byte) 0x80;
1442            // Fill out the last 8 bytes with the length
1443            for (int i = 0; i < 8; i++) {
1444                postBytes[padLen + i] = (byte) ((count * 8) >>> (8 * i));
1445            }
1446
1447            // Update the engine
1448            update(postBytes);
1449
1450            // Calculate final result
1451            final byte[] result = new byte[16];
1452            writeULong(result, A, 0);
1453            writeULong(result, B, 4);
1454            writeULong(result, C, 8);
1455            writeULong(result, D, 12);
1456            return result;
1457        }
1458
1459        protected void processBuffer() {
1460            // Convert current buffer to 16 ulongs
1461            final int[] d = new int[16];
1462
1463            for (int i = 0; i < 16; i++) {
1464                d[i] = (dataBuffer[i * 4] & 0xff) + ((dataBuffer[i * 4 + 1] & 0xff) << 8)
1465                        + ((dataBuffer[i * 4 + 2] & 0xff) << 16)
1466                        + ((dataBuffer[i * 4 + 3] & 0xff) << 24);
1467            }
1468
1469            // Do a round of processing
1470            final int AA = A;
1471            final int BB = B;
1472            final int CC = C;
1473            final int DD = D;
1474            round1(d);
1475            round2(d);
1476            round3(d);
1477            A += AA;
1478            B += BB;
1479            C += CC;
1480            D += DD;
1481
1482        }
1483
1484        protected void round1(final int[] d) {
1485            A = rotintlft((A + F(B, C, D) + d[0]), 3);
1486            D = rotintlft((D + F(A, B, C) + d[1]), 7);
1487            C = rotintlft((C + F(D, A, B) + d[2]), 11);
1488            B = rotintlft((B + F(C, D, A) + d[3]), 19);
1489
1490            A = rotintlft((A + F(B, C, D) + d[4]), 3);
1491            D = rotintlft((D + F(A, B, C) + d[5]), 7);
1492            C = rotintlft((C + F(D, A, B) + d[6]), 11);
1493            B = rotintlft((B + F(C, D, A) + d[7]), 19);
1494
1495            A = rotintlft((A + F(B, C, D) + d[8]), 3);
1496            D = rotintlft((D + F(A, B, C) + d[9]), 7);
1497            C = rotintlft((C + F(D, A, B) + d[10]), 11);
1498            B = rotintlft((B + F(C, D, A) + d[11]), 19);
1499
1500            A = rotintlft((A + F(B, C, D) + d[12]), 3);
1501            D = rotintlft((D + F(A, B, C) + d[13]), 7);
1502            C = rotintlft((C + F(D, A, B) + d[14]), 11);
1503            B = rotintlft((B + F(C, D, A) + d[15]), 19);
1504        }
1505
1506        protected void round2(final int[] d) {
1507            A = rotintlft((A + G(B, C, D) + d[0] + 0x5a827999), 3);
1508            D = rotintlft((D + G(A, B, C) + d[4] + 0x5a827999), 5);
1509            C = rotintlft((C + G(D, A, B) + d[8] + 0x5a827999), 9);
1510            B = rotintlft((B + G(C, D, A) + d[12] + 0x5a827999), 13);
1511
1512            A = rotintlft((A + G(B, C, D) + d[1] + 0x5a827999), 3);
1513            D = rotintlft((D + G(A, B, C) + d[5] + 0x5a827999), 5);
1514            C = rotintlft((C + G(D, A, B) + d[9] + 0x5a827999), 9);
1515            B = rotintlft((B + G(C, D, A) + d[13] + 0x5a827999), 13);
1516
1517            A = rotintlft((A + G(B, C, D) + d[2] + 0x5a827999), 3);
1518            D = rotintlft((D + G(A, B, C) + d[6] + 0x5a827999), 5);
1519            C = rotintlft((C + G(D, A, B) + d[10] + 0x5a827999), 9);
1520            B = rotintlft((B + G(C, D, A) + d[14] + 0x5a827999), 13);
1521
1522            A = rotintlft((A + G(B, C, D) + d[3] + 0x5a827999), 3);
1523            D = rotintlft((D + G(A, B, C) + d[7] + 0x5a827999), 5);
1524            C = rotintlft((C + G(D, A, B) + d[11] + 0x5a827999), 9);
1525            B = rotintlft((B + G(C, D, A) + d[15] + 0x5a827999), 13);
1526
1527        }
1528
1529        protected void round3(final int[] d) {
1530            A = rotintlft((A + H(B, C, D) + d[0] + 0x6ed9eba1), 3);
1531            D = rotintlft((D + H(A, B, C) + d[8] + 0x6ed9eba1), 9);
1532            C = rotintlft((C + H(D, A, B) + d[4] + 0x6ed9eba1), 11);
1533            B = rotintlft((B + H(C, D, A) + d[12] + 0x6ed9eba1), 15);
1534
1535            A = rotintlft((A + H(B, C, D) + d[2] + 0x6ed9eba1), 3);
1536            D = rotintlft((D + H(A, B, C) + d[10] + 0x6ed9eba1), 9);
1537            C = rotintlft((C + H(D, A, B) + d[6] + 0x6ed9eba1), 11);
1538            B = rotintlft((B + H(C, D, A) + d[14] + 0x6ed9eba1), 15);
1539
1540            A = rotintlft((A + H(B, C, D) + d[1] + 0x6ed9eba1), 3);
1541            D = rotintlft((D + H(A, B, C) + d[9] + 0x6ed9eba1), 9);
1542            C = rotintlft((C + H(D, A, B) + d[5] + 0x6ed9eba1), 11);
1543            B = rotintlft((B + H(C, D, A) + d[13] + 0x6ed9eba1), 15);
1544
1545            A = rotintlft((A + H(B, C, D) + d[3] + 0x6ed9eba1), 3);
1546            D = rotintlft((D + H(A, B, C) + d[11] + 0x6ed9eba1), 9);
1547            C = rotintlft((C + H(D, A, B) + d[7] + 0x6ed9eba1), 11);
1548            B = rotintlft((B + H(C, D, A) + d[15] + 0x6ed9eba1), 15);
1549
1550        }
1551
1552    }
1553
1554    /**
1555     * Cryptography support - HMACMD5 - algorithmically based on various web
1556     * resources by Karl Wright
1557     */
1558    static class HMACMD5 {
1559        protected byte[] ipad;
1560        protected byte[] opad;
1561        protected MessageDigest md5;
1562
1563        HMACMD5(final byte[] input) throws NTLMEngineException {
1564            byte[] key = input;
1565            try {
1566                md5 = MessageDigest.getInstance("MD5");
1567            } catch (final Exception ex) {
1568                // Umm, the algorithm doesn't exist - throw an
1569                // NTLMEngineException!
1570                throw new NTLMEngineException(
1571                        "Error getting md5 message digest implementation: " + ex.getMessage(), ex);
1572            }
1573
1574            // Initialize the pad buffers with the key
1575            ipad = new byte[64];
1576            opad = new byte[64];
1577
1578            int keyLength = key.length;
1579            if (keyLength > 64) {
1580                // Use MD5 of the key instead, as described in RFC 2104
1581                md5.update(key);
1582                key = md5.digest();
1583                keyLength = key.length;
1584            }
1585            int i = 0;
1586            while (i < keyLength) {
1587                ipad[i] = (byte) (key[i] ^ (byte) 0x36);
1588                opad[i] = (byte) (key[i] ^ (byte) 0x5c);
1589                i++;
1590            }
1591            while (i < 64) {
1592                ipad[i] = (byte) 0x36;
1593                opad[i] = (byte) 0x5c;
1594                i++;
1595            }
1596
1597            // Very important: update the digest with the ipad buffer
1598            md5.reset();
1599            md5.update(ipad);
1600
1601        }
1602
1603        /** Grab the current digest. This is the "answer". */
1604        byte[] getOutput() {
1605            final byte[] digest = md5.digest();
1606            md5.update(opad);
1607            return md5.digest(digest);
1608        }
1609
1610        /** Update by adding a complete array */
1611        void update(final byte[] input) {
1612            md5.update(input);
1613        }
1614
1615        /** Update the algorithm */
1616        void update(final byte[] input, final int offset, final int length) {
1617            md5.update(input, offset, length);
1618        }
1619
1620    }
1621
1622    @Override
1623    public String generateType1Msg(
1624            final String domain,
1625            final String workstation) throws NTLMEngineException {
1626        return getType1Message(workstation, domain);
1627    }
1628
1629    @Override
1630    public String generateType3Msg(
1631            final String username,
1632            final String password,
1633            final String domain,
1634            final String workstation,
1635            final String challenge) throws NTLMEngineException {
1636        final Type2Message t2m = new Type2Message(challenge);
1637        return getType3Message(
1638                username,
1639                password,
1640                workstation,
1641                domain,
1642                t2m.getChallenge(),
1643                t2m.getFlags(),
1644                t2m.getTarget(),
1645                t2m.getTargetInfo());
1646    }
1647
1648}