diff --git a/.gitignore b/.gitignore index d6166ec..f7e4664 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules .*.swp +.vscode +yarn.lock diff --git a/lib/common.js b/lib/common.js index de56061..01f1e21 100644 --- a/lib/common.js +++ b/lib/common.js @@ -14,7 +14,8 @@ * Copyright (C) 2012 Joshua M. Clulow */ -var crypto = require('crypto'); +var { DES } = require('des.js'); +var md4 = require('js-md4'); function zeroextend(str, len) { @@ -46,7 +47,7 @@ function oddpar(buf) */ function expandkey(key56) { - var key64 = new Buffer(8); + var key64 = Buffer.alloc(8); key64[0] = key56[0] & 0xFE; key64[1] = ((key56[0] << 7) & 0xFF) | (key56[1] >> 1); @@ -65,13 +66,41 @@ function expandkey(key56) */ function bintohex(bin) { - var buf = (Buffer.isBuffer(buf) ? buf : new Buffer(bin, 'binary')); + var buf = (Buffer.isBuffer(buf) ? buf : Buffer.from(bin, 'binary')); var str = buf.toString('hex').toUpperCase(); return zeroextend(str, 32); } +function calculateDES(key, message) { + var desKey = new Buffer.alloc(8); + desKey[0] = key[0] & 0xFE; + desKey[1] = ((key[0] << 7) & 0xFF) | (key[1] >> 1); + desKey[2] = ((key[1] << 6) & 0xFF) | (key[2] >> 2); + desKey[3] = ((key[2] << 5) & 0xFF) | (key[3] >> 3); + desKey[4] = ((key[3] << 4) & 0xFF) | (key[4] >> 4); + desKey[5] = ((key[4] << 3) & 0xFF) | (key[5] >> 5); + desKey[6] = ((key[5] << 2) & 0xFF) | (key[6] >> 6); + desKey[7] = (key[6] << 1) & 0xFF; + for (var i = 0; i < 8; i++) { + var parity = 0; + for (var j = 1; j < 8; j++) { + parity += (desKey[i] >> j) % 2; + } + desKey[i] |= (parity % 2) === 0 ? 1 : 0; + } + var des = DES.create({ type: 'encrypt', key: desKey}); + return Buffer.from(des.update(message)); +} + +function calculateMD4(message) { + var md4sum = md4.create(); + md4sum.update(new Buffer.from(message, 'ucs2')); + return Buffer.from(md4sum.buffer()); +} module.exports.zeroextend = zeroextend; module.exports.oddpar = oddpar; module.exports.expandkey = expandkey; module.exports.bintohex = bintohex; +module.exports.calculateDES = calculateDES; +module.exports.calculateMD4 = calculateMD4; diff --git a/lib/ntlm.js b/lib/ntlm.js index 4d2863b..c1a02d2 100755 --- a/lib/ntlm.js +++ b/lib/ntlm.js @@ -14,12 +14,9 @@ * Copyright (C) 2012 Joshua M. Clulow */ -var log = console.log; -var crypto = require('crypto'); var $ = require('./common'); -var lmhashbuf = require('./smbhash').lmhashbuf; -var nthashbuf = require('./smbhash').nthashbuf; - +var { lmhashbuf, nthashbuf } = require('./smbhash'); +var { URL } = require('url'); function encodeType1(hostname, ntdomain) { hostname = hostname.toUpperCase(); @@ -28,7 +25,7 @@ function encodeType1(hostname, ntdomain) { var ntdomainlen = Buffer.byteLength(ntdomain, 'ascii'); var pos = 0; - var buf = new Buffer(32 + hostnamelen + ntdomainlen); + var buf = Buffer.alloc(32 + hostnamelen + ntdomainlen); buf.write('NTLMSSP', pos, 7, 'ascii'); // byte protocol[8]; pos += 7; @@ -76,7 +73,6 @@ function encodeType1(hostname, ntdomain) { return buf; } - /* * */ @@ -102,15 +98,10 @@ function encodeType3(username, hostname, ntdomain, nonce, password) { hostname = hostname.toUpperCase(); ntdomain = ntdomain.toUpperCase(); - var lmh = new Buffer(21); - lmhashbuf(password).copy(lmh); - lmh.fill(0x00, 16); // null pad to 21 bytes - var nth = new Buffer(21); - nthashbuf(password).copy(nth); - nth.fill(0x00, 16); // null pad to 21 bytes + const challenge = new Buffer.from(nonce, 'ascii') - var lmr = makeResponse(lmh, nonce); - var ntr = makeResponse(nth, nonce); + var lmr = makeResponse(lmhashbuf(password), challenge); + var ntr = makeResponse(nthashbuf(password), challenge); var usernamelen = Buffer.byteLength(username, 'ucs2'); var hostnamelen = Buffer.byteLength(hostname, 'ucs2'); @@ -126,7 +117,7 @@ function encodeType3(username, hostname, ntdomain, nonce, password) { var pos = 0; var msg_len = 64 + ntdomainlen + usernamelen + hostnamelen + lmrlen + ntrlen; - var buf = new Buffer(msg_len); + var buf = Buffer.alloc(msg_len); buf.write('NTLMSSP', pos, 7, 'ascii'); // byte protocol[8]; pos += 7; @@ -203,16 +194,18 @@ function encodeType3(username, hostname, ntdomain, nonce, password) { return buf; } -function makeResponse(hash, nonce) +function makeResponse(lmhash, challenge) { - var out = new Buffer(24); - for (var i = 0; i < 3; i++) { - var keybuf = $.oddpar($.expandkey(hash.slice(i * 7, i * 7 + 7))); - var des = crypto.createCipheriv('DES-ECB', keybuf, ''); - var str = des.update(nonce.toString('binary'), 'binary', 'binary'); - out.write(str, i * 8, i * 8 + 8, 'binary'); - } - return out; + let buf = new Buffer.alloc(24), + pwBuffer = new Buffer.alloc(21).fill(0); + + lmhash.copy(pwBuffer); + + $.calculateDES(pwBuffer.slice(0, 7), challenge).copy(buf); + $.calculateDES(pwBuffer.slice(7, 14), challenge).copy(buf, 8); + $.calculateDES(pwBuffer.slice(14), challenge).copy(buf, 16); + + return buf; } exports.encodeType1 = encodeType1; @@ -226,9 +219,9 @@ exports.challengeHeader = function (hostname, domain) { }; exports.responseHeader = function (res, url, domain, username, password) { - var serverNonce = new Buffer((res.headers['www-authenticate'].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1], 'base64'); - var hostname = require('url').parse(url).hostname; - return 'NTLM ' + exports.encodeType3(username, hostname, domain, exports.decodeType2(serverNonce), password).toString('base64') + const serverNonce = Buffer.from((res.headers['www-authenticate'].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1], 'base64'); + const hostname = new URL(url).hostname; + return 'NTLM ' + exports.encodeType3(username, hostname, domain, exports.decodeType2(serverNonce), password).toString('base64'); }; // Import smbhash module. diff --git a/lib/smbhash.js b/lib/smbhash.js index 4199b7b..f4489d1 100644 --- a/lib/smbhash.js +++ b/lib/smbhash.js @@ -14,7 +14,6 @@ * Copyright (C) 2011-2012 Joshua M. Clulow */ -var crypto = require('crypto'); var $ = require('./common'); /* @@ -22,45 +21,22 @@ var $ = require('./common'); */ function lmhashbuf(inputstr) { - /* ASCII --> uppercase */ - var x = inputstr.substring(0, 14).toUpperCase(); - var xl = Buffer.byteLength(x, 'ascii'); + let pwBuffer = new Buffer.alloc(14), + magicKey = new Buffer.from('KGS!@#$%', 'ascii'); - /* null pad to 14 bytes */ - var y = new Buffer(14); - y.write(x, 0, xl, 'ascii'); - y.fill(0, xl); + if (inputstr.length > 14) { + inputstr = inputstr.slice(0, 14); + } - /* insert odd parity bits in key */ - var halves = [ - $.oddpar($.expandkey(y.slice(0, 7))), - $.oddpar($.expandkey(y.slice(7, 14))) - ]; + pwBuffer.fill(0); + pwBuffer.write(inputstr.toUpperCase(), 0, 'ascii'); - /* DES encrypt magic number "KGS!@#$%" to two - * 8-byte ciphertexts, (ECB, no padding) - */ - var buf = new Buffer(16); - var pos = 0; - var cts = halves.forEach(function(z) { - var des = crypto.createCipheriv('DES-ECB', z, ''); - var str = des.update('KGS!@#$%', 'binary', 'binary'); - buf.write(str, pos, pos + 8, 'binary'); - pos += 8; - }); - - /* concat the two ciphertexts to form 16byte value, - * the LM hash */ - return buf; + return Buffer.from([...$.calculateDES(pwBuffer.slice(0, 7), magicKey), ...$.calculateDES(pwBuffer.slice(7), magicKey)]); } -function nthashbuf(str) +function nthashbuf(inputstr) { - /* take MD4 hash of UCS-2 encoded password */ - var ucs2 = new Buffer(str, 'ucs2'); - var md4 = crypto.createHash('md4'); - md4.update(ucs2); - return new Buffer(md4.digest('binary'), 'binary'); + return $.calculateMD4(inputstr) } function lmhash(is) diff --git a/package.json b/package.json index af547dd..c87ab53 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ntlm", - "version": "0.1.3", + "version": "0.2.1", "devDependencies": { "nodeunit": "*" }, @@ -18,5 +18,9 @@ ], "main": "lib/ntlm.js", "description": "NTLM authentication and Samba LM/NT hash library", - "homepage": "https://github.com/tcr/node-ntlm" + "homepage": "https://github.com/tcr/node-ntlm", + "dependencies": { + "des.js": "^1.1.0", + "js-md4": "^0.3.2" + } } diff --git a/tests/common.js b/tests/common.js index a4a70a9..c01e89b 100644 --- a/tests/common.js +++ b/tests/common.js @@ -17,9 +17,9 @@ var $ = require('../lib/common'); var GOOD = [ - { key56: new Buffer([0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x30]), - raw64: new Buffer([0x52, 0xa2, 0x50, 0x6a, 0x24, 0x2a, 0x50, 0x60]), - par64: new Buffer([0x52, 0xa2, 0x51, 0x6b, 0x25, 0x2a, 0x51, 0x61]) + { key56: Buffer.from([0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x30]), + raw64: Buffer.from([0x52, 0xa2, 0x50, 0x6a, 0x24, 0x2a, 0x50, 0x60]), + par64: Buffer.from([0x52, 0xa2, 0x51, 0x6b, 0x25, 0x2a, 0x51, 0x61]) } ]; diff --git a/tests/ntlm.js b/tests/ntlm.js index f185665..e731419 100644 --- a/tests/ntlm.js +++ b/tests/ntlm.js @@ -46,7 +46,7 @@ module.exports.type2_success = function(test) { test.expect(GOOD.length * 1); for (var i = 0; i < GOOD.length; i++) { var g = GOOD[i]; - var inbuf = new Buffer(g.messages[1], 'base64'); + var inbuf = Buffer.from(g.messages[1], 'base64'); var out = $.decodeType2(inbuf); test.strictEqual(out.toString('binary'), g.nonce); } diff --git a/tests/smbhash.js b/tests/smbhash.js index 9daff62..73350b5 100644 --- a/tests/smbhash.js +++ b/tests/smbhash.js @@ -14,8 +14,7 @@ * Copyright (C) 2011-2012 Joshua M. Clulow */ -var lmhash = require('..').smbhash.lmhash; -var nthash = require('..').smbhash.nthash; +const { lmhash, nthash } = require('../lib/smbhash'); var GOOD = [ { password: 'pass123',