@dfroehli42/infinispan
Version:
Infinispan Javascript client
117 lines (83 loc) • 3.2 kB
JavaScript
var bitops = require('./bitops');
var utils = require('./utils');
var RESP = {};
var CLIENT_KEY = 'Client Key';
var SERVER_KEY = 'Server Key';
function Mechanism(options) {
options = options || {};
this._genNonce = options.genNonce || utils.genNonce;
this._stage = 'initial';
}
// Conform to the SASL lib's expectations
Mechanism.Mechanism = Mechanism;
Mechanism.prototype.name = 'SCRAM-SHA-1';
Mechanism.prototype.clientFirst = true;
Mechanism.prototype.response = function (cred) {
return RESP[this._stage](this, cred);
};
Mechanism.prototype.challenge = function (chal) {
var values = utils.parse(chal);
this._salt = Buffer.from(values.s, 'base64');
this._iterationCount = parseInt(values.i, 10);
this._nonce = values.r;
this._verifier = values.v;
this._error = values.e;
this._challenge = chal;
return this;
};
RESP.initial = function (mech, cred) {
mech._cnonce = mech._genNonce();
var authzid = '';
if (cred.authzid) {
authzid = 'a=' + utils.saslname(cred.authzid);
}
mech._gs2Header = 'n,' + authzid + ',';
var nonce = 'r=' + mech._cnonce;
var username = 'n=' + utils.saslname(cred.username || '');
mech._clientFirstMessageBare = username + ',' + nonce;
var result = mech._gs2Header + mech._clientFirstMessageBare;
mech._stage = 'challenge';
return result;
};
RESP.challenge = function (mech, cred) {
var gs2Header = Buffer.from(mech._gs2Header).toString('base64');
mech._clientFinalMessageWithoutProof = 'c=' + gs2Header + ',r=' + mech._nonce;
var saltedPassword, clientKey, serverKey;
// If our cached salt is the same, we can reuse cached credentials to speed
// up the hashing process.
if (cred.salt && Buffer.compare(cred.salt, mech._salt) === 0) {
if (cred.clientKey && cred.serverKey) {
clientKey = cred.clientKey;
serverKey = cred.serverKey;
} else if (cred.saltedPassword) {
saltedPassword = cred.saltedPassword;
clientKey = bitops.HMAC(saltedPassword, CLIENT_KEY);
serverKey = bitops.HMAC(saltedPassword, SERVER_KEY);
}
} else {
saltedPassword = bitops.Hi(cred.password || '', mech._salt, mech._iterationCount);
clientKey = bitops.HMAC(saltedPassword, CLIENT_KEY);
serverKey = bitops.HMAC(saltedPassword, SERVER_KEY);
}
var storedKey = bitops.H(clientKey);
var authMessage = mech._clientFirstMessageBare + ',' +
mech._challenge + ',' +
mech._clientFinalMessageWithoutProof;
var clientSignature = bitops.HMAC(storedKey, authMessage);
var clientProof = bitops.XOR(clientKey, clientSignature).toString('base64');
mech._serverSignature = bitops.HMAC(serverKey, authMessage);
var result = mech._clientFinalMessageWithoutProof + ',p=' + clientProof;
mech._stage = 'final';
mech.cache = {
salt: mech._salt,
saltedPassword: saltedPassword,
clientKey: clientKey,
serverKey: serverKey
};
return result;
};
RESP.final = function () {
// TODO: Signal errors
return '';
};
module.exports = Mechanism;