Skip to content

Commit

Permalink
Merge LinusU#13 and bump package version
Browse files Browse the repository at this point in the history
  • Loading branch information
TheBinaryGuy committed Jul 7, 2023
1 parent 4e3c41c commit 2e567b9
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 52 deletions.
3 changes: 3 additions & 0 deletions browser/sha256.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ function concat (buffers) {
return combined.buffer
}

/**
* @param {(string | SRPInteger)[]} args
*/
module.exports = function sha256 (...args) {
const buffer = concat(args.map((arg) => {
if (arg instanceof SRPInteger) {
Expand Down
55 changes: 28 additions & 27 deletions client.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ const params = require('./lib/params')
const SRPInteger = require('./lib/srp-integer')

exports.generateSalt = function () {
// s User's salt
// s User's salt
const s = SRPInteger.randomInteger(params.hashOutputBytes)

return s.toHex()
}

exports.derivePrivateKey = function (salt, username, password) {
// H() One-way hash function
// H() One-way hash function
const { H } = params

// s User's salt
// I Username
// p Cleartext Password
// s User's salt
// I Username
// p Cleartext Password
const s = SRPInteger.fromHex(salt)
const I = String(username)
const p = String(password)
Expand All @@ -28,11 +28,11 @@ exports.derivePrivateKey = function (salt, username, password) {
}

exports.deriveVerifier = function (privateKey) {
// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
const { N, g } = params

// x Private key (derived from p and s)
// x Private key (derived from p and s)
const x = SRPInteger.fromHex(privateKey)

// v = g^x (computes password verifier)
Expand All @@ -42,8 +42,8 @@ exports.deriveVerifier = function (privateKey) {
}

exports.generateEphemeral = function () {
// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
const { N, g } = params

// A = g^a (a = random number)
Expand All @@ -57,17 +57,18 @@ exports.generateEphemeral = function () {
}

exports.deriveSession = function (clientSecretEphemeral, serverPublicEphemeral, salt, username, privateKey) {
// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)
// H() One-way hash function
const { N, g, k, H } = params

// a Secret ephemeral values
// B Public ephemeral values
// s User's salt
// I Username
// x Private key (derived from p and s)
// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)
// H() One-way hash function
// PAD() Pad the number to have the same number of bytes as N
const { N, g, k, H, PAD } = params

// a Secret ephemeral values
// B Public ephemeral values
// s User's salt
// I Username
// x Private key (derived from p and s)
const a = SRPInteger.fromHex(clientSecretEphemeral)
const B = SRPInteger.fromHex(serverPublicEphemeral)
const s = SRPInteger.fromHex(salt)
Expand All @@ -83,8 +84,8 @@ exports.deriveSession = function (clientSecretEphemeral, serverPublicEphemeral,
throw new Error('The server sent an invalid public ephemeral')
}

// u = H(A, B)
const u = H(A, B)
// u = H(PAD(A), PAD(B))
const u = H(PAD(A), PAD(B))

// S = (B - kg^x) ^ (a + ux)
const S = B.subtract(k.multiply(g.modPow(x, N))).modPow(a.add(u.multiply(x)), N)
Expand All @@ -102,12 +103,12 @@ exports.deriveSession = function (clientSecretEphemeral, serverPublicEphemeral,
}

exports.verifySession = function (clientPublicEphemeral, clientSession, serverSessionProof) {
// H() One-way hash function
// H() One-way hash function
const { H } = params

// A Public ephemeral values
// M Proof of K
// K Shared, strong session key
// A Public ephemeral values
// M Proof of K
// K Shared, strong session key
const A = SRPInteger.fromHex(clientPublicEphemeral)
const M = SRPInteger.fromHex(clientSession.proof)
const K = SRPInteger.fromHex(clientSession.key)
Expand Down
22 changes: 16 additions & 6 deletions lib/params.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,26 @@ const input = {
`,
generatorModulo: '02',
hashFunction: 'sha256',
hashOutputBytes: (256 / 8)
hashOutputBytes: (256 / 8),
paddedLength: 512
}

// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)
// H() One-way hash function
/**
* @param {SRPInteger} integer
*/
function pad (integer) {
return integer.pad(input.paddedLength)
}

// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)
// H() One-way hash function
// PAD() Pad the number to have the same number of bytes as N
exports.N = SRPInteger.fromHex(input.largeSafePrime.replace(/\s+/g, ''))
exports.g = SRPInteger.fromHex(input.generatorModulo.replace(/\s+/g, ''))
exports.k = sha256(exports.N, exports.g)
exports.k = sha256(exports.N, pad(exports.g))
exports.H = sha256
exports.PAD = pad

exports.hashOutputBytes = input.hashOutputBytes
3 changes: 3 additions & 0 deletions lib/sha256.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const crypto = require('crypto')

const SRPInteger = require('./srp-integer')

/**
* @param {(string | SRPInteger)[]} args
*/
module.exports = function sha256 (...args) {
const h = crypto.createHash('sha256')

Expand Down
8 changes: 8 additions & 0 deletions lib/srp-integer.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ class SRPInteger {
return new SRPInteger(this[kBigInteger].xor(val[kBigInteger]), this[kHexLength])
}

pad (hexLength) {
if (hexLength < this[kHexLength]) {
throw new Error('Cannot pad to a shorter length')
}

return new SRPInteger(this[kBigInteger], hexLength)
}

inspect () {
const hex = this[kBigInteger].toString(16)

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "secure-remote-password",
"version": "0.3.1",
"version": "0.3.2",
"license": "MIT",
"repository": "LinusU/secure-remote-password",
"scripts": {
Expand Down
37 changes: 19 additions & 18 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ const params = require('./lib/params')
const SRPInteger = require('./lib/srp-integer')

exports.generateEphemeral = function (verifier) {
// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)
// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)
const { N, g, k } = params

// v Password verifier
// v Password verifier
const v = SRPInteger.fromHex(verifier)

// B = kv + g^b (b = random number)
Expand All @@ -23,18 +23,19 @@ exports.generateEphemeral = function (verifier) {
}

exports.deriveSession = function (serverSecretEphemeral, clientPublicEphemeral, salt, username, verifier, clientSessionProof) {
// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)
// H() One-way hash function
const { N, g, k, H } = params

// b Secret ephemeral values
// A Public ephemeral values
// s User's salt
// p Cleartext Password
// I Username
// v Password verifier
// N A large safe prime (N = 2q+1, where q is prime)
// g A generator modulo N
// k Multiplier parameter (k = H(N, g) in SRP-6a, k = 3 for legacy SRP-6)
// H() One-way hash function
// PAD() Pad the number to have the same number of bytes as N
const { N, g, k, H, PAD } = params

// b Secret ephemeral values
// A Public ephemeral values
// s User's salt
// p Cleartext Password
// I Username
// v Password verifier
const b = SRPInteger.fromHex(serverSecretEphemeral)
const A = SRPInteger.fromHex(clientPublicEphemeral)
const s = SRPInteger.fromHex(salt)
Expand All @@ -50,8 +51,8 @@ exports.deriveSession = function (serverSecretEphemeral, clientPublicEphemeral,
throw new Error('The client sent an invalid public ephemeral')
}

// u = H(A, B)
const u = H(A, B)
// u = H(PAD(A), PAD(B))
const u = H(PAD(A), PAD(B))

// S = (Av^u) ^ b (computes session key)
const S = A.multiply(v.modPow(u, N)).modPow(b, N)
Expand Down

0 comments on commit 2e567b9

Please sign in to comment.