jsrp
Version:
JavaScript SRP implementation
203 lines (158 loc) • 4.25 kB
text/coffeescript
BigInteger = require('jsbn').BigInteger
createHash = require 'create-hash'
randomBytes = require 'randombytes'
transform = require './transform'
parameters = require './parameters'
class SRP
constructor: (length) ->
@params = parameters.get length
# x = H(s | H(I | ":" | P))
# Returns bigInteger
x: (options) ->
# Options expects I, P, and salt property, where I is the identifier
# and P is the password. All of these should be buffers.
I = options.I
P = options.P
salt = options.salt
identifierPasswordHash = createHash(@params.hash)
.update(I)
.update(Buffer.from(':'))
.update(P)
.digest()
xHash = createHash(@params.hash)
.update(salt)
.update(identifierPasswordHash)
.digest()
result = transform.buffer.toBigInteger xHash
return result
# v = g^x % N
# Returns Buffer
v: (options) ->
# Expects salt, I, P
I = options.I
P = options.P
salt = options.salt
result = @params.g.modPow @x(options), @params.N
result = transform.pad.toN result, @params
return result
# Returns a random BigInteger
a: (callback) ->
randomBytes 32, (err, resultBuf) ->
result = transform.buffer.toBigInteger resultBuf
callback err, result
# Returns a random BigInteger
b: (callback) ->
randomBytes 32, (err, resultBuf) ->
result = transform.buffer.toBigInteger resultBuf
callback err, result
# A = g^a % N
A: (options) ->
# Expects a, which should be a random BigInteger
a = options.a
# Check here to ensure that a.length is not smaller than 256
result = @params.g.modPow a, @params.N
result = transform.pad.toN result, @params
return result
# B = (kv + g^b) % N
B: (options) ->
# Expects v, which should be the client verifier BigInteger, and b,
# which should be a random BigInteger
v = options.v # BigInteger
b = options.b # BigInteger
# This actually ends up being (v + (g^b % N)) % N, this is the way
# everyone else is doing it, so it's the way we're gonna do it.
result = @k().multiply(v).add(@params.g.modPow(b, @params.N)).mod(@params.N)
result = transform.pad.toN result, @params
return result
# u = H(A | B)
# Returns BigInteger
u: (options) ->
# We want A and B, both in buffer format.
A = options.A # Buffer
B = options.B # Buffer
result = createHash(@params.hash)
.update(A)
.update(B)
.digest()
result = transform.buffer.toBigInteger result
return result
# Client S calculation, where S = (B - g^x) ^ (a + u * x) % N
# Returns Buffer
clientS: (options) ->
B = options.B # BigInteger
a = options.a # BigInteger
u = options.u # BigInteger
x = options.x # BigInteger
result = B
.subtract(
@k().multiply(
@params.g.modPow(x, @params.N)
)
)
.modPow(
a.add(
u.multiply(x)
),
@params.N
)
result = transform.pad.toN result, @params
return result
# S = (A * v^u) ^ b % N
serverS: (options) ->
A = options.A # BigInteger
v = options.v # BigInteger
u = options.u # BigInteger
b = options.b # BigInteger
result = A
.multiply(
v.modPow(u, @params.N)
)
.modPow(b, @params.N)
result = transform.pad.toN result, @params
return result
# SRP-6 multiplier
# Returns BigInteger
k: ->
result = createHash(@params.hash)
.update(
transform.pad.toN(@params.N, @params)
)
.update(
transform.pad.toN(@params.g, @params)
)
.digest()
result = transform.buffer.toBigInteger result
return result
K: (options) ->
S = options.S # Buffer
result = createHash(@params.hash)
.update(S)
.digest()
return result
M1: (options) ->
A = options.A # Buffer
B = options.B # Buffer
K = options.K # Buffer
result = createHash(@params.hash)
.update(A)
.update(B)
.update(K)
.digest()
return result
M2: (options) ->
A = options.A # Buffer
M = options.M # Buffer
K = options.K # Buffer
result = createHash(@params.hash)
.update(A)
.update(M)
.update(K)
.digest()
return result
generateSalt: (callback) ->
randomBytes 32, (err, resultBuf) ->
callback err, resultBuf
# This is used to ensure that values are not zero when mod N.
isZeroWhenModN: (thisBigInt) ->
return thisBigInt.mod(@params.N).equals(BigInteger.ZERO)
module.exports = SRP