From 7c7b6cd15b9f8f630d0af58d8a073c89bb91b53e Mon Sep 17 00:00:00 2001 From: "Joshua M. Clulow" Date: Sat, 12 Nov 2011 16:16:31 +1100 Subject: [PATCH] initial version --- .gitignore | 2 + README.md | 36 ++++++++++++++++ lib/smbhash.js | 106 +++++++++++++++++++++++++++++++++++++++++++++++ package.json | 23 ++++++++++ run_tests.js | 20 +++++++++ tests/smbhash.js | 51 +++++++++++++++++++++++ 6 files changed, 238 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 lib/smbhash.js create mode 100644 package.json create mode 100755 run_tests.js create mode 100644 tests/smbhash.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6166ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +.*.swp diff --git a/README.md b/README.md new file mode 100644 index 0000000..f109561 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# node-smbhash: Samba LM/NT Hash Library + +## Introduction + +This library converts passwords into the LAN Manager (LM) and +NT Hashes used by SMB/CIFS servers. It was written to populate +the sambaLMPassword and sambaNTPassword values in an LDAP directory +for use with Samba. + +## Installation + + npm install smbhash + +## Usage + +```javascript +var lmhash = require('smbhash').lmhash; +var nthash = require('smbhash').nthash; + +var pass = 'pass123'; +console.log('LM Hash: ' + lmhash(pass)); +console.log('NT Hash: ' + nthash(pass)); +``` + +This produces output: + +``` +LM Hash: 4FB7D301186E0EB3AAD3B435B51404EE +NT Hash: 5FBC3D5FEC8206A30F4B6C473D68AE76 +``` + +## References + + The NTLM Authentication Protocol and Security Support Provider + Copyright (C) 2003, 2006 Eric Glass + http://davenport.sourceforge.net/ntlm.html diff --git a/lib/smbhash.js b/lib/smbhash.js new file mode 100644 index 0000000..475ad35 --- /dev/null +++ b/lib/smbhash.js @@ -0,0 +1,106 @@ +/* + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABigIntegerLITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Copyright (C) 2011 Joshua M. Clulow + */ + +var crypto = require('crypto'); +var BigInteger = require('bigdecimal').BigInteger; + +function zeroExtend(str, len) +{ + while (str.length < len) + str = '0' + str; + return (str); +} + +/* number -> 8bit binary string */ +function bbs(num) +{ + var s = BigInteger.valueOf(num).toString(2); + return (zeroExtend(s, 8)); +} + +/* 7bit bit string -> 8bit odd parity bit string */ +function oddpar(bitstr) +{ + var par = 1; + for (var i = 0; i < bitstr.length; i++) { + if (bitstr[i] === '1') + par = (par + 1) % 2; + } + return (bitstr + String(par)); +} + +/* 64bit bit string -> 8byte binary string */ +function binkey(bitstr) +{ + var out = new Buffer(8); + var bi = new BigInteger(bitstr, 2); + for (var i = 0; i < 8; i++) { + var bi = new BigInteger(bitstr.substr(8 * i, 8), 2); + out[i] = bi.intValue(); + } + return out.toString('binary'); +} + +/* + * Generate the LM Hash + */ +module.exports.lmhash = function lmhash(inputstr) +{ + /* ASCII --> uppercase */ + var x = inputstr.toUpperCase(); + + /* null pad to 14 bytes */ + var y = ""; + for (var i = 0; i < 14; i++) { + if (i < x.length) + y += bbs(x.charCodeAt(i)); + else + y += '00000000'; + } + + /* insert odd parity bits in key */ + var os = ""; + for (var i = 0; i < y.length / 7; i++) { + os += oddpar(y.substr(i * 7, 7)); + } + + /* split into two 8-byte DES keys */ + var halves = [os.substr(0, 64), os.substr(64, 64)]; + + /* DES encrypt magic number "KGS!@#$%" to two + * 8-byte ciphertexts, (ECB, no padding) + */ + var cts = []; + for (var i = 0; i < halves.length; i++) { + var des = crypto.createCipheriv('DES-ECB', + binkey(halves[i]), ''); + cts[i] = des.update('KGS!@#$%', 'binary', 'hex'); + } + + /* concat the two ciphertexts to form 16byte value, + * the LM hash */ + var out = zeroExtend(cts[0], 16) + zeroExtend(cts[1], 16); + return (out.toUpperCase()); +} + +module.exports.nthash = function nthash(str) +{ + /* take MD4 hash of UCS-2 encoded password */ + var ucs2 = new Buffer(str, 'ucs2'); + var md4 = crypto.createHash('md4'); + md4.update(ucs2); + return (zeroExtend(md4.digest('hex'), 32).toUpperCase()); +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..fc53d4f --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "smbhash", + "version": "0.0.1", + "dependencies": { + "bigdecimal": ">= 0.6.0" + }, + "devDependencies": { + "nodeunit": "*" + }, + "author": { + "name": "Joshua M. Clulow", + "email": "josh@sysmgr.org", + "url": "http://blog.sysmgr.org" + }, + "repository": { + "type": "git", + "url": "git://github.com/jclulow/node-smbhash" + }, + "engines": [ "node" ], + "main": "lib/smbhash.js", + "description": "Samba LM/NT Hash Library", + "homepage": "https://github.com/jclulow/node-smbhash" +} diff --git a/run_tests.js b/run_tests.js new file mode 100755 index 0000000..cef70d4 --- /dev/null +++ b/run_tests.js @@ -0,0 +1,20 @@ +#!/usr/bin/env node +/* + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABigIntegerLITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Copyright (C) 2011 Joshua M. Clulow + */ + +var reporter = require('nodeunit').reporters.default; + +reporter.run(['tests']); diff --git a/tests/smbhash.js b/tests/smbhash.js new file mode 100644 index 0000000..9846fe7 --- /dev/null +++ b/tests/smbhash.js @@ -0,0 +1,51 @@ +/* + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABigIntegerLITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Copyright (C) 2011 Joshua M. Clulow + */ + +var lmhash = require('../lib/smbhash').lmhash; +var nthash = require('../lib/smbhash').nthash; + +var GOOD = [ + { password: 'pass123', + lmhash: '4FB7D301186E0EB3AAD3B435B51404EE', + nthash: '5FBC3D5FEC8206A30F4B6C473D68AE76' }, + { password: 'SecREt01', + lmhash: 'FF3750BCC2B22412C2265B23734E0DAC', + nthash: 'CD06CA7C7E10C99B1D33B7485A2ED808' } +]; + +module.exports.nthash_success = function(test) { + test.expect(GOOD.length * 2); + for (var i = 0; i < GOOD.length; i++) { + var g = GOOD[i]; + test.doesNotThrow(function() { + var out = nthash(g.password); + test.strictEqual(out, g.nthash); + }); + } + test.done(); +} + +module.exports.lmhash_success = function(test) { + test.expect(GOOD.length * 2); + for (var i = 0; i < GOOD.length; i++) { + var g = GOOD[i]; + test.doesNotThrow(function() { + var out = lmhash(g.password); + test.strictEqual(out, g.lmhash); + }); + } + test.done(); +}