-
-
Save wbroek/8c5b2b740ca095250fee77b200f73251 to your computer and use it in GitHub Desktop.
/** | |
* Copyright (c) 2019 Wouter van den Broek https://github.com/wbroek/ | |
* All rights reserved. | |
*/ | |
const axios = require('axios'); | |
const ntlm = require('./ntlm'); | |
// NODE JS | |
const https = require('https'); | |
const httpsAgent = new https.Agent({ keepAlive: true }); | |
const client = axios.create({ | |
httpsAgent, | |
agent: httpsAgent, | |
withCredentials: true, | |
shouldKeepAlive: true, | |
keepAlive: true, | |
keepAliveMsecs: 3000, | |
maxRedirects: 0, | |
'Access-Control-Allow-Origin': '*', | |
}); | |
var options = { | |
url: '', | |
username: '', | |
password: '', | |
workstation: '', | |
domain: '' | |
}; | |
client.interceptors.response.use( | |
(response) => { | |
// IF DEV console.log('Response:', response); | |
return response; | |
}, | |
(err) => { | |
// IF DEV console.log('Response error:',err); | |
const error = err.response; | |
if (error && error.status === 401 && error.headers['www-authenticate'] && error.headers['www-authenticate'] === 'Negotiate, NTLM' && !err.config.headers['X-retry']) { | |
// TYPE 1 MESSAGE | |
return sendType1Message(); | |
} else if (error && error.status === 401 && error.headers['www-authenticate'] && error.headers['www-authenticate'].substring(0,4) === 'NTLM' ) { | |
// TYPE 2 MESSAGE PARSE ANS TYPE 3 MESSAGE SEND | |
return sendType3Message(error.headers['www-authenticate']); | |
} | |
return err; | |
}, | |
); | |
client.interceptors.request.use((request) => { | |
// IF DEV console.log('Starting Request', request); | |
return request; | |
}); | |
const sendType1Message = () => { | |
var type1msg = ntlm.createType1Message(options); | |
return client({ | |
method: 'get', | |
url: options.url, | |
headers:{ | |
'Connection' : 'keep-alive', | |
'Authorization': type1msg | |
}, | |
}); | |
}; | |
const sendType3Message = token => { | |
var type2msg = ntlm.parseType2Message(token, (err) => { console.log(err) }); | |
var type3msg = ntlm.createType3Message(type2msg, options); | |
return client({ | |
method: 'get', | |
url: options.url, | |
headers:{ | |
'X-retry' : 'false', | |
'Connection' : 'Close', | |
'Authorization': type3msg | |
}, | |
}) | |
} | |
client({ | |
method: 'get', | |
url: options.url, | |
}).then((response) => { | |
console.log(response); | |
}) | |
.catch((error) => { | |
console.log(error); | |
}); | |
/** | |
* 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; | |
You forgot to fix the second if (error.status === 401...
at line 42 of file fetch-axios-ntlm.js ;)
It should also be if (error && error.status === 401...
@tbl0605 thank you for the improvement. Hopefully others can use it as well
Yes, hope it too, your work was very helpful to me ;) thanx!
Hi, could you help how should I send my parameters if I am trying to use a local user?. I tried with this:
username: '\mespinoza',
password: 'pass',
workstation: 'INGLP-DH68.local',
domain: 'INGLP-DH68'
But It doesn't work. It returns error 403
I'm curious as to what insertZerosEvery7Bits
is supposed to be doing?
I wrote a little test to understand it more and it appears to not be giving consistent results.
it("zero", async () => {
const buf1 = Buffer.from("ABC");
const buf2 = insertZerosEvery7Bits(buf1);
console.log(Array.from(buf1.values()).map((i) => i.toString(2).split("")));
console.log(Array.from(buf2.values()).map((i) => i.toString(2).split("")));
});
NTLM
✓ zero (7 ms)
console.log
[
[
'1', '0', '0',
'0', '0', '0',
'1'
],
[
'1', '0', '0',
'0', '0', '1',
'0'
],
[
'1', '0', '0',
'0', '0', '1',
'1'
]
]
at Object.<anonymous> (src/__tests__/ntlm.ts:57:13)
console.log
[
[
'1', '0', '0',
'0', '0', '0',
'0'
],
[
'1', '0', '1',
'0', '0', '0',
'0', '0'
],
[
'1', '0', '0',
'1', '0', '0',
'0', '0'
]
]
at Object.<anonymous> (src/__tests__/ntlm.ts:58:13)
I think I understand now after reading this: http://davenport.sourceforge.net/ntlm.html#theLmResponse
Unfortunately I don't think insertZerosEvery7Bits
is working correctly and therefore breaking the encryption.
I did create a substitute function that adds a parity bit to each byte of a buffer:
export function setParity(part: Buffer): Buffer {
return Buffer.from(
toMatrix(
Array.from(part.values())
.map((i) => i.toString(2).padStart(8, "0"))
.reduce((p, c) => `${p}${c}`, "")
.split(""),
7
)
.map((i) => [
...i,
i.reduce((p, c) => p + Number.parseInt(c), 0) % 2 ? "0" : "1",
])
.map((b) => b.join(""))
.map((n) => Number.parseInt(n, 2))
);
}
which also uses this function:
export const toMatrix = <T>(arr: T[], width: number): T[][] =>
Array.from(
Array<T>(Math.ceil(arr.length / width) * width),
(v, k) => arr[k]
).reduce((rows, key, index) => {
index % width == 0 ? rows.push([key]) : rows[rows.length - 1].push(key);
return rows;
}, [] as T[][]);
After more testing I think my initial conclusion is wrong.
This test shows that the insertZerosEvery7Bits
is properly creating a "none" parity bit.
it("parity (none)", async () => {
const password = "SecREt01".toLocaleUpperCase().padEnd(14, "\0");
const buf1 = Buffer.from(password.slice(0, 7));
const buf2 = insertZerosEvery7Bits(buf1);
const buf3 = setParity(buf1, "none");
console.log(buf1);
console.log(buf2);
console.log(buf3);
expect(buf2).toEqual(buf3);
});
PASS src/__tests__/ntlm.ts
NTLM
✓ parity (none) (8 ms)
console.log
<Buffer 53 45 43 52 45 54 30>
console.log
<Buffer 52 a2 50 6a 24 2a 50 60>
console.log
<Buffer 52 a2 50 6a 24 2a 50 60>
So I'm still not sure why the NTLM in this GIST does not work.
I implemented this in a React Native Application, and I couldn't make it work. Keeping getting 401 Error, no matter what.
Hi,
thank you very much for your code gist! :)
I just had to replace the 2 occurrences of
if (error.status === 401...
withif (error && error.status === 401...
because axios doesn't seterr.response
in case of network errors.