text: /**
   * Original by Sam Decrock https://github.com/SamDecrock/ (c) 2013
   * Modified for use outside of Node.JS by Wouter van den Broek https://github.com/wbroek/ (c) 2019
   * All rights reserved.
   */
  
   var Buffer = require('buffer').Buffer;
   var createCipheriv = require('browserify-cipher').createCipheriv;
   var createHash = require('create-hash');
   var md4 = require('js-md4');
   
   var flags = {
       NTLM_NegotiateUnicode                :  0x00000001,
       NTLM_NegotiateOEM                    :  0x00000002,
       NTLM_RequestTarget                   :  0x00000004,
       NTLM_Unknown9                        :  0x00000008,
       NTLM_NegotiateSign                   :  0x00000010,
       NTLM_NegotiateSeal                   :  0x00000020,
       NTLM_NegotiateDatagram               :  0x00000040,
       NTLM_NegotiateLanManagerKey          :  0x00000080,
       NTLM_Unknown8                        :  0x00000100,
       NTLM_NegotiateNTLM                   :  0x00000200,
       NTLM_NegotiateNTOnly                 :  0x00000400,
       NTLM_Anonymous                       :  0x00000800,
       NTLM_NegotiateOemDomainSupplied      :  0x00001000,
       NTLM_NegotiateOemWorkstationSupplied :  0x00002000,
       NTLM_Unknown6                        :  0x00004000,
       NTLM_NegotiateAlwaysSign             :  0x00008000,
       NTLM_TargetTypeDomain                :  0x00010000,
       NTLM_TargetTypeServer                :  0x00020000,
       NTLM_TargetTypeShare                 :  0x00040000,
       NTLM_NegotiateExtendedSecurity       :  0x00080000,
       NTLM_NegotiateIdentify               :  0x00100000,
       NTLM_Unknown5                        :  0x00200000,
       NTLM_RequestNonNTSessionKey          :  0x00400000,
       NTLM_NegotiateTargetInfo             :  0x00800000,
       NTLM_Unknown4                        :  0x01000000,
       NTLM_NegotiateVersion                :  0x02000000,
       NTLM_Unknown3                        :  0x04000000,
       NTLM_Unknown2                        :  0x08000000,
       NTLM_Unknown1                        :  0x10000000,
       NTLM_Negotiate128                    :  0x20000000,
       NTLM_NegotiateKeyExchange            :  0x40000000,
       NTLM_Negotiate56                     :  0x80000000
   };
   var typeflags = {
       NTLM_TYPE1_FLAGS : 	  flags.NTLM_NegotiateUnicode
                           + flags.NTLM_NegotiateOEM
                           + flags.NTLM_RequestTarget
                           + flags.NTLM_NegotiateNTLM
                           + flags.NTLM_NegotiateOemDomainSupplied
                           + flags.NTLM_NegotiateOemWorkstationSupplied
                           + flags.NTLM_NegotiateAlwaysSign
                           + flags.NTLM_NegotiateExtendedSecurity
                           + flags.NTLM_NegotiateVersion
                           + flags.NTLM_Negotiate128
                           + flags.NTLM_Negotiate56,
   
       NTLM_TYPE2_FLAGS :    flags.NTLM_NegotiateUnicode
                           + flags.NTLM_RequestTarget
                           + flags.NTLM_NegotiateNTLM
                           + flags.NTLM_NegotiateAlwaysSign
                           + flags.NTLM_NegotiateExtendedSecurity
                           + flags.NTLM_NegotiateTargetInfo
                           + flags.NTLM_NegotiateVersion
                           + flags.NTLM_Negotiate128
                           + flags.NTLM_Negotiate56
   };
   
   function createType1Message(options){
       var domain = escape(options.domain.toUpperCase());
       var workstation = escape(options.workstation.toUpperCase());
       var protocol = 'NTLMSSP\0';
   
       var BODY_LENGTH = 40;
   
       var type1flags = typeflags.NTLM_TYPE1_FLAGS;
       if(!domain || domain === '')
           type1flags = type1flags - flags.NTLM_NegotiateOemDomainSupplied;
   
       var pos = 0;
       var buf = new Buffer(BODY_LENGTH + domain.length + workstation.length);
   
   
       buf.write(protocol, pos, protocol.length); pos += protocol.length; // protocol
       buf.writeUInt32LE(1, pos); pos += 4;          // type 1
       buf.writeUInt32LE(type1flags, pos); pos += 4; // TYPE1 flag
   
       buf.writeUInt16LE(domain.length, pos); pos += 2; // domain length
       buf.writeUInt16LE(domain.length, pos); pos += 2; // domain max length
       buf.writeUInt32LE(BODY_LENGTH + workstation.length, pos); pos += 4; // domain buffer offset
   
       buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation length
       buf.writeUInt16LE(workstation.length, pos); pos += 2; // workstation max length
       buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; // workstation buffer offset
   
       buf.writeUInt8(5, pos); pos += 1;      //ProductMajorVersion
       buf.writeUInt8(1, pos); pos += 1;      //ProductMinorVersion
       buf.writeUInt16LE(2600, pos); pos += 2; //ProductBuild
   
       buf.writeUInt8(0 , pos); pos += 1; //VersionReserved1
       buf.writeUInt8(0 , pos); pos += 1; //VersionReserved2
       buf.writeUInt8(0 , pos); pos += 1; //VersionReserved3
       buf.writeUInt8(15, pos); pos += 1; //NTLMRevisionCurrent
   
   
       // length checks is to fix issue #46 and possibly #57
       if(workstation.length !=0) buf.write(workstation, pos, workstation.length, 'ascii'); pos += workstation.length; // workstation string
       if(domain.length !=0)      buf.write(domain     , pos, domain.length     , 'ascii'); pos += domain.length; // domain string
   
       return 'NTLM ' + buf.toString('base64');
   }
   
   function parseType2Message(rawmsg, callback){
       var match = rawmsg.match(/NTLM (.+)?/);
       if(!match || !match[1]) {
           callback(new Error("Couldn't find NTLM in the message type2 comming from the server"));
           return null;
       }
   
       var buf = new Buffer(match[1], 'base64');
   
       var msg = {};
   
       msg.signature = buf.slice(0, 8);
       msg.type = buf.readInt16LE(8);
   
       if(msg.type != 2) {
           callback(new Error("Server didn't return a type 2 message"));
           return null;
       }
   
       msg.targetNameLen = buf.readInt16LE(12);
       msg.targetNameMaxLen = buf.readInt16LE(14);
       msg.targetNameOffset = buf.readInt32LE(16);
       msg.targetName  = buf.slice(msg.targetNameOffset, msg.targetNameOffset + msg.targetNameMaxLen);
   
       msg.negotiateFlags = buf.readInt32LE(20);
       msg.serverChallenge = buf.slice(24, 32);
       msg.reserved = buf.slice(32, 40);
   
       if(msg.negotiateFlags & flags.NTLM_NegotiateTargetInfo){
           msg.targetInfoLen = buf.readInt16LE(40);
           msg.targetInfoMaxLen = buf.readInt16LE(42);
           msg.targetInfoOffset = buf.readInt32LE(44);
           msg.targetInfo = buf.slice(msg.targetInfoOffset, msg.targetInfoOffset + msg.targetInfoLen);
       }
       return msg;
   }
   
   function createType3Message(msg2, options){
       var nonce = msg2.serverChallenge;
       var username = options.username;
       var password = options.password;
       var lm_password = options.lm_password;
       var nt_password = options.nt_password;
       var negotiateFlags = msg2.negotiateFlags;
   
       var isUnicode = negotiateFlags & flags.NTLM_NegotiateUnicode;
       var isNegotiateExtendedSecurity = negotiateFlags & flags.NTLM_NegotiateExtendedSecurity;
   
       var BODY_LENGTH = 72;
   
       var domainName = escape(options.domain.toUpperCase());
       var workstation = escape(options.workstation.toUpperCase());
   
       var workstationBytes, domainNameBytes, usernameBytes, encryptedRandomSessionKeyBytes;
   
       var encryptedRandomSessionKey = "";
       if(isUnicode){
           workstationBytes = new Buffer(workstation, 'utf16le');
           domainNameBytes = new Buffer(domainName, 'utf16le');
           usernameBytes = new Buffer(username, 'utf16le');
           encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'utf16le');
       }else{
           workstationBytes = new Buffer(workstation, 'ascii');
           domainNameBytes = new Buffer(domainName, 'ascii');
           usernameBytes = new Buffer(username, 'ascii');
           encryptedRandomSessionKeyBytes = new Buffer(encryptedRandomSessionKey, 'ascii');
       }
   
       var lmChallengeResponse = calc_resp((lm_password!=null)?lm_password:create_LM_hashed_password_v1(password), nonce);
       var ntChallengeResponse = calc_resp((nt_password!=null)?nt_password:create_NT_hashed_password_v1(password), nonce);
   
       if(isNegotiateExtendedSecurity){
           var pwhash = (nt_password!=null)?nt_password:create_NT_hashed_password_v1(password);
            var clientChallenge = "";
            for(var i=0; i < 8; i++){
                clientChallenge += String.fromCharCode( Math.floor(Math.random()*256) );
              }
              var clientChallengeBytes = new Buffer(clientChallenge, 'ascii');
           var challenges = ntlm2sr_calc_resp(pwhash, nonce, clientChallengeBytes);
           lmChallengeResponse = challenges.lmChallengeResponse;
           ntChallengeResponse = challenges.ntChallengeResponse;
       }
   
       var signature = 'NTLMSSP\0';
   
       var pos = 0;
       var buf = new Buffer(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length + encryptedRandomSessionKeyBytes.length);
   
       buf.write(signature, pos, signature.length); pos += signature.length;
       buf.writeUInt32LE(3, pos); pos += 4;          // type 1
   
       buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseLen
       buf.writeUInt16LE(lmChallengeResponse.length, pos); pos += 2; // LmChallengeResponseMaxLen
       buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length, pos); pos += 4; // LmChallengeResponseOffset
   
       buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseLen
       buf.writeUInt16LE(ntChallengeResponse.length, pos); pos += 2; // NtChallengeResponseMaxLen
       buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length, pos); pos += 4; // NtChallengeResponseOffset
   
       buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameLen
       buf.writeUInt16LE(domainNameBytes.length, pos); pos += 2; // DomainNameMaxLen
       buf.writeUInt32LE(BODY_LENGTH, pos); pos += 4; 			  // DomainNameOffset
   
       buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameLen
       buf.writeUInt16LE(usernameBytes.length, pos); pos += 2; // UserNameMaxLen
       buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length, pos); pos += 4; // UserNameOffset
   
       buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationLen
       buf.writeUInt16LE(workstationBytes.length, pos); pos += 2; // WorkstationMaxLen
       buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length, pos); pos += 4; // WorkstationOffset
   
       buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyLen
       buf.writeUInt16LE(encryptedRandomSessionKeyBytes.length, pos); pos += 2; // EncryptedRandomSessionKeyMaxLen
       buf.writeUInt32LE(BODY_LENGTH + domainNameBytes.length + usernameBytes.length + workstationBytes.length + lmChallengeResponse.length + ntChallengeResponse.length, pos); pos += 4; // EncryptedRandomSessionKeyOffset
   
       buf.writeUInt32LE(typeflags.NTLM_TYPE2_FLAGS, pos); pos += 4; // NegotiateFlags
   
       buf.writeUInt8(5, pos); pos++; // ProductMajorVersion
       buf.writeUInt8(1, pos); pos++; // ProductMinorVersion
       buf.writeUInt16LE(2600, pos); pos += 2; // ProductBuild
       buf.writeUInt8(0, pos); pos++; // VersionReserved1
       buf.writeUInt8(0, pos); pos++; // VersionReserved2
       buf.writeUInt8(0, pos); pos++; // VersionReserved3
       buf.writeUInt8(15, pos); pos++; // NTLMRevisionCurrent
   
       domainNameBytes.copy(buf, pos); pos += domainNameBytes.length;
       usernameBytes.copy(buf, pos); pos += usernameBytes.length;
       workstationBytes.copy(buf, pos); pos += workstationBytes.length;
       lmChallengeResponse.copy(buf, pos); pos += lmChallengeResponse.length;
       ntChallengeResponse.copy(buf, pos); pos += ntChallengeResponse.length;
       encryptedRandomSessionKeyBytes.copy(buf, pos); pos += encryptedRandomSessionKeyBytes.length;
   
       return 'NTLM ' + buf.toString('base64');
   }
   
   function create_LM_hashed_password_v1(password){
       // fix the password length to 14 bytes
       password = password.toUpperCase();
       var passwordBytes = new Buffer(password, 'ascii');
   
       var passwordBytesPadded = new Buffer(14);
       passwordBytesPadded.fill("\0");
       var sourceEnd = 14;
       if(passwordBytes.length < 14) sourceEnd = passwordBytes.length;
       passwordBytes.copy(passwordBytesPadded, 0, 0, sourceEnd);
   
       // split into 2 parts of 7 bytes:
       var firstPart = passwordBytesPadded.slice(0,7);
       var secondPart = passwordBytesPadded.slice(7);
   
       function encrypt(buf){
           var key = insertZerosEvery7Bits(buf);
           var des = createCipheriv('DES-ECB', key, '');
           return des.update("KGS!@#$%"); // page 57 in [MS-NLMP]);
       }
   
       var firstPartEncrypted = encrypt(firstPart);
       var secondPartEncrypted = encrypt(secondPart);
   
       return Buffer.concat([firstPartEncrypted, secondPartEncrypted]);
   }
   
   function insertZerosEvery7Bits(buf){
       var binaryArray = bytes2binaryArray(buf);
       var newBinaryArray = [];
       for(var i=0; i<binaryArray.length; i++){
           newBinaryArray.push(binaryArray[i]);
   
           if((i+1)%7 === 0){
               newBinaryArray.push(0);
           }
       }
       return binaryArray2bytes(newBinaryArray);
   }
   
   function bytes2binaryArray(buf){
       var hex2binary = {
           0: [0,0,0,0],
           1: [0,0,0,1],
           2: [0,0,1,0],
           3: [0,0,1,1],
           4: [0,1,0,0],
           5: [0,1,0,1],
           6: [0,1,1,0],
           7: [0,1,1,1],
           8: [1,0,0,0],
           9: [1,0,0,1],
           A: [1,0,1,0],
           B: [1,0,1,1],
           C: [1,1,0,0],
           D: [1,1,0,1],
           E: [1,1,1,0],
           F: [1,1,1,1]
       };
   
       var hexString = buf.toString('hex').toUpperCase();
       var array = [];
       for(var i=0; i<hexString.length; i++){
              var hexchar = hexString.charAt(i);
              array = array.concat(hex2binary[hexchar]);
          }
          return array;
   }
   
   function binaryArray2bytes(array){
       var binary2hex = {
           '0000': 0,
           '0001': 1,
           '0010': 2,
           '0011': 3,
           '0100': 4,
           '0101': 5,
           '0110': 6,
           '0111': 7,
           '1000': 8,
           '1001': 9,
           '1010': 'A',
           '1011': 'B',
           '1100': 'C',
           '1101': 'D',
           '1110': 'E',
           '1111': 'F'
       };
   
        var bufArray = [];
   
       for(var i=0; i<array.length; i +=8 ){
           if((i+7) > array.length)
               break;
   
           var binString1 = '' + array[i] + '' + array[i+1] + '' + array[i+2] + '' + array[i+3];
           var binString2 = '' + array[i+4] + '' + array[i+5] + '' + array[i+6] + '' + array[i+7];
              var hexchar1 = binary2hex[binString1];
              var hexchar2 = binary2hex[binString2];
   
              var buf = new Buffer(hexchar1 + '' + hexchar2, 'hex');
              bufArray.push(buf);
          }
   
          return Buffer.concat(bufArray);
   }
   
   function create_NT_hashed_password_v1(password){
       var buf = new Buffer(password, 'utf16le');
       var hash = md4.create();
       hash.update(buf);
       return new Buffer(hash.digest());
   }
   
   function calc_resp(password_hash, server_challenge){
       // padding with zeros to make the hash 21 bytes long
       var passHashPadded = new Buffer(21);
       passHashPadded.fill("\0");
       password_hash.copy(passHashPadded, 0, 0, password_hash.length);
   
       var resArray = [];
   
       var des = createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(0,7)), '');
       resArray.push( des.update(server_challenge.slice(0,8)) );
   
       des = createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(7,14)), '');
       resArray.push( des.update(server_challenge.slice(0,8)) );
   
       des = createCipheriv('DES-ECB', insertZerosEvery7Bits(passHashPadded.slice(14,21)), '');
       resArray.push( des.update(server_challenge.slice(0,8)) );
   
          return Buffer.concat(resArray);
   }
   
   function ntlm2sr_calc_resp(responseKeyNT, serverChallenge, clientChallenge){
       // padding with zeros to make the hash 16 bytes longer
       var lmChallengeResponse = new Buffer(clientChallenge.length + 16);
       lmChallengeResponse.fill("\0");
       clientChallenge.copy(lmChallengeResponse, 0, 0, clientChallenge.length);
   
       var buf = Buffer.concat([serverChallenge, clientChallenge]);
       var md5 = createHash('md5');
       md5.update(buf);
       var sess = md5.digest();
       var ntChallengeResponse = calc_resp(responseKeyNT, sess.slice(0,8));
   
       return {
           lmChallengeResponse: lmChallengeResponse,
           ntChallengeResponse: ntChallengeResponse
       };
   }
   
   exports.createType1Message = createType1Message;
   exports.parseType2Message = parseType2Message;
   exports.createType3Message = createType3Message;
   exports.create_NT_hashed_password = create_NT_hashed_password_v1;
   exports.create_LM_hashed_password = create_LM_hashed_password_v1;
   
   
   