113 lines
2.6 KiB
JavaScript
113 lines
2.6 KiB
JavaScript
'use strict';
|
|
|
|
/*
|
|
4.1 authentication: (http://bazaar.launchpad.net/~mysql/mysql-server/5.5/view/head:/sql/password.c)
|
|
|
|
SERVER: public_seed=create_random_string()
|
|
send(public_seed)
|
|
|
|
CLIENT: recv(public_seed)
|
|
hash_stage1=sha1("password")
|
|
hash_stage2=sha1(hash_stage1)
|
|
reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
|
|
|
|
// this three steps are done in scramble()
|
|
|
|
send(reply)
|
|
|
|
|
|
SERVER: recv(reply)
|
|
hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
|
|
candidate_hash2=sha1(hash_stage1)
|
|
check(candidate_hash2==hash_stage2)
|
|
|
|
server stores sha1(sha1(password)) ( hash_stag2)
|
|
*/
|
|
|
|
const crypto = require('crypto');
|
|
|
|
function sha1(msg, msg1, msg2) {
|
|
const hash = crypto.createHash('sha1');
|
|
hash.update(msg);
|
|
if (msg1) {
|
|
hash.update(msg1);
|
|
}
|
|
|
|
if (msg2) {
|
|
hash.update(msg2);
|
|
}
|
|
|
|
return hash.digest();
|
|
}
|
|
|
|
function xor(a, b) {
|
|
if (!Buffer.isBuffer(a)) {
|
|
a = Buffer.from(a, 'binary');
|
|
}
|
|
|
|
if (!Buffer.isBuffer(b)) {
|
|
b = Buffer.from(b, 'binary');
|
|
}
|
|
|
|
const result = Buffer.allocUnsafe(a.length);
|
|
|
|
for (let i = 0; i < a.length; i++) {
|
|
result[i] = a[i] ^ b[i];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
exports.xor = xor;
|
|
|
|
function token(password, scramble1, scramble2) {
|
|
// TODO: use buffers (not sure why strings here)
|
|
if (!password) {
|
|
return Buffer.alloc(0);
|
|
}
|
|
const stage1 = sha1(password);
|
|
return exports.calculateTokenFromPasswordSha(stage1, scramble1, scramble2);
|
|
}
|
|
|
|
exports.calculateTokenFromPasswordSha = function(
|
|
passwordSha,
|
|
scramble1,
|
|
scramble2
|
|
) {
|
|
// we use AUTH 41 here, and we need only the bytes we just need.
|
|
const authPluginData1 = scramble1.slice(0, 8);
|
|
const authPluginData2 = scramble2.slice(0, 12);
|
|
const stage2 = sha1(passwordSha);
|
|
const stage3 = sha1(authPluginData1, authPluginData2, stage2);
|
|
return xor(stage3, passwordSha);
|
|
};
|
|
|
|
exports.calculateToken = token;
|
|
|
|
exports.verifyToken = function(publicSeed1, publicSeed2, token, doubleSha) {
|
|
const hashStage1 = xor(token, sha1(publicSeed1, publicSeed2, doubleSha));
|
|
const candidateHash2 = sha1(hashStage1);
|
|
return candidateHash2.compare(doubleSha) === 0;
|
|
};
|
|
|
|
exports.doubleSha1 = function(password) {
|
|
return sha1(sha1(password));
|
|
};
|
|
|
|
function xorRotating(a, seed) {
|
|
if (!Buffer.isBuffer(a)) {
|
|
a = Buffer.from(a, 'binary');
|
|
}
|
|
|
|
if (!Buffer.isBuffer(seed)) {
|
|
seed = Buffer.from(seed, 'binary');
|
|
}
|
|
|
|
const result = Buffer.allocUnsafe(a.length);
|
|
const seedLen = seed.length;
|
|
|
|
for (let i = 0; i < a.length; i++) {
|
|
result[i] = a[i] ^ seed[i % seedLen];
|
|
}
|
|
return result;
|
|
}
|
|
exports.xorRotating = xorRotating;
|