forked from LinusU/secure-remote-password
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.js
123 lines (96 loc) · 3.23 KB
/
client.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
'use strict'
const params = require('./lib/params')
const SRPInteger = require('./lib/srp-integer')
exports.generateSalt = function () {
// s User's salt
const s = SRPInteger.randomInteger(params.hashOutputBytes)
return s.toHex()
}
exports.derivePrivateKey = function (salt, username, password) {
// H() One-way hash function
const { H } = params
// s User's salt
// I Username
// p Cleartext Password
const s = SRPInteger.fromHex(salt)
const I = String(username)
const p = String(password)
// x = H(s, H(I | ':' | p)) (s is chosen randomly)
const x = H(s, H(`${I}:${p}`))
return x.toHex()
}
exports.deriveVerifier = function (privateKey) {
// 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)
const x = SRPInteger.fromHex(privateKey)
// v = g^x (computes password verifier)
const v = g.modPow(x, N)
return v.toHex()
}
exports.generateEphemeral = function () {
// 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)
const a = SRPInteger.randomInteger(params.hashOutputBytes)
const A = g.modPow(a, N)
return {
secret: a.toHex(),
public: A.toHex()
}
}
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)
const a = SRPInteger.fromHex(clientSecretEphemeral)
const B = SRPInteger.fromHex(serverPublicEphemeral)
const s = SRPInteger.fromHex(salt)
const I = String(username)
const x = SRPInteger.fromHex(privateKey)
// A = g^a (a = random number)
const A = g.modPow(a, N)
// B % N > 0
if (B.mod(N).equals(SRPInteger.ZERO)) {
// fixme: .code, .statusCode, etc.
throw new Error('The server sent an invalid public ephemeral')
}
// u = H(A, B)
const u = H(A, B)
// S = (B - kg^x) ^ (a + ux)
const S = B.subtract(k.multiply(g.modPow(x, N))).modPow(a.add(u.multiply(x)), N)
// K = H(S)
const K = H(S)
// M = H(H(N) xor H(g), H(I), s, A, B, K)
const M = H(H(N).xor(H(g)), H(I), s, A, B, K)
return {
key: K.toHex(),
proof: M.toHex()
}
}
exports.verifySession = function (clientPublicEphemeral, clientSession, serverSessionProof) {
// H() One-way hash function
const { H } = params
// 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)
// H(A, M, K)
const expected = H(A, M, K)
const actual = SRPInteger.fromHex(serverSessionProof)
if (!actual.equals(expected)) {
// fixme: .code, .statusCode, etc.
throw new Error('Server provided session proof is invalid')
}
}