Skip to content

Commit 0f5d385

Browse files
committed
Fix: Resolved compatability issue hashing with openssl v3 by replacing native calls with pure JS deps
1 parent 126971e commit 0f5d385

File tree

8 files changed

+76
-73
lines changed

8 files changed

+76
-73
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
node_modules
22
.*.swp
3+
.vscode
4+
yarn.lock

lib/common.js

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
* Copyright (C) 2012 Joshua M. Clulow <[email protected]>
1515
*/
1616

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

1920
function zeroextend(str, len)
2021
{
@@ -46,7 +47,7 @@ function oddpar(buf)
4647
*/
4748
function expandkey(key56)
4849
{
49-
var key64 = new Buffer(8);
50+
var key64 = Buffer.alloc(8);
5051

5152
key64[0] = key56[0] & 0xFE;
5253
key64[1] = ((key56[0] << 7) & 0xFF) | (key56[1] >> 1);
@@ -65,13 +66,41 @@ function expandkey(key56)
6566
*/
6667
function bintohex(bin)
6768
{
68-
var buf = (Buffer.isBuffer(buf) ? buf : new Buffer(bin, 'binary'));
69+
var buf = (Buffer.isBuffer(buf) ? buf : Buffer.from(bin, 'binary'));
6970
var str = buf.toString('hex').toUpperCase();
7071
return zeroextend(str, 32);
7172
}
7273

74+
function calculateDES(key, message) {
75+
var desKey = new Buffer.alloc(8);
76+
desKey[0] = key[0] & 0xFE;
77+
desKey[1] = ((key[0] << 7) & 0xFF) | (key[1] >> 1);
78+
desKey[2] = ((key[1] << 6) & 0xFF) | (key[2] >> 2);
79+
desKey[3] = ((key[2] << 5) & 0xFF) | (key[3] >> 3);
80+
desKey[4] = ((key[3] << 4) & 0xFF) | (key[4] >> 4);
81+
desKey[5] = ((key[4] << 3) & 0xFF) | (key[5] >> 5);
82+
desKey[6] = ((key[5] << 2) & 0xFF) | (key[6] >> 6);
83+
desKey[7] = (key[6] << 1) & 0xFF;
84+
for (var i = 0; i < 8; i++) {
85+
var parity = 0;
86+
for (var j = 1; j < 8; j++) {
87+
parity += (desKey[i] >> j) % 2;
88+
}
89+
desKey[i] |= (parity % 2) === 0 ? 1 : 0;
90+
}
91+
var des = DES.create({ type: 'encrypt', key: desKey});
92+
return Buffer.from(des.update(message));
93+
}
94+
95+
function calculateMD4(message) {
96+
var md4sum = md4.create();
97+
md4sum.update(new Buffer.from(message, 'ucs2'));
98+
return Buffer.from(md4sum.buffer());
99+
}
73100

74101
module.exports.zeroextend = zeroextend;
75102
module.exports.oddpar = oddpar;
76103
module.exports.expandkey = expandkey;
77104
module.exports.bintohex = bintohex;
105+
module.exports.calculateDES = calculateDES;
106+
module.exports.calculateMD4 = calculateMD4;

lib/ntlm.js

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,9 @@
1414
* Copyright (C) 2012 Joshua M. Clulow <[email protected]>
1515
*/
1616

17-
var log = console.log;
18-
var crypto = require('crypto');
1917
var $ = require('./common');
20-
var lmhashbuf = require('./smbhash').lmhashbuf;
21-
var nthashbuf = require('./smbhash').nthashbuf;
22-
18+
var { lmhashbuf, nthashbuf } = require('./smbhash');
19+
var { URL } = require('url');
2320

2421
function encodeType1(hostname, ntdomain) {
2522
hostname = hostname.toUpperCase();
@@ -28,7 +25,7 @@ function encodeType1(hostname, ntdomain) {
2825
var ntdomainlen = Buffer.byteLength(ntdomain, 'ascii');
2926

3027
var pos = 0;
31-
var buf = new Buffer(32 + hostnamelen + ntdomainlen);
28+
var buf = Buffer.alloc(32 + hostnamelen + ntdomainlen);
3229

3330
buf.write('NTLMSSP', pos, 7, 'ascii'); // byte protocol[8];
3431
pos += 7;
@@ -76,7 +73,6 @@ function encodeType1(hostname, ntdomain) {
7673
return buf;
7774
}
7875

79-
8076
/*
8177
*
8278
*/
@@ -102,15 +98,10 @@ function encodeType3(username, hostname, ntdomain, nonce, password) {
10298
hostname = hostname.toUpperCase();
10399
ntdomain = ntdomain.toUpperCase();
104100

105-
var lmh = new Buffer(21);
106-
lmhashbuf(password).copy(lmh);
107-
lmh.fill(0x00, 16); // null pad to 21 bytes
108-
var nth = new Buffer(21);
109-
nthashbuf(password).copy(nth);
110-
nth.fill(0x00, 16); // null pad to 21 bytes
101+
const challenge = new Buffer.from(nonce, 'ascii')
111102

112-
var lmr = makeResponse(lmh, nonce);
113-
var ntr = makeResponse(nth, nonce);
103+
var lmr = makeResponse(lmhashbuf(password), challenge);
104+
var ntr = makeResponse(nthashbuf(password), challenge);
114105

115106
var usernamelen = Buffer.byteLength(username, 'ucs2');
116107
var hostnamelen = Buffer.byteLength(hostname, 'ucs2');
@@ -126,7 +117,7 @@ function encodeType3(username, hostname, ntdomain, nonce, password) {
126117

127118
var pos = 0;
128119
var msg_len = 64 + ntdomainlen + usernamelen + hostnamelen + lmrlen + ntrlen;
129-
var buf = new Buffer(msg_len);
120+
var buf = Buffer.alloc(msg_len);
130121

131122
buf.write('NTLMSSP', pos, 7, 'ascii'); // byte protocol[8];
132123
pos += 7;
@@ -203,16 +194,18 @@ function encodeType3(username, hostname, ntdomain, nonce, password) {
203194
return buf;
204195
}
205196

206-
function makeResponse(hash, nonce)
197+
function makeResponse(lmhash, challenge)
207198
{
208-
var out = new Buffer(24);
209-
for (var i = 0; i < 3; i++) {
210-
var keybuf = $.oddpar($.expandkey(hash.slice(i * 7, i * 7 + 7)));
211-
var des = crypto.createCipheriv('DES-ECB', keybuf, '');
212-
var str = des.update(nonce.toString('binary'), 'binary', 'binary');
213-
out.write(str, i * 8, i * 8 + 8, 'binary');
214-
}
215-
return out;
199+
let buf = new Buffer.alloc(24),
200+
pwBuffer = new Buffer.alloc(21).fill(0);
201+
202+
lmhash.copy(pwBuffer);
203+
204+
$.calculateDES(pwBuffer.slice(0, 7), challenge).copy(buf);
205+
$.calculateDES(pwBuffer.slice(7, 14), challenge).copy(buf, 8);
206+
$.calculateDES(pwBuffer.slice(14), challenge).copy(buf, 16);
207+
208+
return buf;
216209
}
217210

218211
exports.encodeType1 = encodeType1;
@@ -226,9 +219,9 @@ exports.challengeHeader = function (hostname, domain) {
226219
};
227220

228221
exports.responseHeader = function (res, url, domain, username, password) {
229-
var serverNonce = new Buffer((res.headers['www-authenticate'].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1], 'base64');
230-
var hostname = require('url').parse(url).hostname;
231-
return 'NTLM ' + exports.encodeType3(username, hostname, domain, exports.decodeType2(serverNonce), password).toString('base64')
222+
const serverNonce = Buffer.from((res.headers['www-authenticate'].match(/^NTLM\s+(.+?)(,|\s+|$)/) || [])[1], 'base64');
223+
const host = new URL(url).host;
224+
return 'NTLM ' + exports.encodeType3(username, host, domain, exports.decodeType2(serverNonce), password).toString('base64');
232225
};
233226

234227
// Import smbhash module.

lib/smbhash.js

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,53 +14,29 @@
1414
* Copyright (C) 2011-2012 Joshua M. Clulow <[email protected]>
1515
*/
1616

17-
var crypto = require('crypto');
1817
var $ = require('./common');
1918

2019
/*
2120
* Generate the LM Hash
2221
*/
2322
function lmhashbuf(inputstr)
2423
{
25-
/* ASCII --> uppercase */
26-
var x = inputstr.substring(0, 14).toUpperCase();
27-
var xl = Buffer.byteLength(x, 'ascii');
24+
let pwBuffer = new Buffer.alloc(14),
25+
magicKey = new Buffer.from('KGS!@#$%', 'ascii');
2826

29-
/* null pad to 14 bytes */
30-
var y = new Buffer(14);
31-
y.write(x, 0, xl, 'ascii');
32-
y.fill(0, xl);
27+
if (inputstr.length > 14) {
28+
inputstr = inputstr.slice(0, 14);
29+
}
3330

34-
/* insert odd parity bits in key */
35-
var halves = [
36-
$.oddpar($.expandkey(y.slice(0, 7))),
37-
$.oddpar($.expandkey(y.slice(7, 14)))
38-
];
31+
pwBuffer.fill(0);
32+
pwBuffer.write(inputstr.toUpperCase(), 0, 'ascii');
3933

40-
/* DES encrypt magic number "KGS!@#$%" to two
41-
* 8-byte ciphertexts, (ECB, no padding)
42-
*/
43-
var buf = new Buffer(16);
44-
var pos = 0;
45-
var cts = halves.forEach(function(z) {
46-
var des = crypto.createCipheriv('DES-ECB', z, '');
47-
var str = des.update('KGS!@#$%', 'binary', 'binary');
48-
buf.write(str, pos, pos + 8, 'binary');
49-
pos += 8;
50-
});
51-
52-
/* concat the two ciphertexts to form 16byte value,
53-
* the LM hash */
54-
return buf;
34+
return Buffer.from([...$.calculateDES(pwBuffer.slice(0, 7), magicKey), ...$.calculateDES(pwBuffer.slice(7), magicKey)]);
5535
}
5636

57-
function nthashbuf(str)
37+
function nthashbuf(inputstr)
5838
{
59-
/* take MD4 hash of UCS-2 encoded password */
60-
var ucs2 = new Buffer(str, 'ucs2');
61-
var md4 = crypto.createHash('md4');
62-
md4.update(ucs2);
63-
return new Buffer(md4.digest('binary'), 'binary');
39+
return $.calculateMD4(inputstr)
6440
}
6541

6642
function lmhash(is)

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ntlm",
3-
"version": "0.1.3",
3+
"version": "0.2.1",
44
"devDependencies": {
55
"nodeunit": "*"
66
},
@@ -18,5 +18,9 @@
1818
],
1919
"main": "lib/ntlm.js",
2020
"description": "NTLM authentication and Samba LM/NT hash library",
21-
"homepage": "https://github.com/tcr/node-ntlm"
21+
"homepage": "https://github.com/tcr/node-ntlm",
22+
"dependencies": {
23+
"des.js": "^1.1.0",
24+
"js-md4": "^0.3.2"
25+
}
2226
}

tests/common.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717
var $ = require('../lib/common');
1818

1919
var GOOD = [
20-
{ key56: new Buffer([0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x30]),
21-
raw64: new Buffer([0x52, 0xa2, 0x50, 0x6a, 0x24, 0x2a, 0x50, 0x60]),
22-
par64: new Buffer([0x52, 0xa2, 0x51, 0x6b, 0x25, 0x2a, 0x51, 0x61])
20+
{ key56: Buffer.from([0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x30]),
21+
raw64: Buffer.from([0x52, 0xa2, 0x50, 0x6a, 0x24, 0x2a, 0x50, 0x60]),
22+
par64: Buffer.from([0x52, 0xa2, 0x51, 0x6b, 0x25, 0x2a, 0x51, 0x61])
2323
}
2424
];
2525

tests/ntlm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ module.exports.type2_success = function(test) {
4646
test.expect(GOOD.length * 1);
4747
for (var i = 0; i < GOOD.length; i++) {
4848
var g = GOOD[i];
49-
var inbuf = new Buffer(g.messages[1], 'base64');
49+
var inbuf = Buffer.from(g.messages[1], 'base64');
5050
var out = $.decodeType2(inbuf);
5151
test.strictEqual(out.toString('binary'), g.nonce);
5252
}

tests/smbhash.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
* Copyright (C) 2011-2012 Joshua M. Clulow <[email protected]>
1515
*/
1616

17-
var lmhash = require('..').smbhash.lmhash;
18-
var nthash = require('..').smbhash.nthash;
17+
const { lmhash, nthash } = require('../lib/smbhash');
1918

2019
var GOOD = [
2120
{ password: 'pass123',

0 commit comments

Comments
 (0)