strophe.js
Version:
Strophe.js is an XMPP library for JavaScript
88 lines (74 loc) • 2.98 kB
JavaScript
import MD5 from './md5';
import SASLMechanism from './sasl.js';
import SHA1 from './sha1';
import utils from './utils';
export default class SASLSHA1 extends SASLMechanism {
/** PrivateConstructor: SASLSHA1
* SASL SCRAM SHA 1 authentication.
*/
constructor (mechname='SCRAM-SHA-1', isClientFirst=true, priority=60) {
super(mechname, isClientFirst, priority);
}
test (connection) { // eslint-disable-line class-methods-use-this
return connection.authcid !== null;
}
onChallenge (connection, challenge) { // eslint-disable-line class-methods-use-this
let nonce, salt, iter, Hi, U, U_old, i, k;
let responseText = "c=biws,";
let authMessage = `${connection._sasl_data["client-first-message-bare"]},${challenge},`;
const cnonce = connection._sasl_data.cnonce;
const attribMatch = /([a-z]+)=([^,]+)(,|$)/;
while (challenge.match(attribMatch)) {
const matches = challenge.match(attribMatch);
challenge = challenge.replace(matches[0], "");
switch (matches[1]) {
case "r":
nonce = matches[2];
break;
case "s":
salt = matches[2];
break;
case "i":
iter = matches[2];
break;
}
}
if (nonce.substr(0, cnonce.length) !== cnonce) {
connection._sasl_data = {};
return connection._sasl_failure_cb();
}
responseText += "r=" + nonce;
authMessage += responseText;
salt = atob(salt);
salt += "\x00\x00\x00\x01";
const pass = utils.utf16to8(connection.pass);
Hi = U_old = SHA1.core_hmac_sha1(pass, salt);
for (i=1; i<iter; i++) {
U = SHA1.core_hmac_sha1(pass, SHA1.binb2str(U_old));
for (k = 0; k < 5; k++) {
Hi[k] ^= U[k];
}
U_old = U;
}
Hi = SHA1.binb2str(Hi);
const clientKey = SHA1.core_hmac_sha1(Hi, "Client Key");
const serverKey = SHA1.str_hmac_sha1(Hi, "Server Key");
const clientSignature = SHA1.core_hmac_sha1(SHA1.str_sha1(SHA1.binb2str(clientKey)), authMessage);
connection._sasl_data["server-signature"] = SHA1.b64_hmac_sha1(serverKey, authMessage);
for (k = 0; k < 5; k++) {
clientKey[k] ^= clientSignature[k];
}
responseText += ",p=" + btoa(SHA1.binb2str(clientKey));
return responseText;
}
clientChallenge (connection, test_cnonce) { // eslint-disable-line class-methods-use-this
const cnonce = test_cnonce || MD5.hexdigest("" + (Math.random() * 1234567890));
let auth_str = "n=" + utils.utf16to8(connection.authcid);
auth_str += ",r=";
auth_str += cnonce;
connection._sasl_data.cnonce = cnonce;
connection._sasl_data["client-first-message-bare"] = auth_str;
auth_str = "n,," + auth_str;
return auth_str;
}
}