This repository has been archived by the owner on May 11, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 7
/
secbuf.js
278 lines (244 loc) · 5 KB
/
secbuf.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
const sodium = require('sodium-native')
const { AsyncClass } = require('@holochain/n3h-common')
class SBRoot extends AsyncClass {
/**
*/
async init (size) {
await super.init()
this._size = size
}
/**
*/
size () {
return this._size
}
}
class SBSecure extends SBRoot {
/**
*/
async init (size) {
await super.init(size)
this._alignSize = Math.ceil(size / 8) * 8
this._b = sodium.sodium_malloc(this._alignSize)
sodium.sodium_mprotect_noaccess(this._b)
this.$pushDestructor(() => {
// normally sodium free would clear the buffer...
// but since we're waiting for js gc, let's clear it now
sodium.sodium_mprotect_readwrite(this._b)
this._b.fill(0)
sodium.sodium_mprotect_noaccess(this._b)
this._b = null
})
}
/**
*/
async readable (fn) {
try {
sodium.sodium_mprotect_readonly(this._b)
return await fn(this._b.slice(0, this._size))
} finally {
sodium.sodium_mprotect_noaccess(this._b)
}
}
/**
*/
async writable (fn) {
try {
sodium.sodium_mprotect_readwrite(this._b)
return await fn(this._b.slice(0, this._size))
} finally {
sodium.sodium_mprotect_noaccess(this._b)
}
}
}
class SBInsecure extends SBRoot {
/**
*/
async init (size) {
await super.init(size)
this._b = Buffer.alloc(size)
this.$pushDestructor(() => {
this._b.fill(0)
this._b = null
})
}
/**
*/
async readable (fn) {
return fn(this._b)
}
/**
*/
async writable (fn) {
return fn(this._b)
}
}
class SBRef extends SBRoot {
/**
*/
async init (ref) {
if (!(ref instanceof Buffer)) {
throw new Error('ref must be a Buffer')
}
await super.init(ref.byteLength)
this._b = ref
}
/**
*/
async readable (fn) {
return fn(this._b)
}
/**
*/
async writable (fn) {
return fn(this._b)
}
}
async function _from (oth, offset, len, Class) {
if (typeof offset !== 'number') {
offset = 0
}
if (typeof len !== 'number') {
len = oth.size()
}
oth = await SecBuf.ref(oth)
const out = await new SecBuf(await new Class(len))
await oth.readable(async r => {
await out.write(0, r.slice(offset, offset + len))
})
return out
}
/**
*/
class SecBuf extends AsyncClass {
/**
*/
static async unlockMulti (spec, fn) {
const a = new Array(spec.length)
let res = null
let rej = null
const promise = new Promise((resolve, reject) => {
res = resolve
rej = reject
})
const wait = []
for (let i = 0; i < spec.length; ++i) {
const buf = spec[i][0]
const api = spec[i][1]
wait.push(buf[api](async buffer => {
try {
a[i] = buffer
for (let j of a) {
if (!j) {
await promise
return
}
}
await fn(...a)
res()
} catch (e) {
rej(e)
throw e
}
}))
}
await Promise.all(wait)
}
/**
*/
static async secure (size) {
return new SecBuf(await new SBSecure(size))
}
/**
*/
static async insecure (size) {
return new SecBuf(await new SBInsecure(size))
}
/**
*/
static async ref (oth) {
if (oth instanceof SecBuf) {
return oth
} else if (oth instanceof Buffer) {
return new SecBuf(await new SBRef(oth))
} else if (oth instanceof Uint8Array) {
return new SecBuf(await new SBRef(Buffer.from(oth)))
} else {
throw new Error('oth must be a SecBuf, Buffer, or Uint8Array')
}
}
/**
*/
static async secureFrom (oth, offset, len) {
return _from(oth, offset, len, SBSecure)
}
/**
*/
static async insecureFrom (oth, offset, len) {
return _from(oth, offset, len, SBInsecure)
}
/**
*/
async init (backend) {
await super.init()
if (!(backend instanceof SBRoot)) {
throw new Error('can only create SecBuf with SBSecure or SBInsecure')
}
this._b = backend
this.$pushDestructor(async () => {
await this._b.destroy()
this._b = null
})
}
/**
*/
size () {
return this._b.size()
}
/**
*/
async readable (fn) {
return this._b.readable(fn)
}
/**
*/
async writable (fn) {
return this._b.writable(fn)
}
/**
*/
async write (offset, oth) {
if (typeof offset !== 'number') {
throw new Error('offset must be a number')
}
oth = await SecBuf.ref(oth)
if (offset + oth.size() > this.size()) {
throw new Error('would write out of bounds')
}
await this.writable(async w => {
await oth.readable(r => {
r.copy(w, offset)
})
})
}
/**
*/
async increment () {
await this.writable(async w => {
sodium.sodium_increment(w)
})
}
/**
*/
async compare (oth) {
oth = await SecBuf.ref(oth)
let out = null
await this.readable(async rA => {
await oth.readable(async rB => {
out = sodium.sodium_compare(rA, rB)
})
})
return out
}
}
exports.SecBuf = SecBuf