169 lines
4.2 KiB
JavaScript
169 lines
4.2 KiB
JavaScript
|
var Buffer = require('safe-buffer').Buffer;
|
||
|
var Crypto = require('crypto');
|
||
|
var Auth = exports;
|
||
|
|
||
|
function auth(name, data, options) {
|
||
|
options = options || {};
|
||
|
|
||
|
switch (name) {
|
||
|
case 'mysql_native_password':
|
||
|
return Auth.token(options.password, data.slice(0, 20));
|
||
|
default:
|
||
|
return undefined;
|
||
|
}
|
||
|
}
|
||
|
Auth.auth = auth;
|
||
|
|
||
|
function sha1(msg) {
|
||
|
var hash = Crypto.createHash('sha1');
|
||
|
hash.update(msg, 'binary');
|
||
|
return hash.digest('binary');
|
||
|
}
|
||
|
Auth.sha1 = sha1;
|
||
|
|
||
|
function xor(a, b) {
|
||
|
a = Buffer.from(a, 'binary');
|
||
|
b = Buffer.from(b, 'binary');
|
||
|
var result = Buffer.allocUnsafe(a.length);
|
||
|
for (var i = 0; i < a.length; i++) {
|
||
|
result[i] = (a[i] ^ b[i]);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
Auth.xor = xor;
|
||
|
|
||
|
Auth.token = function(password, scramble) {
|
||
|
if (!password) {
|
||
|
return Buffer.alloc(0);
|
||
|
}
|
||
|
|
||
|
// password must be in binary format, not utf8
|
||
|
var stage1 = sha1((Buffer.from(password, 'utf8')).toString('binary'));
|
||
|
var stage2 = sha1(stage1);
|
||
|
var stage3 = sha1(scramble.toString('binary') + stage2);
|
||
|
return xor(stage3, stage1);
|
||
|
};
|
||
|
|
||
|
// This is a port of sql/password.c:hash_password which needs to be used for
|
||
|
// pre-4.1 passwords.
|
||
|
Auth.hashPassword = function(password) {
|
||
|
var nr = [0x5030, 0x5735];
|
||
|
var add = 7;
|
||
|
var nr2 = [0x1234, 0x5671];
|
||
|
var result = Buffer.alloc(8);
|
||
|
|
||
|
if (typeof password === 'string'){
|
||
|
password = Buffer.from(password);
|
||
|
}
|
||
|
|
||
|
for (var i = 0; i < password.length; i++) {
|
||
|
var c = password[i];
|
||
|
if (c === 32 || c === 9) {
|
||
|
// skip space in password
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// nr^= (((nr & 63)+add)*c)+ (nr << 8);
|
||
|
// nr = xor(nr, add(mul(add(and(nr, 63), add), c), shl(nr, 8)))
|
||
|
nr = this.xor32(nr, this.add32(this.mul32(this.add32(this.and32(nr, [0, 63]), [0, add]), [0, c]), this.shl32(nr, 8)));
|
||
|
|
||
|
// nr2+=(nr2 << 8) ^ nr;
|
||
|
// nr2 = add(nr2, xor(shl(nr2, 8), nr))
|
||
|
nr2 = this.add32(nr2, this.xor32(this.shl32(nr2, 8), nr));
|
||
|
|
||
|
// add+=tmp;
|
||
|
add += c;
|
||
|
}
|
||
|
|
||
|
this.int31Write(result, nr, 0);
|
||
|
this.int31Write(result, nr2, 4);
|
||
|
|
||
|
return result;
|
||
|
};
|
||
|
|
||
|
Auth.randomInit = function(seed1, seed2) {
|
||
|
return {
|
||
|
max_value : 0x3FFFFFFF,
|
||
|
max_value_dbl : 0x3FFFFFFF,
|
||
|
seed1 : seed1 % 0x3FFFFFFF,
|
||
|
seed2 : seed2 % 0x3FFFFFFF
|
||
|
};
|
||
|
};
|
||
|
|
||
|
Auth.myRnd = function(r){
|
||
|
r.seed1 = (r.seed1 * 3 + r.seed2) % r.max_value;
|
||
|
r.seed2 = (r.seed1 + r.seed2 + 33) % r.max_value;
|
||
|
|
||
|
return r.seed1 / r.max_value_dbl;
|
||
|
};
|
||
|
|
||
|
Auth.scramble323 = function(message, password) {
|
||
|
if (!password) {
|
||
|
return Buffer.alloc(0);
|
||
|
}
|
||
|
|
||
|
var to = Buffer.allocUnsafe(8);
|
||
|
var hashPass = this.hashPassword(password);
|
||
|
var hashMessage = this.hashPassword(message.slice(0, 8));
|
||
|
var seed1 = this.int32Read(hashPass, 0) ^ this.int32Read(hashMessage, 0);
|
||
|
var seed2 = this.int32Read(hashPass, 4) ^ this.int32Read(hashMessage, 4);
|
||
|
var r = this.randomInit(seed1, seed2);
|
||
|
|
||
|
for (var i = 0; i < 8; i++){
|
||
|
to[i] = Math.floor(this.myRnd(r) * 31) + 64;
|
||
|
}
|
||
|
var extra = (Math.floor(this.myRnd(r) * 31));
|
||
|
|
||
|
for (var i = 0; i < 8; i++){
|
||
|
to[i] ^= extra;
|
||
|
}
|
||
|
|
||
|
return to;
|
||
|
};
|
||
|
|
||
|
Auth.xor32 = function(a, b){
|
||
|
return [a[0] ^ b[0], a[1] ^ b[1]];
|
||
|
};
|
||
|
|
||
|
Auth.add32 = function(a, b){
|
||
|
var w1 = a[1] + b[1];
|
||
|
var w2 = a[0] + b[0] + ((w1 & 0xFFFF0000) >> 16);
|
||
|
|
||
|
return [w2 & 0xFFFF, w1 & 0xFFFF];
|
||
|
};
|
||
|
|
||
|
Auth.mul32 = function(a, b){
|
||
|
// based on this example of multiplying 32b ints using 16b
|
||
|
// http://www.dsprelated.com/showmessage/89790/1.php
|
||
|
var w1 = a[1] * b[1];
|
||
|
var w2 = (((a[1] * b[1]) >> 16) & 0xFFFF) + ((a[0] * b[1]) & 0xFFFF) + (a[1] * b[0] & 0xFFFF);
|
||
|
|
||
|
return [w2 & 0xFFFF, w1 & 0xFFFF];
|
||
|
};
|
||
|
|
||
|
Auth.and32 = function(a, b){
|
||
|
return [a[0] & b[0], a[1] & b[1]];
|
||
|
};
|
||
|
|
||
|
Auth.shl32 = function(a, b){
|
||
|
// assume b is 16 or less
|
||
|
var w1 = a[1] << b;
|
||
|
var w2 = (a[0] << b) | ((w1 & 0xFFFF0000) >> 16);
|
||
|
|
||
|
return [w2 & 0xFFFF, w1 & 0xFFFF];
|
||
|
};
|
||
|
|
||
|
Auth.int31Write = function(buffer, number, offset) {
|
||
|
buffer[offset] = (number[0] >> 8) & 0x7F;
|
||
|
buffer[offset + 1] = (number[0]) & 0xFF;
|
||
|
buffer[offset + 2] = (number[1] >> 8) & 0xFF;
|
||
|
buffer[offset + 3] = (number[1]) & 0xFF;
|
||
|
};
|
||
|
|
||
|
Auth.int32Read = function(buffer, offset){
|
||
|
return (buffer[offset] << 24)
|
||
|
+ (buffer[offset + 1] << 16)
|
||
|
+ (buffer[offset + 2] << 8)
|
||
|
+ (buffer[offset + 3]);
|
||
|
};
|