mirror of
https://github.com/danog/fast-srp.git
synced 2024-11-26 11:54:45 +01:00
Initial import
This commit is contained in:
parent
b97a9dd7b4
commit
f33302df91
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Zarmack Tanen
|
||||
Copyright (c) 2012-2013 Jed Parsons
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
3
index.js
Normal file
3
index.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = require('./lib/srp');
|
||||
|
||||
module.exports.params = require('./lib/params');
|
1542
lib/jsbn.js
Normal file
1542
lib/jsbn.js
Normal file
File diff suppressed because it is too large
Load Diff
182
lib/params.js
Normal file
182
lib/params.js
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
* SRP Group Parameters
|
||||
* http://tools.ietf.org/html/rfc5054#appendix-A
|
||||
*
|
||||
* The 1024-, 1536-, and 2048-bit groups are taken from software
|
||||
* developed by Tom Wu and Eugene Jhong for the Stanford SRP
|
||||
* distribution, and subsequently proven to be prime. The larger primes
|
||||
* are taken from [MODP], but generators have been calculated that are
|
||||
* primitive roots of N, unlike the generators in [MODP].
|
||||
*
|
||||
* The 1024-bit and 1536-bit groups MUST be supported.
|
||||
*/
|
||||
|
||||
// since these are meant to be used internally, all values are numbers. If
|
||||
// you want to add parameter sets, you'll need to convert them to bignums.
|
||||
|
||||
"use strict";
|
||||
|
||||
var BigInteger = require('./jsbn');
|
||||
|
||||
|
||||
function hex(s) {
|
||||
return new BigInteger(s.split(/\s/).join(''), 16);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
1024: {
|
||||
N_length_bits: 1024,
|
||||
N: hex(' EEAF0AB9 ADB38DD6 9C33F80A FA8FC5E8 60726187 75FF3C0B 9EA2314C'
|
||||
+'9C256576 D674DF74 96EA81D3 383B4813 D692C6E0 E0D5D8E2 50B98BE4'
|
||||
+'8E495C1D 6089DAD1 5DC7D7B4 6154D6B6 CE8EF4AD 69B15D49 82559B29'
|
||||
+'7BCF1885 C529F566 660E57EC 68EDBC3C 05726CC0 2FD4CBF4 976EAA9A'
|
||||
+'FD5138FE 8376435B 9FC61D2F C0EB06E3'),
|
||||
g: hex('02'),
|
||||
hash: 'sha1'},
|
||||
|
||||
1536: {
|
||||
N_length_bits: 1536,
|
||||
N: hex(' 9DEF3CAF B939277A B1F12A86 17A47BBB DBA51DF4 99AC4C80 BEEEA961'
|
||||
+'4B19CC4D 5F4F5F55 6E27CBDE 51C6A94B E4607A29 1558903B A0D0F843'
|
||||
+'80B655BB 9A22E8DC DF028A7C EC67F0D0 8134B1C8 B9798914 9B609E0B'
|
||||
+'E3BAB63D 47548381 DBC5B1FC 764E3F4B 53DD9DA1 158BFD3E 2B9C8CF5'
|
||||
+'6EDF0195 39349627 DB2FD53D 24B7C486 65772E43 7D6C7F8C E442734A'
|
||||
+'F7CCB7AE 837C264A E3A9BEB8 7F8A2FE9 B8B5292E 5A021FFF 5E91479E'
|
||||
+'8CE7A28C 2442C6F3 15180F93 499A234D CF76E3FE D135F9BB'),
|
||||
g: hex('02'),
|
||||
hash: 'sha1'},
|
||||
|
||||
2048: {
|
||||
N_length_bits: 2048,
|
||||
N: hex(' AC6BDB41 324A9A9B F166DE5E 1389582F AF72B665 1987EE07 FC319294'
|
||||
+'3DB56050 A37329CB B4A099ED 8193E075 7767A13D D52312AB 4B03310D'
|
||||
+'CD7F48A9 DA04FD50 E8083969 EDB767B0 CF609517 9A163AB3 661A05FB'
|
||||
+'D5FAAAE8 2918A996 2F0B93B8 55F97993 EC975EEA A80D740A DBF4FF74'
|
||||
+'7359D041 D5C33EA7 1D281E44 6B14773B CA97B43A 23FB8016 76BD207A'
|
||||
+'436C6481 F1D2B907 8717461A 5B9D32E6 88F87748 544523B5 24B0D57D'
|
||||
+'5EA77A27 75D2ECFA 032CFBDB F52FB378 61602790 04E57AE6 AF874E73'
|
||||
+'03CE5329 9CCC041C 7BC308D8 2A5698F3 A8D0C382 71AE35F8 E9DBFBB6'
|
||||
+'94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F'
|
||||
+'9E4AFF73'),
|
||||
g: hex('02'),
|
||||
hash: 'sha256'},
|
||||
|
||||
3072: {
|
||||
N_length_bits: 3072,
|
||||
N: hex(' FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08'
|
||||
+'8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B'
|
||||
+'302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9'
|
||||
+'A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6'
|
||||
+'49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8'
|
||||
+'FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D'
|
||||
+'670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C'
|
||||
+'180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718'
|
||||
+'3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D'
|
||||
+'04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D'
|
||||
+'B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226'
|
||||
+'1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C'
|
||||
+'BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC'
|
||||
+'E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF'),
|
||||
g: hex('05'),
|
||||
hash: 'sha256'},
|
||||
|
||||
4096: {
|
||||
N_length_bits: 4096,
|
||||
N: hex(' FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08'
|
||||
+'8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B'
|
||||
+'302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9'
|
||||
+'A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6'
|
||||
+'49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8'
|
||||
+'FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D'
|
||||
+'670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C'
|
||||
+'180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718'
|
||||
+'3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D'
|
||||
+'04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D'
|
||||
+'B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226'
|
||||
+'1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C'
|
||||
+'BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC'
|
||||
+'E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26'
|
||||
+'99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB'
|
||||
+'04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2'
|
||||
+'233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127'
|
||||
+'D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199'
|
||||
+'FFFFFFFF FFFFFFFF'),
|
||||
g: hex('05'),
|
||||
hash: 'sha256'},
|
||||
|
||||
6244: {
|
||||
N_length_bits: 6244,
|
||||
N: hex(' FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08'
|
||||
+'8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B'
|
||||
+'302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9'
|
||||
+'A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6'
|
||||
+'49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8'
|
||||
+'FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D'
|
||||
+'670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C'
|
||||
+'180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718'
|
||||
+'3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D'
|
||||
+'04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D'
|
||||
+'B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226'
|
||||
+'1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C'
|
||||
+'BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC'
|
||||
+'E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26'
|
||||
+'99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB'
|
||||
+'04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2'
|
||||
+'233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127'
|
||||
+'D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492'
|
||||
+'36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406'
|
||||
+'AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918'
|
||||
+'DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151'
|
||||
+'2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03'
|
||||
+'F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F'
|
||||
+'BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA'
|
||||
+'CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B'
|
||||
+'B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632'
|
||||
+'387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E'
|
||||
+'6DCC4024 FFFFFFFF FFFFFFFF'),
|
||||
g: hex('05'),
|
||||
hash: 'sha256'},
|
||||
|
||||
8192: {
|
||||
N_length_bits: 8192,
|
||||
N: hex(' FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08'
|
||||
+'8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B'
|
||||
+'302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9'
|
||||
+'A637ED6B 0BFF5CB6 F406B7ED EE386BFB 5A899FA5 AE9F2411 7C4B1FE6'
|
||||
+'49286651 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8'
|
||||
+'FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D'
|
||||
+'670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C'
|
||||
+'180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718'
|
||||
+'3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D'
|
||||
+'04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D'
|
||||
+'B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226'
|
||||
+'1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C'
|
||||
+'BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC'
|
||||
+'E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26'
|
||||
+'99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB'
|
||||
+'04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2'
|
||||
+'233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127'
|
||||
+'D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492'
|
||||
+'36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406'
|
||||
+'AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918'
|
||||
+'DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151'
|
||||
+'2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03'
|
||||
+'F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F'
|
||||
+'BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA'
|
||||
+'CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B'
|
||||
+'B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632'
|
||||
+'387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E'
|
||||
+'6DBE1159 74A3926F 12FEE5E4 38777CB6 A932DF8C D8BEC4D0 73B931BA'
|
||||
+'3BC832B6 8D9DD300 741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C'
|
||||
+'5AE4F568 3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9'
|
||||
+'22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B 4BCBC886'
|
||||
+'2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A 062B3CF5 B3A278A6'
|
||||
+'6D2A13F8 3F44F82D DF310EE0 74AB6A36 4597E899 A0255DC1 64F31CC5'
|
||||
+'0846851D F9AB4819 5DED7EA1 B1D510BD 7EE74D73 FAF36BC3 1ECFA268'
|
||||
+'359046F4 EB879F92 4009438B 481C6CD7 889A002E D5EE382B C9190DA6'
|
||||
+'FC026E47 9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71'
|
||||
+'60C980DD 98EDD3DF FFFFFFFF FFFFFFFF'),
|
||||
g: hex('13'),
|
||||
hash: 'sha256'}
|
||||
};
|
456
lib/srp.js
Normal file
456
lib/srp.js
Normal file
@ -0,0 +1,456 @@
|
||||
"use strict";
|
||||
|
||||
var crypto = require('crypto');
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
var BigInteger = require('./jsbn');
|
||||
|
||||
var zero = new BigInteger(0);
|
||||
|
||||
function assert_(val, msg) {
|
||||
if (!val)
|
||||
throw new Error(msg||"assertion");
|
||||
}
|
||||
|
||||
/*
|
||||
* If a conversion is explicitly specified with the operator PAD(),
|
||||
* the integer will first be implicitly converted, then the resultant
|
||||
* byte-string will be left-padded with zeros (if necessary) until its
|
||||
* length equals the implicitly-converted length of N.
|
||||
*
|
||||
* params:
|
||||
* n (buffer) Number to pad
|
||||
* len (int) length of the resulting Buffer
|
||||
*
|
||||
* returns: buffer
|
||||
*/
|
||||
function padTo(n, len) {
|
||||
assertIsBuffer(n, "n");
|
||||
var padding = len - n.length;
|
||||
/*
|
||||
console.log("n = " + n.toString('hex'));
|
||||
console.log("n.length = " + n.length);
|
||||
console.log("len = " + len);
|
||||
*/
|
||||
assert_(padding > -1, "Negative padding. Very uncomfortable.");
|
||||
var result = new Buffer(len);
|
||||
result.fill(0, 0, padding);
|
||||
n.copy(result, padding);
|
||||
assert.equal(result.length, len);
|
||||
return result;
|
||||
};
|
||||
|
||||
function padToN(number, params) {
|
||||
assertIsBigInteger(number);
|
||||
//! return padTo(number.toBuffer(), params.N_length_bits/8);
|
||||
var n = number.toString(16).length % 2 != 0 ? "0" + number.toString(16) : number.toString(16);
|
||||
return padTo(new Buffer(n, 'hex'), params.N_length_bits / 8);
|
||||
}
|
||||
|
||||
function padToH(number, params) {
|
||||
assertIsBigInteger(number);
|
||||
var hashlen_bits;
|
||||
if (params.hash === "sha1")
|
||||
hashlen_bits = 160;
|
||||
else if (params.hash === "sha256")
|
||||
hashlen_bits = 256;
|
||||
else if (params.hash === "sha512")
|
||||
hashlen_bits = 512;
|
||||
else
|
||||
throw Error("cannot determine length of hash '"+params.hash+"'");
|
||||
|
||||
//! return padTo(number.toBuffer(), hashlen_bits / 8);
|
||||
return padTo(new Buffer(number.toString(16), 'hex'), hashlen_bits / 8);
|
||||
}
|
||||
|
||||
function assertIsBuffer(arg, argname) {
|
||||
argname = argname || "arg";
|
||||
assert_(Buffer.isBuffer(arg), "Type error: "+argname+" must be a buffer");
|
||||
}
|
||||
|
||||
function assertIsNBuffer(arg, params, argname) {
|
||||
argname = argname || "arg";
|
||||
assert_(Buffer.isBuffer(arg), "Type error: "+argname+" must be a buffer");
|
||||
if (arg.length != params.N_length_bits/8)
|
||||
assert_(false, argname+" was "+arg.length+", expected "+(params.N_length_bits/8));
|
||||
}
|
||||
|
||||
function assertIsBigInteger(arg) {
|
||||
assert_(arg.constructor.name === 'BigInteger', "Type error: " + arg.argname + " must be a BigInteger");
|
||||
}
|
||||
|
||||
/*
|
||||
* compute the intermediate value x as a hash of three buffers:
|
||||
* salt, identity, and password. And a colon. FOUR buffers.
|
||||
*
|
||||
* x = H(s | H(I | ":" | P))
|
||||
*
|
||||
* params:
|
||||
* salt (buffer) salt
|
||||
* I (buffer) user identity
|
||||
* P (buffer) user password
|
||||
*
|
||||
* returns: x (bignum) user secret
|
||||
*/
|
||||
function getx(params, salt, I, P) {
|
||||
assertIsBuffer(salt, "salt (salt)");
|
||||
assertIsBuffer(I, "identity (I)");
|
||||
assertIsBuffer(P, "password (P)");
|
||||
var hashIP = crypto.createHash(params.hash)
|
||||
.update(Buffer.concat([I, new Buffer(':'), P]))
|
||||
.digest();
|
||||
var hashX = crypto.createHash(params.hash)
|
||||
.update(salt)
|
||||
.update(hashIP)
|
||||
.digest();
|
||||
//! return bignum.fromBuffer(hashX);
|
||||
return(new BigInteger(hashX));
|
||||
};
|
||||
|
||||
/*
|
||||
* The verifier is calculated as described in Section 3 of [SRP-RFC].
|
||||
* We give the algorithm here for convenience.
|
||||
*
|
||||
* The verifier (v) is computed based on the salt (s), user name (I),
|
||||
* password (P), and group parameters (N, g).
|
||||
*
|
||||
* x = H(s | H(I | ":" | P))
|
||||
* v = g^x % N
|
||||
*
|
||||
* params:
|
||||
* params (obj) group parameters, with .N, .g, .hash
|
||||
* salt (buffer) salt
|
||||
* I (buffer) user identity
|
||||
* P (buffer) user password
|
||||
*
|
||||
* returns: buffer
|
||||
*/
|
||||
function computeVerifier(params, salt, I, P) {
|
||||
assertIsBuffer(salt, "salt (salt)");
|
||||
assertIsBuffer(I, "identity (I)");
|
||||
assertIsBuffer(P, "password (P)");
|
||||
//* var v_num = params.g.powm(getx(params, salt, I, P), params.N);
|
||||
var v_num = params.g.modPow(getx(params, salt, I, P), params.N);
|
||||
return padToN(v_num, params);
|
||||
};
|
||||
|
||||
/*
|
||||
* calculate the SRP-6 multiplier
|
||||
*
|
||||
* params:
|
||||
* params (obj) group parameters, with .N, .g, .hash
|
||||
*
|
||||
* returns: bignum
|
||||
*/
|
||||
function getk(params) {
|
||||
var k_buf = crypto
|
||||
.createHash(params.hash)
|
||||
.update(padToN(params.N, params))
|
||||
.update(padToN(params.g, params))
|
||||
.digest();
|
||||
return(new BigInteger(k_buf.toString('hex'), 16));
|
||||
};
|
||||
|
||||
/*
|
||||
* Generate a random key
|
||||
*
|
||||
* params:
|
||||
* bytes (int) length of key (default=32)
|
||||
* callback (func) function to call with err,key
|
||||
*
|
||||
* returns: nothing, but runs callback with a Buffer
|
||||
*/
|
||||
function genKey(bytes, callback) {
|
||||
// bytes is optional
|
||||
if (arguments.length < 2) {
|
||||
callback = bytes;
|
||||
bytes = 32;
|
||||
}
|
||||
if (typeof callback !== 'function') {
|
||||
throw("Callback required");
|
||||
}
|
||||
crypto.randomBytes(bytes, function(err, buf) {
|
||||
if (err) return callback (err);
|
||||
return callback(null, buf);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
* The server key exchange message also contains the server's public
|
||||
* value (B). The server calculates this value as B = k*v + g^b % N,
|
||||
* where b is a random number that SHOULD be at least 256 bits in length
|
||||
* and k = H(N | PAD(g)).
|
||||
*
|
||||
* Note: as the tests imply, the entire expression is mod N.
|
||||
*
|
||||
* params:
|
||||
* params (obj) group parameters, with .N, .g, .hash
|
||||
* v (bignum) verifier (stored)
|
||||
* b (bignum) server secret exponent
|
||||
*
|
||||
* returns: B (buffer) the server public message
|
||||
*/
|
||||
function getB(params, k, v, b) {
|
||||
assertIsBigInteger(v);
|
||||
assertIsBigInteger(k);
|
||||
assertIsBigInteger(b);
|
||||
var N = params.N;
|
||||
//* var r = k.mul(v).add(params.g.powm(b, N)).mod(N);
|
||||
var r = k.multiply(v).add(params.g.modPow(b, N)).mod(N);
|
||||
return padToN(r, params);
|
||||
};
|
||||
|
||||
/*
|
||||
* The client key exchange message carries the client's public value
|
||||
* (A). The client calculates this value as A = g^a % N, where a is a
|
||||
* random number that SHOULD be at least 256 bits in length.
|
||||
*
|
||||
* Note: for this implementation, we take that to mean 256/8 bytes.
|
||||
*
|
||||
* params:
|
||||
* params (obj) group parameters, with .N, .g, .hash
|
||||
* a (bignum) client secret exponent
|
||||
*
|
||||
* returns A (bignum) the client public message
|
||||
*/
|
||||
function getA(params, a_num) {
|
||||
assertIsBigInteger(a_num);
|
||||
//! if (Math.ceil(a_num.bitLength() / 8) < 256/8) {
|
||||
if (Math.ceil(a_num.toString(16).length / 2) < 32) {
|
||||
console.warn("getA: client key length", a_num.bitLength(), "is less than the recommended 256 bits");
|
||||
}
|
||||
//* return padToN(params.g.powm(a_num, params.N), params);
|
||||
return padToN(params.g.modPow(a_num, params.N), params);
|
||||
};
|
||||
|
||||
/*
|
||||
* getu() hashes the two public messages together, to obtain a scrambling
|
||||
* parameter "u" which cannot be predicted by either party ahead of time.
|
||||
* This makes it safe to use the message ordering defined in the SRP-6a
|
||||
* paper, in which the server reveals their "B" value before the client
|
||||
* commits to their "A" value.
|
||||
*
|
||||
* params:
|
||||
* params (obj) group parameters, with .N, .g, .hash
|
||||
* A (Buffer) client ephemeral public key
|
||||
* B (Buffer) server ephemeral public key
|
||||
*
|
||||
* returns: u (bignum) shared scrambling parameter
|
||||
*/
|
||||
function getu(params, A, B) {
|
||||
assertIsNBuffer(A, params, "A");
|
||||
assertIsNBuffer(B, params, "B");
|
||||
var u_buf = crypto.createHash(params.hash)
|
||||
.update(A).update(B)
|
||||
.digest();
|
||||
//! return bignum.fromBuffer(u_buf);
|
||||
return(new BigInteger(u_buf.toString('hex'), 16));
|
||||
};
|
||||
|
||||
/*
|
||||
* The TLS premaster secret as calculated by the client
|
||||
*
|
||||
* params:
|
||||
* params (obj) group parameters, with .N, .g, .hash
|
||||
* salt (buffer) salt (read from server)
|
||||
* I (buffer) user identity (read from user)
|
||||
* P (buffer) user password (read from user)
|
||||
* a (bignum) ephemeral private key (generated for session)
|
||||
* B (bignum) server ephemeral public key (read from server)
|
||||
*
|
||||
* returns: buffer
|
||||
*/
|
||||
|
||||
function client_getS(params, k_num, x_num, a_num, B_num, u_num) {
|
||||
assertIsBigInteger(k_num);
|
||||
assertIsBigInteger(x_num);
|
||||
assertIsBigInteger(a_num);
|
||||
assertIsBigInteger(B_num);
|
||||
assertIsBigInteger(u_num);
|
||||
var g = params.g;
|
||||
var N = params.N;
|
||||
// if (zero.greater(B_num) || N.lesser(B_num))
|
||||
if((zero.compareTo(B_num) > 0) && (N.compareTo(B_num) < 0))
|
||||
throw new Error("invalid server-supplied 'B', must be 1..N-1");
|
||||
//* var S_num = B_num.sub(k_num.mul(g.powm(x_num, N))).powm(a_num.add(u_num.mul(x_num)), N).mod(N);
|
||||
var S_num = B_num.subtract(k_num.multiply(g.modPow(x_num, N))).modPow(a_num.add(u_num.multiply(x_num)), N).mod(N);
|
||||
|
||||
return padToN(S_num, params);
|
||||
};
|
||||
|
||||
/*
|
||||
* The TLS premastersecret as calculated by the server
|
||||
*
|
||||
* params:
|
||||
* params (obj) group parameters, with .N, .g, .hash
|
||||
* v (bignum) verifier (stored on server)
|
||||
* A (bignum) ephemeral client public key (read from client)
|
||||
* b (bignum) server ephemeral private key (generated for session)
|
||||
*
|
||||
* returns: bignum
|
||||
*/
|
||||
|
||||
function server_getS(params, v_num, A_num, b_num, u_num) {
|
||||
assertIsBigInteger(v_num);
|
||||
assertIsBigInteger(A_num);
|
||||
assertIsBigInteger(b_num);
|
||||
assertIsBigInteger(u_num);
|
||||
var N = params.N;
|
||||
//! if (zero.greater(A_num) || N.lesser(A_num))
|
||||
if((zero.compareTo(A_num) > 0) && (N.compareTo(A_num) < 0))
|
||||
throw new Error("invalid client-supplied 'A', must be 1..N-1");
|
||||
//* var S_num = A_num.mul(v_num.powm(u_num, N)).powm(b_num, N).mod(N);
|
||||
var S_num = A_num.multiply(v_num.modPow(u_num, N)).modPow(b_num, N).mod(N);
|
||||
return padToN(S_num, params);
|
||||
};
|
||||
|
||||
/*
|
||||
* Compute the shared session key K from S
|
||||
*
|
||||
* params:
|
||||
* params (obj) group parameters, with .N, .g, .hash
|
||||
* S (buffer) Session key
|
||||
*
|
||||
* returns: buffer
|
||||
*/
|
||||
function getK(params, S_buf) {
|
||||
assertIsNBuffer(S_buf, params, "S");
|
||||
return crypto.createHash(params.hash)
|
||||
.update(S_buf)
|
||||
.digest();
|
||||
};
|
||||
|
||||
function getM1(params, A_buf, B_buf, S_buf) {
|
||||
assertIsNBuffer(A_buf, params, "A");
|
||||
assertIsNBuffer(B_buf, params, "B");
|
||||
assertIsNBuffer(S_buf, params, "S");
|
||||
return crypto.createHash(params.hash)
|
||||
.update(A_buf).update(B_buf).update(S_buf)
|
||||
.digest();
|
||||
}
|
||||
|
||||
function getM2(params, A_buf, M_buf, K_buf) {
|
||||
assertIsNBuffer(A_buf, params, "A");
|
||||
assertIsBuffer(M_buf, "M");
|
||||
assertIsBuffer(K_buf, "K");
|
||||
return crypto.createHash(params.hash)
|
||||
.update(A_buf).update(M_buf).update(K_buf)
|
||||
.digest();
|
||||
}
|
||||
|
||||
function equal(buf1, buf2) {
|
||||
// constant-time comparison. A drop in the ocean compared to our
|
||||
// non-constant-time modexp operations, but still good practice.
|
||||
var mismatch = buf1.length - buf2.length;
|
||||
if (mismatch) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < buf1.length; i++) {
|
||||
mismatch |= buf1[i] ^ buf2[i];
|
||||
}
|
||||
return mismatch === 0;
|
||||
}
|
||||
|
||||
function Client(params, salt_buf, identity_buf, password_buf, secret1_buf) {
|
||||
if (!(this instanceof Client)) {
|
||||
return new Client(params, salt_buf, identity_buf, password_buf, secret1_buf);
|
||||
}
|
||||
assertIsBuffer(salt_buf, "salt (salt)");
|
||||
assertIsBuffer(identity_buf, "identity (I)");
|
||||
assertIsBuffer(password_buf, "password (P)");
|
||||
assertIsBuffer(secret1_buf, "secret1");
|
||||
this._private = { params: params,
|
||||
k_num: getk(params),
|
||||
x_num: getx(params, salt_buf, identity_buf, password_buf),
|
||||
//! a_num: bignum.fromBuffer(secret1_buf) };
|
||||
a_num: new BigInteger(secret1_buf.toString('hex'), 16) };
|
||||
this._private.A_buf = getA(params, this._private.a_num);
|
||||
}
|
||||
|
||||
Client.prototype = {
|
||||
computeA: function computeA() {
|
||||
return this._private.A_buf;
|
||||
},
|
||||
setB: function setB(B_buf) {
|
||||
var p = this._private;
|
||||
//! var B_num = bignum.fromBuffer(B_buf);
|
||||
var B_num = new BigInteger(B_buf.toString('hex'), 16);
|
||||
var u_num = getu(p.params, p.A_buf, B_buf);
|
||||
// console.log(util.inspect(p));
|
||||
var S_buf_x = client_getS(p.params, p.k_num, p.x_num, p.a_num, B_num, u_num);
|
||||
p.K_buf = getK(p.params, S_buf_x);
|
||||
p.M1_buf = getM1(p.params, p.A_buf, B_buf, S_buf_x);
|
||||
p.M2_buf = getM2(p.params, p.A_buf, p.M1_buf, p.K_buf);
|
||||
p.u_num = u_num; // only for tests
|
||||
p.S_buf = S_buf_x; // only for tests
|
||||
},
|
||||
computeM1: function computeM1() {
|
||||
if (this._private.M1_buf === undefined)
|
||||
throw new Error("incomplete protocol");
|
||||
return this._private.M1_buf;
|
||||
},
|
||||
checkM2: function checkM2(serverM2_buf) {
|
||||
if (!equal(this._private.M2_buf, serverM2_buf))
|
||||
throw new Error("server is not authentic");
|
||||
},
|
||||
computeK: function computeK() {
|
||||
if (this._private.K_buf === undefined)
|
||||
throw new Error("incomplete protocol");
|
||||
return this._private.K_buf;
|
||||
}
|
||||
};
|
||||
|
||||
function Server(params, verifier_buf, secret2_buf) {
|
||||
if (!(this instanceof Server)) {
|
||||
return new Server(params, verifier_buf, secret2_buf);
|
||||
}
|
||||
assertIsBuffer(verifier_buf, "verifier");
|
||||
assertIsBuffer(secret2_buf, "secret2");
|
||||
this._private = { params: params,
|
||||
k_num: getk(params),
|
||||
//! b_num: bignum.fromBuffer(secret2_buf),
|
||||
//! v_num: bignum.fromBuffer(verifier_buf) };
|
||||
b_num: new BigInteger(secret2_buf.toString('hex'), 16),
|
||||
v_num: new BigInteger(verifier_buf.toString('hex'), 16) };
|
||||
|
||||
this._private.B_buf = getB(params, this._private.k_num,
|
||||
this._private.v_num, this._private.b_num);
|
||||
}
|
||||
|
||||
Server.prototype = {
|
||||
computeB: function computeB() {
|
||||
return this._private.B_buf;
|
||||
},
|
||||
setA: function setA(A_buf) {
|
||||
var p = this._private;
|
||||
//! var A_num = bignum.fromBuffer(A_buf);
|
||||
var A_num = new BigInteger(A_buf.toString('hex'), 16);
|
||||
var u_num = getu(p.params, A_buf, p.B_buf);
|
||||
var S_buf = server_getS(p.params, p.v_num, A_num, p.b_num, u_num);
|
||||
p.K_buf = getK(p.params, S_buf);
|
||||
p.M1_buf = getM1(p.params, A_buf, p.B_buf, S_buf);
|
||||
p.M2_buf = getM2(p.params, A_buf, p.M1_buf, p.K_buf);
|
||||
p.u_num = u_num; // only for tests
|
||||
p.S_buf = S_buf; // only for tests
|
||||
},
|
||||
checkM1: function checkM1(clientM1_buf) {
|
||||
if (this._private.M1_buf === undefined)
|
||||
throw new Error("incomplete protocol");
|
||||
if (!equal(this._private.M1_buf, clientM1_buf))
|
||||
throw new Error("client did not use the same password");
|
||||
return this._private.M2_buf;
|
||||
},
|
||||
computeK: function computeK() {
|
||||
if (this._private.K_buf === undefined)
|
||||
throw new Error("incomplete protocol");
|
||||
return this._private.K_buf;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
params: require('./params'),
|
||||
genKey: genKey,
|
||||
computeVerifier: computeVerifier,
|
||||
Client: Client,
|
||||
Server: Server
|
||||
};
|
22
package.json
Normal file
22
package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "fast-srp",
|
||||
"description": "Secure Remote Password (SRP)",
|
||||
"version": "0.9.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "vows test/test_*.js --spec"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zarmack/fast-srp"
|
||||
},
|
||||
"author": "Zarmack Tanen",
|
||||
"licence": "MIT",
|
||||
"readmeFilename": "README.md",
|
||||
"dependencies": {
|
||||
"lodash": "^3.10.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vows": "0.7.0"
|
||||
}
|
||||
}
|
313
test/test_picl_vectors.js
Normal file
313
test/test_picl_vectors.js
Normal file
@ -0,0 +1,313 @@
|
||||
"use strict";
|
||||
|
||||
var vows = require('vows');
|
||||
var assert = require('assert');
|
||||
var BigInteger = require('../lib/jsbn');
|
||||
var srp = require('../lib/srp');
|
||||
var util = require('util');
|
||||
|
||||
/*
|
||||
* Vectors from https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol
|
||||
*
|
||||
* Verify that we are inter-compatible with the SRP implementation used by
|
||||
* Mozilla's Identity-Attached Services, aka PiCl (Profile in the Cloud).
|
||||
*
|
||||
* Note that P is the HKDF-stretched key, computed elsewhere.
|
||||
*/
|
||||
|
||||
function join(s) {
|
||||
return s.split(/\s/).join('');
|
||||
}
|
||||
|
||||
function h(s) {
|
||||
return new Buffer(join(s), 'hex');
|
||||
}
|
||||
|
||||
var params = srp.params['2048'];
|
||||
|
||||
/* inputs_1/expected_1 are the main PiCl test vectors. They were mechanically
|
||||
* generated to force certain derived values (stretched-password "P", v, A,
|
||||
* B, and S) to begin with a 0x00 byte (to exercise padding bugs).
|
||||
*/
|
||||
|
||||
var inputs_1 = {
|
||||
I: new Buffer('andré@example.org', 'utf8'),
|
||||
P: h('00f9b71800ab5337 d51177d8fbc682a3 653fa6dae5b87628 eeec43a18af59a9d'),
|
||||
salt: h('00f1000000000000000000000000000000000000000000000000000000000179'),
|
||||
// a and b are usually random. For testing, we force them to specific values.
|
||||
a: h(' 00f2000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 000000000000d3d7'
|
||||
),
|
||||
b: h(' 00f3000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 000000000000000f'
|
||||
)
|
||||
};
|
||||
|
||||
var expected_1 = {
|
||||
// 'k' encodes the group (N and g), used in SRP-6a
|
||||
k: h('05b9e8ef059c6b32 ea59fc1d322d37f0 4aa30bae5aa9003b 8321e21ddb04e300'),
|
||||
// 'x' is derived from the salt and password
|
||||
// 'v' is the SRP verifier
|
||||
x: h('b5200337cc3f3f92 6cdddae0b2d31029 c069936a844aff58 779a545be89d0abe'),
|
||||
v: h(' 00173ffa0263e63c cfd6791b8ee2a40f 048ec94cd95aa8a3 125726f9805e0c82'
|
||||
+'83c658dc0b607fbb 25db68e68e93f265 8483049c68af7e82 14c49fde2712a775'
|
||||
+'b63e545160d64b00 189a86708c69657d a7a1678eda0cd79f 86b8560ebdb1ffc2'
|
||||
+'21db360eab901d64 3a75bf1205070a57 91230ae56466b8c3 c1eb656e19b794f1'
|
||||
+'ea0d2a077b3a7553 50208ea0118fec8c 4b2ec344a05c66ae 1449b32609ca7189'
|
||||
+'451c259d65bd15b3 4d8729afdb5faff8 af1f3437bbdc0c3d 0b069a8ab2a959c9'
|
||||
+'0c5a43d42082c774 90f3afcc10ef5648 625c0605cdaace6c 6fdc9e9a7e6635d6'
|
||||
+'19f50af773452247 0502cab26a52a198 f5b00a2798589165 07b0b4e9ef9524d6'),
|
||||
// 'B' is the server's public message
|
||||
B: h(' 0022ce5a7b9d8127 7172caa20b0f1efb 4643b3becc535664 73959b07b790d3c3'
|
||||
+'f08650d5531c19ad 30ebb67bdb481d1d 9cf61bf272f84398 48fdda58a4e6abc5'
|
||||
+'abb2ac496da5098d 5cbf90e29b4b110e 4e2c033c70af7392 5fa37457ee13ea3e'
|
||||
+'8fde4ab516dff1c2 ae8e57a6b264fb9d b637eeeae9b5e43d faba9b329d3b8770'
|
||||
+'ce89888709e02627 0e474eef822436e6 397562f284778673 a1a7bc12b6883d1c'
|
||||
+'21fbc27ffb3dbeb8 5efda279a69a1941 4969113f10451603 065f0a0126666456'
|
||||
+'51dde44a52f4d8de 113e2131321df1bf 4369d2585364f9e5 36c39a4dce33221b'
|
||||
+'e57d50ddccb4384e 3612bbfd03a268a3 6e4f7e01de651401 e108cc247db50392'),
|
||||
// 'A' is the client's public message
|
||||
A: h(' 007da76cb7e77af5 ab61f334dbd5a958 513afcdf0f47ab99 271fc5f7860fe213'
|
||||
+'2e5802ca79d2e5c0 64bb80a38ee08771 c98a937696698d87 8d78571568c98a1c'
|
||||
+'40cc6e7cb101988a 2f9ba3d65679027d 4d9068cb8aad6ebf f0101bab6d52b5fd'
|
||||
+'fa81d2ed48bba119 d4ecdb7f3f478bd2 36d5749f2275e948 4f2d0a9259d05e49'
|
||||
+'d78a23dd26c60bfb a04fd346e5146469 a8c3f010a627be81 c58ded1caaef2363'
|
||||
+'635a45f97ca0d895 cc92ace1d09a99d6 beb6b0dc0829535c 857a419e834db128'
|
||||
+'64cd6ee8a843563b 0240520ff0195735 cd9d316842d5d3f8 ef7209a0bb4b54ad'
|
||||
+'7374d73e79be2c39 75632de562c59647 0bb27bad79c3e2fc ddf194e1666cb9fc'),
|
||||
// 'u' combines the two public messages
|
||||
u: h('b284aa1064e87751 50da6b5e2147b47c a7df505bed94a6f4 bb2ad873332ad732'),
|
||||
// 'S' is the shared secret
|
||||
S: h(' 0092aaf0f527906a a5e8601f5d707907 a03137e1b601e04b 5a1deb02a981f4be'
|
||||
+'037b39829a27dba5 0f1b27545ff2e287 29c2b79dcbdd32c9 d6b20d340affab91'
|
||||
+'a626a8075806c26f e39df91d0ad979f9 b2ee8aad1bc783e7 097407b63bfe58d9'
|
||||
+'118b9b0b2a7c5c4c debaf8e9a460f4bf 6247b0da34b760a5 9fac891757ddedca'
|
||||
+'f08eed823b090586 c63009b2d740cc9f 5397be89a2c32cdc fe6d6251ce11e44e'
|
||||
+'6ecbdd9b6d93f30e 90896d2527564c7e b9ff70aa91acc0ba c1740a11cd184ffb'
|
||||
+'989554ab58117c21 96b353d70c356160 100ef5f4c28d19f6 e59ea2508e8e8aac'
|
||||
+'6001497c27f362ed bafb25e0f045bfdf 9fb02db9c908f103 40a639fe84c31b27'),
|
||||
// 'K' is the shared derived key
|
||||
K: h('e68fd0112bfa31dc ffc8e9c96a1cbadb 4c3145978ff35c73 e5bf8d30bbc7499a'),
|
||||
// 'M1' is the client's proof that it knows the shared key
|
||||
M1: h('27949ec1e0f16256 33436865edb037e2 3eb6bf5cb91873f2 a2729373c2039008')
|
||||
};
|
||||
|
||||
/* inputs_2/expected_2 have leading 0x00 bytes in 'x' and 'u' */
|
||||
var inputs_2 = {
|
||||
I: inputs_1.I, P: inputs_1.P,
|
||||
salt: h('00f1000000000000000000000000000000000000000000000000000000000021'),
|
||||
a: h(' 00f2000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 000000000000000d'
|
||||
),
|
||||
b: h(' 00f3000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000001'
|
||||
)
|
||||
};
|
||||
var expected_2 = {
|
||||
k: expected_1.k,
|
||||
x: h('009b2740fb49284d 69cab7c916d449ee d7dcabf41332b8b8 d6928f529bd1a94e'),
|
||||
v: h(' 1cd8b856685672ee 7a5895d897121234 6c17c3472f2696e4 8cdeec5533c06693'
|
||||
+'179bc24802b762bc c1e1f8fc8abe607a f2f44aac9172e7dd 0c0110e45cf3b700'
|
||||
+'f8db153b67fb0e76 3c6710b8c1c26baf c3b67a50652ee0d7 c6045a5c4b51ff33'
|
||||
+'d0135065dca5d6bb 7e150e07414bd572 a954471059c1b466 d0530b0a80bd2d0c'
|
||||
+'f1bedf5abfc05c3c f2736ac40b083dcf 62271e834042ecb0 d4882ddd35403c1e'
|
||||
+'d24bc4ffe274c5f6 be50ec9b85aa0cfa 26d97e086ec45e06 3c29174d3dbe5490'
|
||||
+'1d2a557b7eb46b18 9e17cc721fc098a0 baee2f364a2b409d 49d9372a9625db11'
|
||||
+'acfd74ba7f41285f 9c1916d3caaf5238 852694bbde2a13f7 8fcc92d16658dd04'),
|
||||
// 'B' is the server's public message
|
||||
B: h(' 485d56912c60d9c1 7af15494d4d50006 45eefa2d41f6bcb5 785e08efad0833a1'
|
||||
+'3cb43ee3869e78d4 c2006f42b9741782 a85c90a110cc9a74 4fc2a361d5535966'
|
||||
+'2dc5fa4a8d0c7c0e 63e0cf32a28af655 863dd5d66f550557 eacd3e3e64d90f9f'
|
||||
+'0d757403c9bbfb08 fcc9a35e1cb421d7 3bb93fa72d5b54ed bfa219d3867255ba'
|
||||
+'f96223eef038f085 722b2d14457a5a13 1857a56e66d3011b b5aa7504c4b9a346'
|
||||
+'8d0ebdd817d20105 be06ba261ea16740 723faa097f27ddc2 efe34cf8fe59451a'
|
||||
+'5bb3987d7161085f b8fc28d5cc28c466 6a3ca486ad0ca83d 1984248ac838574e'
|
||||
+'348fb9745ffd1163 f53b5566768a8971 237065d8f6e786be e15107125fb10df1'),
|
||||
// 'A' is the client's public message
|
||||
A: h(' a4b17836b1e7d6f1 5b9901f644bcdf5e 119e7a861c6ee88d 006d8420a5066f22'
|
||||
+'d9bf5ccf3d380437 0d29d778ec40afcf c88de7bf22ec03fc 6ab12e0dd95d15e3'
|
||||
+'a6249c94393435b0 0d23b1b0439dabed cce1726b2b3cdea2 647c8790d604d87d'
|
||||
+'2ac890cfceec0dbe 434f09a9bc11d984 a1e1990f69956ae0 db6068992ad1715f'
|
||||
+'b4381516da83637a 73de4211908c8f2f f8b3a09e8535acf3 c2b8de4e9a632f89'
|
||||
+'9bfa08cee543b4ea 50d0aca0b3e4fbfa e49ffa2a1ab89a42 8bea928868828501'
|
||||
+'2e8af13fcdd444ad da9ad1d0ab4c2069 91919e5391bd2b1a ab8c2d006baceaf8'
|
||||
+'cdcb555a6b16e844 5b03e09776eba841 7576dac458afbbd5 2902dfb0282bed79'),
|
||||
// 'u' combines the two public messages
|
||||
u: h('000e4039be3989ad 088dc17d8ade899a 6409e7e57b3e8518 cee1cbc77e1de243'),
|
||||
// 'S' is the shared secret
|
||||
S: h(' 5c7f591d134d19f9 fcedc2b4e3eecd3d 5deadfe7dd42bd59 b1c960516c65ab61'
|
||||
+'d007f8134e0a7ca3 0dd409128ef2c780 6784afd95985c8f3 c2d42cd73d26d315'
|
||||
+'541645d28aefabc9 980c9a6e5714b178 aa69e5321828ca00 f3d10d742776cfe4'
|
||||
+'4b7f5f5c0247addc 0ab0640b49b540ff 9bccea8702e1f996 49448680c00fb484'
|
||||
+'51919224d44236ba 1b1e5cf62a5946bd 637f189ff7b8eba9 7b719f18ad9251f0'
|
||||
+'a81c157604065388 d7bf4abbf774bfb2 d7b95ed8359b0d70 6ff5df0223992c81'
|
||||
+'4aac506e1bace002 d134ed5e41d74f93 a8f410dfe7dc5954 f70b6bafcd0ddfde'
|
||||
+'e75f0058f718ec14 f9bbeb29ff966e00 ddfdd2d38a1c7a68 ac455a57b972d528'),
|
||||
// 'K' is the shared derived key
|
||||
K: h('b637ede0b7a31c46 b2567e855eb8a7f7 a994937deee76479 62afbe35d6929709'),
|
||||
// 'M1' is the client's proof that it knows the shared key
|
||||
M1: h('67c83797eb1a3987 e2d48d287e3bd772 d25db2b3cd86ea22 c8cf3ae932a1e45b')
|
||||
};
|
||||
|
||||
/* inputs_3/expected_3 have leading 0x00 bytes in 'x' and 'K' */
|
||||
var inputs_3 = {
|
||||
I: inputs_2.I, P: inputs_2.P,
|
||||
salt: h('00f1000000000000000000000000000000000000000000000000000000000021'),
|
||||
a: h(' 00f2000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 00000000000001a0'
|
||||
),
|
||||
b: inputs_2.b
|
||||
};
|
||||
var expected_3 = {
|
||||
k: expected_2.k,
|
||||
x: expected_2.x,
|
||||
v: expected_2.v,
|
||||
B: expected_2.B,
|
||||
A: h(' 87b6da9e4162843b 4d5ee60c403ae3e1 e9fdab64883f13ab 4a44b0718a9ea1b6'
|
||||
+'1ad17c675e0f0395 b37d58a046a2d5ab 1fb665a9777abe80 8077ccf6fd8ec583'
|
||||
+'854eab98deb257d9 10e5bf5cafed4955 2a5cd9927c0979f7 5a21654644000173'
|
||||
+'aef6f2244296439c 10b3c61a03e7146e f6c9c9564b1d2bf5 1ece84d115965f9c'
|
||||
+'c82006bdb7a124da 3304bcc24c8f3724 522b748fb19a0cb6 b60e355acbf649b5'
|
||||
+'40b4972e24077c29 32004a3ad9e59464 2e90a3bfc8de7085 f4a4efc195bd06c9'
|
||||
+'6c7011f3c979eaab 469f06465a5b7239 afaee535aedc5bd2 1a220546e0e6b70b'
|
||||
+'5b6f54db3fea46d5 7ebc7fe46156d793 c59e6290d3cf9bc2 4316528da34f4640'),
|
||||
u: h('865d0efca6cf17d6 f489e129231f1a48 b20c83ec6581d11f 3a2fa48ea93cd305'),
|
||||
S: h(' 0ae26456e1a0dec1 ce162fb2e5bc7300 3c285e17c0b44f03 7ebbc57f8020ceae'
|
||||
+'5d10a9e6e44eab2a 6915b582ab5f6e7d 16002ce05e524015 e9bc7c56d5131da4'
|
||||
+'d4c4d7c3debaffcd b60e58468bd2c0da 5de95855480190a3 5258c79032001882'
|
||||
+'3d836ca91848c5b6 3ca4265c3329eb44 161af9ce64cf4468 ef0eb88a788a0d07'
|
||||
+'52a69821278c94ae 7193161b5c638b55 bf732e2a5996ccc5 16335f9f3d00dfa9'
|
||||
+'8ac1b1e4971c5417 d34eba1e2a90ed60 a07d1d8be5b9d773 d8f2cb03bfb75994'
|
||||
+'249f7734081aa42d 58dd54f8f725b245 175cf7d102e1086c eba4cfe7e49a2d27'
|
||||
+'ffd6aef7549d402f bfcea78b4f3398ac 9ab1ee199f70acb6 4d2a17e159ff500d'),
|
||||
K: h('00217598a4008956 4b17196bd43422d6 03a0a88a545b61b3 98c42c9cbcc1d1b3'),
|
||||
M1: h('96d815ecece1dff4 254cd77517b37b97 65e741c1a57169ab af538e867444ec7f')
|
||||
};
|
||||
|
||||
/* inputs_4/expected_4 have leading 0x00 bytes in 'x' and 'M1' */
|
||||
var inputs_4 = {
|
||||
I: inputs_2.I, P: inputs_2.P,
|
||||
salt: h('00f1000000000000000000000000000000000000000000000000000000000021'),
|
||||
a: h(' 00f2000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000000'
|
||||
+'0000000000000000 0000000000000000 0000000000000000 0000000000000190'
|
||||
),
|
||||
b: inputs_2.b
|
||||
};
|
||||
var expected_4 = {
|
||||
k: expected_2.k,
|
||||
x: expected_2.x,
|
||||
v: expected_2.v,
|
||||
B: expected_2.B,
|
||||
A: h(' 4aee66beefb92d12 c8e341814809afcd 9ce083c11abcda70 0c03d5379c429cb9'
|
||||
+'acbde6bb42a628f3 7a2536c864c40f74 f48a9d9356029a8b fe0e10cb9cf5a8a4'
|
||||
+'2e591841f426d281 edf7c9b04112d8ef bf73f9768a4faace ddd351d3e9380bf1'
|
||||
+'dcd0590c7ab50a95 bd23e9617e303bea 6f8fbe8a657b6417 4b60cdf5c059ba67'
|
||||
+'1b6735324ae0c30a e7f3e361de8f273c af7b2513fa048ed1 0106c66ce460c5cc'
|
||||
+'78544c790f5ffcce 378b79d5f02ec361 3a457b03fa0cc39c 80d6fdd645e24f65'
|
||||
+'c690f9478d5b331d c00eef68670edbf3 629fd1a6c85267d2 cbb90f1670e7ba09'
|
||||
+'cf2b5a9b00be8e11 f33e47a1c1f04eca f35bccb61af1116e 4d0f9d475017bad2'),
|
||||
u: h('d0913eb75b61e15a 87756ffa04d4f967 e492bd0b330a2b11 fe8976aada2bb1ee'),
|
||||
S: h(' 7ba3ce4a3d236b95 3c2d0fee42195c85 081664a44f55b82d a3abf66ac68bdbd7'
|
||||
+'ad82d5ad95090782 5241fb706de8fc58 0a29e4579fbbedf3 0bec0138b3f76e06'
|
||||
+'f9c86b16ad673890 3003ce8c86cb14ea 552db904a20970a9 7d9258a768087d30'
|
||||
+'47a6e77520d32968 de3f64e94cd8c463 92c13e194194745c 8e53a9bb15a79473'
|
||||
+'2a645068970fcdd9 a7c98b4aec19773a 5196802c2e932e71 d3a4a340e6f4fe16'
|
||||
+'9e7ccc687f7246fe 20edeaf88d1125da c812751317f7213c d84f9efe2313d701'
|
||||
+'d4a9bf0242bfe703 26fc19b68c90e83b 59b5cc21886ab602 f8bfa16fb50c3147'
|
||||
+'9aad5e31698abf67 863b7ca6b6ac25a7 09a24d8f94c80bbf 691e38c81beb3c72'),
|
||||
K: h('bd2a167a93b8496e 68c7e24b37956924 672eb8249d25c281 13984912d5cf27a6'),
|
||||
M1: h('00cef66a047d506c bf941c236218e583 5343534ae08cf0cd 0fb7980bed242e05')
|
||||
};
|
||||
|
||||
|
||||
function hexequal(a, b, msg) {
|
||||
assert.equal(a.length, b.length, msg);
|
||||
assert.equal(a.toString('hex'), b.toString('hex'), msg);
|
||||
}
|
||||
|
||||
function numequal(a, b, msg) {
|
||||
assert(a.compareTo(b) == 0, msg);
|
||||
}
|
||||
|
||||
function checkVectors(params, inputs, expected) {
|
||||
hexequal(inputs.I, new Buffer('616e6472c3a9406578616d706c652e6f7267', "hex"), "I");
|
||||
hexequal(srp.computeVerifier(params, inputs.salt, inputs.I, inputs.P), expected.v, "v");
|
||||
|
||||
var client = new srp.Client(params, inputs.salt, inputs.I, inputs.P, inputs.a);
|
||||
var server = new srp.Server(params, expected.v, inputs.b);
|
||||
|
||||
//! numequal(client._private.k_num, bignum.fromBuffer(expected.k), "k");
|
||||
//! numequal(client._private.x_num, bignum.fromBuffer(expected.x), "x");
|
||||
// console.log("k_num = " + client._private.k_num.toString(16) + ", expected k = " + bigInt(expected.k.toString('hex'), 16).toString(16));
|
||||
numequal(client._private.k_num, new BigInteger(expected.k.toString('hex'), 16), "k");
|
||||
numequal(client._private.x_num, new BigInteger(expected.x.toString('hex'), 16), "x");
|
||||
hexequal(client.computeA(), expected.A);
|
||||
hexequal(server.computeB(), expected.B);
|
||||
|
||||
assert.throws(function() {client.computeM1();}, /incomplete protocol/);
|
||||
assert.throws(function() {client.computeK();}, /incomplete protocol/);
|
||||
assert.throws(function() {server.checkM1(expected.M1);}, /incomplete protocol/);
|
||||
assert.throws(function() {server.computeK();}, /incomplete protocol/);
|
||||
|
||||
client.setB(expected.B);
|
||||
|
||||
numequal(client._private.u_num, new BigInteger(expected.u.toString('hex'), 16));
|
||||
hexequal(client._private.S_buf, expected.S);
|
||||
hexequal(client.computeM1(), expected.M1);
|
||||
hexequal(client.computeK(), expected.K);
|
||||
|
||||
server.setA(expected.A);
|
||||
numequal(server._private.u_num, new BigInteger(expected.u.toString('hex'), 16));
|
||||
hexequal(server._private.S_buf, expected.S);
|
||||
assert.throws(function() {server.checkM1(Buffer("notM1"));},
|
||||
/client did not use the same password/);
|
||||
server.checkM1(expected.M1); // happy, not throwy
|
||||
hexequal(server.computeK(), expected.K);
|
||||
}
|
||||
|
||||
vows.describe('picl vectors')
|
||||
|
||||
.addBatch({
|
||||
'vectors 1': function() { checkVectors(params, inputs_1, expected_1); },
|
||||
'vectors 2': function() { checkVectors(params, inputs_2, expected_2); },
|
||||
'vectors 3': function() { checkVectors(params, inputs_3, expected_3); },
|
||||
'vectors 4': function() { checkVectors(params, inputs_4, expected_4); }
|
||||
})
|
||||
|
||||
.export(module);
|
98
test/test_rfc_5054.js
Normal file
98
test/test_rfc_5054.js
Normal file
@ -0,0 +1,98 @@
|
||||
const vows = require('vows'),
|
||||
assert = require('assert'),
|
||||
srp = require('../lib/srp'),
|
||||
params = srp.params['1024'];
|
||||
|
||||
/*
|
||||
* http://tools.ietf.org/html/rfc5054#appendix-B
|
||||
*/
|
||||
|
||||
const I = new Buffer("alice"),
|
||||
P = new Buffer("password123"),
|
||||
s = Buffer('beb25379d1a8581eb5a727673a2441ee', 'hex'),
|
||||
k_expected = '7556aa045aef2cdd07abaf0f665c3e818913186f',
|
||||
x_expected = '94b7555aabe9127cc58ccf4993db6cf84d16c124',
|
||||
v_expected = ('7e273de8 696ffc4f 4e337d05 b4b375be b0dde156 9e8fa00a 9886d812'
|
||||
+'9bada1f1 822223ca 1a605b53 0e379ba4 729fdc59 f105b478 7e5186f5'
|
||||
+'c671085a 1447b52a 48cf1970 b4fb6f84 00bbf4ce bfbb1681 52e08ab5'
|
||||
+'ea53d15c 1aff87b2 b9da6e04 e058ad51 cc72bfc9 033b564e 26480d78'
|
||||
+'e955a5e2 9e7ab245 db2be315 e2099afb').split(/\s/).join(''),
|
||||
|
||||
a = Buffer('60975527035cf2ad1989806f0407210bc81edc04e2762a56afd529ddda2d4393', 'hex'),
|
||||
b = Buffer('e487cb59d31ac550471e81f00f6928e01dda08e974a004f49e61f5d105284d20', 'hex'),
|
||||
A_expected = ('61d5e490 f6f1b795 47b0704c 436f523d d0e560f0 c64115bb 72557ec4'
|
||||
+'4352e890 3211c046 92272d8b 2d1a5358 a2cf1b6e 0bfcf99f 921530ec'
|
||||
+'8e393561 79eae45e 42ba92ae aced8251 71e1e8b9 af6d9c03 e1327f44'
|
||||
+'be087ef0 6530e69f 66615261 eef54073 ca11cf58 58f0edfd fe15efea'
|
||||
+'b349ef5d 76988a36 72fac47b 0769447b').split(/\s/).join(''),
|
||||
B_expected = ('bd0c6151 2c692c0c b6d041fa 01bb152d 4916a1e7 7af46ae1 05393011'
|
||||
+'baf38964 dc46a067 0dd125b9 5a981652 236f99d9 b681cbf8 7837ec99'
|
||||
+'6c6da044 53728610 d0c6ddb5 8b318885 d7d82c7f 8deb75ce 7bd4fbaa'
|
||||
+'37089e6f 9c6059f3 88838e7a 00030b33 1eb76840 910440b1 b27aaeae'
|
||||
+'eb4012b7 d7665238 a8e3fb00 4b117b58').split(/\s/).join(''),
|
||||
|
||||
u_expected = 'ce38b9593487da98554ed47d70a7ae5f462ef019',
|
||||
S_expected = ('b0dc82ba bcf30674 ae450c02 87745e79 90a3381f 63b387aa f271a10d'
|
||||
+'233861e3 59b48220 f7c4693c 9ae12b0a 6f67809f 0876e2d0 13800d6c'
|
||||
+'41bb59b6 d5979b5c 00a172b4 a2a5903a 0bdcaf8a 709585eb 2afafa8f'
|
||||
+'3499b200 210dcc1f 10eb3394 3cd67fc8 8a2f39a4 be5bec4e c0a3212d'
|
||||
+'c346d7e4 74b29ede 8a469ffe ca686e5a').split(/\s/).join('');
|
||||
|
||||
function asHex(num) {
|
||||
//! return num.toBuffer().toString('hex');
|
||||
return num.toString(16);
|
||||
}
|
||||
|
||||
vows.describe('RFC 5054')
|
||||
|
||||
.addBatch({
|
||||
"Test vectors": {
|
||||
topic: function() {
|
||||
return srp.computeVerifier(params, s, I, P);
|
||||
},
|
||||
|
||||
"x": function() {
|
||||
var client = new srp.Client(params, s, I, P, a);
|
||||
assert.equal(asHex(client._private.x_num), x_expected);
|
||||
},
|
||||
|
||||
"V": function(v) {
|
||||
assert.equal(v.toString('hex'), v_expected);
|
||||
},
|
||||
|
||||
"k": function() {
|
||||
var client = new srp.Client(params, s, I, P, a);
|
||||
assert.equal(asHex(client._private.k_num), k_expected);
|
||||
},
|
||||
|
||||
"A": function() {
|
||||
var client = new srp.Client(params, s, I, P, a);
|
||||
assert.equal(client.computeA().toString('hex'), A_expected);
|
||||
},
|
||||
|
||||
"B": function(v) {
|
||||
var server = new srp.Server(params, v, b);
|
||||
assert.equal(server.computeB().toString('hex'), B_expected);
|
||||
},
|
||||
|
||||
"u": function() {
|
||||
var client = new srp.Client(params, s, I, P, a);
|
||||
client.setB(Buffer(B_expected, 'hex'));
|
||||
assert.equal(asHex(client._private.u_num), u_expected);
|
||||
},
|
||||
|
||||
"S client": function() {
|
||||
var client = new srp.Client(params, s, I, P, a);
|
||||
client.setB(Buffer(B_expected, 'hex'));
|
||||
assert.equal(client._private.S_buf.toString('hex'), S_expected);
|
||||
},
|
||||
|
||||
"S server": function(v) {
|
||||
var server = new srp.Server(params, v, b);
|
||||
server.setA(Buffer(A_expected, 'hex'));
|
||||
assert.equal(server._private.S_buf.toString('hex'), S_expected);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
.export(module);
|
194
test/test_srp.js
Normal file
194
test/test_srp.js
Normal file
@ -0,0 +1,194 @@
|
||||
const vows = require('vows'),
|
||||
assert = require('assert'),
|
||||
srp = require('../lib/srp'),
|
||||
params = srp.params[4096],
|
||||
|
||||
salt = new Buffer("salty"),
|
||||
identity = new Buffer("alice"),
|
||||
password = new Buffer("password123");
|
||||
|
||||
assert(params, "missing parameters");
|
||||
|
||||
var client, server;
|
||||
var a, A;
|
||||
var b, B;
|
||||
var verifier;
|
||||
var S_client, S_server;
|
||||
|
||||
vows.describe("srp.js")
|
||||
|
||||
.addBatch({
|
||||
"create Verifier": function() {
|
||||
verifier = srp.computeVerifier(params, salt, identity, password);
|
||||
assert.equal(verifier.toString("hex"), "f0e47f50f5dead8db8d93a279e3b62d6ff50854b31fbd3474a886bef916261717e84dd4fb8b4d27feaa5146db7b1cbbc274fdf96a132b5029c2cd72527427a9b9809d5a4d018252928b4fc343bc17ce63c1859d5806f5466014fc361002d8890aeb4d6316ff37331fc2761be0144c91cdd8e00ed0138c0ce51534d1b9a9ba629d7be34d2742dd4097daabc9ecb7aaad89e53c342b038f1d2adae1f2410b7884a3e9a124c357e421bccd4524467e1922660e0a4460c5f7c38c0877b65f6e32f28296282a93fc11bbabb7bb69bf1b3f9391991d8a86dd05e15000b7e38ba38a536bb0bf59c808ec25e791b8944719488b8087df8bfd7ff20822997a53f6c86f3d45d004476d6303301376bb25a9f94b552cce5ed40de5dd7da8027d754fa5f66738c7e3fc4ef3e20d625df62cbe6e7adfc21e47880d8a6ada37e60370fd4d8fc82672a90c29f2e72f35652649d68348de6f36d0e435c8bd42dd00155d35d501becc0661b43e04cdb2da84ce92b8bf49935d73d75efcbd1176d7bbccc3cc4d4b5fefcc02d478614ee1681d2ff3c711a61a7686eb852ae06fb8227be21fb8802719b1271ba1c02b13bbf0a2c2e459d9bedcc8d1269f6a785cb4563aa791b38fb038269f63f58f47e9051499549789269cc7b8ec7026fc34ba73289c4af829d5a532e723967ce9b6c023ef0fd0cfe37f51f10f19463b6534159a09ddd2f51f3b30033");
|
||||
},
|
||||
|
||||
"create a and b": {
|
||||
topic: function() {
|
||||
var cb = this.callback;
|
||||
srp.genKey(64, function(err, key) {
|
||||
assert(err === null);
|
||||
a = key;
|
||||
srp.genKey(32, function(err, key) {
|
||||
assert(err === null);
|
||||
b = key;
|
||||
cb();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
"use a and b": function() {
|
||||
client = new srp.Client(params, salt, identity, password, a);
|
||||
|
||||
// client produces A
|
||||
A = client.computeA();
|
||||
|
||||
// create server
|
||||
server = new srp.Server(params, verifier, b);
|
||||
|
||||
// server produces B
|
||||
B = server.computeB();
|
||||
|
||||
// server accepts A
|
||||
server.setA(A);
|
||||
|
||||
// client doesn't produce M1 too early
|
||||
assert.throws(function(){client.computeM1();}, /incomplete protocol/);
|
||||
|
||||
// client accepts B
|
||||
client.setB(B);
|
||||
|
||||
// client produces M1 now
|
||||
client.computeM1();
|
||||
|
||||
// server likes client's M1
|
||||
var serverM2 = server.checkM1(client.computeM1());
|
||||
|
||||
// client and server agree on K
|
||||
var client_K = client.computeK();
|
||||
var server_K = server.computeK();
|
||||
assert.equal(client_K.toString("hex"), server_K.toString("hex"));
|
||||
|
||||
// server is authentic
|
||||
assert.doesNotThrow(function(){client.checkM2(serverM2);}, "M2 didn't check");
|
||||
},
|
||||
|
||||
"constructor doesn't require 'new'": function() {
|
||||
client = srp.Client(params, salt, identity, password, a);
|
||||
|
||||
// client produces A
|
||||
A = client.computeA();
|
||||
|
||||
// create server
|
||||
server = srp.Server(params, verifier, b);
|
||||
|
||||
// server produces B
|
||||
B = server.computeB();
|
||||
|
||||
// server accepts A
|
||||
server.setA(A);
|
||||
|
||||
// client doesn't produce M1 too early
|
||||
assert.throws(function(){client.computeM1();}, /incomplete protocol/);
|
||||
|
||||
// client accepts B
|
||||
client.setB(B);
|
||||
|
||||
// client produces M1 now
|
||||
client.computeM1();
|
||||
|
||||
// server likes client's M1
|
||||
serverM2 = server.checkM1(client.computeM1());
|
||||
|
||||
// client and server agree on K
|
||||
var client_K = client.computeK();
|
||||
var server_K = server.computeK();
|
||||
assert.equal(client_K.toString("hex"), server_K.toString("hex"));
|
||||
|
||||
// server is authentic
|
||||
assert.doesNotThrow(function(){client.checkM2(serverM2);}, "M2 didn't check");
|
||||
},
|
||||
|
||||
"server rejects wrong M1": function() {
|
||||
var bad_client = new srp.Client(params, salt, identity, Buffer("bad"), a);
|
||||
var server2 = new srp.Server(params, verifier, b);
|
||||
bad_client.setB(server2.computeB());
|
||||
assert.throws(function(){server.checkM1(bad_client.computeM1());},
|
||||
/client did not use the same password/);
|
||||
},
|
||||
|
||||
"server rejects bad A": function() {
|
||||
// client's "A" must be 1..N-1 . Reject 0 and N and N+1. We should
|
||||
// reject 2*N too, but our Buffer-length checks reject it before the
|
||||
// number itself is examined.
|
||||
|
||||
var server2 = new srp.Server(params, verifier, b);
|
||||
var Azero = new Buffer(params.N_length_bits/8);
|
||||
Azero.fill(0);
|
||||
//! var AN = params.N.toBuffer();
|
||||
//! var AN1 = params.N.add(1).toBuffer();
|
||||
var AN = new Buffer(params.N.toString(16), 16);
|
||||
var AN1 = new Buffer(params.N.add(1).toString(16), 16);
|
||||
assert.throws(function() {server2.setA(Azero);},
|
||||
/invalid client-supplied 'A'/);
|
||||
assert.throws(function() {server2.setA(AN);},
|
||||
/invalid client-supplied 'A'/);
|
||||
assert.throws(function() {server2.setA(AN1);},
|
||||
/invalid client-supplied 'A'/);
|
||||
},
|
||||
|
||||
"client rejects bad B": function() {
|
||||
// server's "B" must be 1..N-1 . Reject 0 and N and N+1
|
||||
var client2 = new srp.Client(params, salt, identity, password, a);
|
||||
var Bzero = new Buffer(params.N_length_bits/8);
|
||||
Bzero.fill(0, 0, params.N_length_bits/8);
|
||||
//! var BN = params.N.toBuffer();
|
||||
//! var BN1 = params.N.add(1).toBuffer();
|
||||
var BN = new Buffer(params.N.toString(16), 16);
|
||||
var BN1 = new Buffer(params.N.add(1).toString(16), 16);
|
||||
assert.throws(function() {client2.setB(Bzero);},
|
||||
/invalid server-supplied 'B'/);
|
||||
assert.throws(function() {client2.setB(BN);},
|
||||
/invalid server-supplied 'B'/);
|
||||
assert.throws(function() {client2.setB(BN1);},
|
||||
/invalid server-supplied 'B'/);
|
||||
},
|
||||
|
||||
"client rejects bad M2": function() {
|
||||
client = srp.Client(params, salt, identity, password, a);
|
||||
|
||||
// client produces A
|
||||
A = client.computeA();
|
||||
|
||||
// create server
|
||||
server = srp.Server(params, verifier, b);
|
||||
|
||||
// server produces B
|
||||
B = server.computeB();
|
||||
|
||||
// server accepts A
|
||||
server.setA(A);
|
||||
|
||||
// client accepts B
|
||||
client.setB(B);
|
||||
|
||||
// client produces M1 now
|
||||
client.computeM1();
|
||||
|
||||
// server likes client's M1
|
||||
var serverM2 = server.checkM1(client.computeM1());
|
||||
// we tamper with the server's M2
|
||||
serverM2 = serverM2 + "a";
|
||||
|
||||
// client and server agree on K
|
||||
var client_K = client.computeK();
|
||||
var server_K = server.computeK();
|
||||
assert.equal(client_K.toString("hex"), server_K.toString("hex"));
|
||||
|
||||
// server is NOT authentic
|
||||
assert.throws(function(){client.checkM2(serverM2);}, "M2 didn't check");
|
||||
},
|
||||
}
|
||||
|
||||
})
|
||||
.export(module);
|
Loading…
Reference in New Issue
Block a user