Skip to content

Commit

Permalink
Fix: Resolved compatability issue hashing with openssl v3 by replacin…
Browse files Browse the repository at this point in the history
…g native calls with pure JS deps
  • Loading branch information
iclaxton committed Oct 31, 2023
1 parent 126971e commit 0f5d385
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 73 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
node_modules
.*.swp
.vscode
yarn.lock
35 changes: 32 additions & 3 deletions lib/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
* Copyright (C) 2012 Joshua M. Clulow <[email protected]>
*/

var crypto = require('crypto');
var { DES } = require('des.js');
var md4 = require('js-md4');

function zeroextend(str, len)
{
Expand Down Expand Up @@ -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);
Expand All @@ -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;
49 changes: 21 additions & 28 deletions lib/ntlm.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,9 @@
* Copyright (C) 2012 Joshua M. Clulow <[email protected]>
*/

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();
Expand All @@ -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;
Expand Down Expand Up @@ -76,7 +73,6 @@ function encodeType1(hostname, ntdomain) {
return buf;
}


/*
*
*/
Expand All @@ -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');
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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 host = new URL(url).host;
return 'NTLM ' + exports.encodeType3(username, host, domain, exports.decodeType2(serverNonce), password).toString('base64');
};

// Import smbhash module.
Expand Down
44 changes: 10 additions & 34 deletions lib/smbhash.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,53 +14,29 @@
* Copyright (C) 2011-2012 Joshua M. Clulow <[email protected]>
*/

var crypto = require('crypto');
var $ = require('./common');

/*
* Generate the LM Hash
*/
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)
Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ntlm",
"version": "0.1.3",
"version": "0.2.1",
"devDependencies": {
"nodeunit": "*"
},
Expand All @@ -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"
}
}
6 changes: 3 additions & 3 deletions tests/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -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])
}
];

Expand Down
2 changes: 1 addition & 1 deletion tests/ntlm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
3 changes: 1 addition & 2 deletions tests/smbhash.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
* Copyright (C) 2011-2012 Joshua M. Clulow <[email protected]>
*/

var lmhash = require('..').smbhash.lmhash;
var nthash = require('..').smbhash.nthash;
const { lmhash, nthash } = require('../lib/smbhash');

var GOOD = [
{ password: 'pass123',
Expand Down

0 comments on commit 0f5d385

Please sign in to comment.