Repository navigation
Expand file tree
/
Copy pathclient.js
More file actions
131 lines (105 loc) · 3.67 KB
/
Copy pathclient.js
File metadata and controls
131 lines (105 loc) · 3.67 KB
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
124
125
126
127
128
129
130
131
const sodium = require('sodium-native')
const assert = require('nanoassert')
const C = require('./lib')
const Reader = require('./lib/reader')
const Writer = require('./lib/writer')
const { createKeys, sharedKeysAndValidators } = C
const OPSLIMIT_DEFAULT = sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE
const MEMLIMIT_DEFAULT = sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE
module.exports = class ClientSide {
constructor (clientId) {
this.id = clientId
this.state = new ClientState()
this.sharedKeys = new C.SpakeSharedKeys(C.SpakeSharedKeys.byteLength)
}
generate (publicData, passwd) {
const res = new Uint8Array(C.crypto_spake_RESPONSE1BYTES)
assert(publicData.byteLength === C.crypto_spake_PUBLICDATABYTES)
this._sanitize()
const pub = new Reader(publicData)
const version = pub.u16LE()
assert(version === C.SERVER_VERSION) // TODO
const alg = pub.u16LE()
const opslimit = pub.u64LE()
const memlimit = pub.u64LE()
const salt = pub.read(sodium.crypto_pwhash_SALTBYTES)
const keys = new C.SpakeKeys()
createKeys(keys, salt, passwd, opslimit, memlimit, alg)
const x = new Uint8Array(32)
const gx = new Uint8Array(32)
const X = res.subarray(0, 32)
sodium.crypto_core_ed25519_scalar_random(x)
sodium.crypto_scalarmult_ed25519_base_noclamp(gx, x)
sodium.crypto_core_ed25519_add(X, gx, keys.M)
this.state.x = x
this.state.X = X
this.state.N = keys.N
this.state.hK = keys.hK
this.state.hL = keys.hL
return res
}
finalise (sharedKeys, serverId, msg) {
assert(sharedKeys instanceof C.SpakeSharedKeys)
const res = new Uint8Array(C.crypto_spake_RESPONSE3BYTES)
const validators = new C.SpakeValidators()
const V = new Uint8Array(32)
const Z = new Uint8Array(32)
const gy = new Uint8Array(32)
const Y = msg.subarray(0, 32)
const clientValidator = msg.subarray(32)
sodium.crypto_core_ed25519_sub(gy, Y, this.state.N)
try {
sodium.crypto_scalarmult_ed25519_noclamp(Z, this.state.x, gy)
sodium.crypto_scalarmult_ed25519(V, this.state.hL, gy)
sharedKeysAndValidators(this.sharedKeys, validators, this.id, serverId, this.state.X, Y, Z, this.state.hK, V)
sodium.sodium_memcmp(clientValidator, validators.clientValidator)
} catch (e) {
this._sanitize()
throw new Error('Server keys invalid: protocol aborted.')
}
res.set(validators.serverValidator.subarray(0, 32))
validators._sanitize()
sharedKeys.clientSk.set(this.sharedKeys.clientSk)
sharedKeys.serverSk.set(this.sharedKeys.serverSk)
this._sanitize()
return res
}
static register (passwd, opslimit = OPSLIMIT_DEFAULT, memlimit = MEMLIMIT_DEFAULT) {
const storedData = new Uint8Array(C.crypto_spake_STOREDBYTES)
const keys = new C.SpakeKeys(new Uint8Array(C.SpakeKeys.byteLength))
const salt = new Uint8Array(sodium.crypto_pwhash_SALTBYTES)
sodium.randombytes_buf(salt)
createKeys(keys, salt, passwd, opslimit, memlimit, sodium.crypto_pwhash_ALG_DEFAULT)
const w = new Writer(storedData)
w.u16LE(C.SERVER_VERSION)
.u16LE(sodium.crypto_pwhash_ALG_DEFAULT)
.u64LE(opslimit)
.u64LE(memlimit)
.write(salt)
.write(keys.M)
.write(keys.N)
.write(keys.hK)
.write(keys.L)
return storedData
}
_sanitize () {
this.state._sanitize()
this.sharedKeys._sanitize()
}
}
class ClientState {
constructor () {
this.x = new Uint8Array(32)
this.X = new Uint8Array(32)
this.N = new Uint8Array(32)
this.hK = new Uint8Array(32)
this.hL = new Uint8Array(32)
}
_sanitize () {
this.x.fill(0)
this.X.fill(0)
this.N.fill(0)
this.hK.fill(0)
this.hL.fill(0)
}
}