@mblaney/holster 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ts/src/sea.ts CHANGED
@@ -3,14 +3,10 @@
3
3
  * Cryptographic functions for signing, verification, encryption, and decryption
4
4
  */
5
5
 
6
- import { userPublicKey, userSignature } from "./utils.ts"
6
+ import {userPublicKey, userSignature} from "./utils.ts"
7
7
  import * as utils from "./sea-utils.ts"
8
8
  import SafeBuffer from "./buffer.ts"
9
- import type {
10
- UserPair,
11
- EncryptedData,
12
- SignedData,
13
- } from "./schemas.ts"
9
+ import type {UserPair, EncryptedData, SignedData} from "./schemas.ts"
14
10
 
15
11
  type Callback<T> = (result: T) => void
16
12
 
@@ -24,7 +20,7 @@ const SEA = {
24
20
  pair: async (cb?: Callback<UserPair>): Promise<UserPair> => {
25
21
  // ECDSA keys for signing/verifying
26
22
  const ecdsa = await utils.subtle
27
- .generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, [
23
+ .generateKey({name: "ECDSA", namedCurve: "P-256"}, true, [
28
24
  "sign",
29
25
  "verify",
30
26
  ])
@@ -38,7 +34,7 @@ const SEA = {
38
34
 
39
35
  // ECDH keys for encryption/decryption
40
36
  const ecdh = await utils.subtle
41
- .generateKey({ name: "ECDH", namedCurve: "P-256" }, true, ["deriveKey"])
37
+ .generateKey({name: "ECDH", namedCurve: "P-256"}, true, ["deriveKey"])
42
38
  .then(async keys => {
43
39
  const pub = await utils.subtle.exportKey("jwk", keys.publicKey)
44
40
  return {
@@ -63,14 +59,14 @@ const SEA = {
63
59
  encrypt: async (
64
60
  data: unknown,
65
61
  pair: Partial<UserPair> | null,
66
- cb?: Callback<EncryptedData | null>
62
+ cb?: Callback<EncryptedData | null>,
67
63
  ): Promise<EncryptedData | null> => {
68
64
  if (!pair || !pair.epriv) {
69
65
  if (cb) cb(null)
70
66
  return null
71
67
  }
72
68
 
73
- const rand = { s: utils.random(9), iv: utils.random(15) }
69
+ const rand = {s: utils.random(9), iv: utils.random(15)}
74
70
  const ct = await utils.aeskey(pair.epriv, rand.s).then(aes => {
75
71
  return utils.subtle.encrypt(
76
72
  {
@@ -78,7 +74,7 @@ const SEA = {
78
74
  iv: new Uint8Array(rand.iv),
79
75
  },
80
76
  aes,
81
- new TextEncoder().encode(utils.stringify(data))
77
+ new TextEncoder().encode(utils.stringify(data)),
82
78
  )
83
79
  })
84
80
  const enc: EncryptedData = {
@@ -96,7 +92,7 @@ const SEA = {
96
92
  decrypt: async (
97
93
  enc: Partial<EncryptedData> | null,
98
94
  pair: Partial<UserPair> | null,
99
- cb?: Callback<unknown | null>
95
+ cb?: Callback<unknown | null>,
100
96
  ): Promise<unknown | null> => {
101
97
  if (!enc || !enc.ct || !enc.iv || !enc.s || !pair || !pair.epriv) {
102
98
  if (cb) cb(null)
@@ -117,7 +113,7 @@ const SEA = {
117
113
  tagLength: 128,
118
114
  },
119
115
  aes,
120
- new Uint8Array(data.ct)
116
+ new Uint8Array(data.ct),
121
117
  )
122
118
  })
123
119
  const dec = utils.parse(new TextDecoder("utf8").decode(ct))
@@ -136,7 +132,7 @@ const SEA = {
136
132
  verify: async (
137
133
  data: string | SignedData,
138
134
  pair: Partial<UserPair> | null,
139
- cb?: Callback<unknown | null>
135
+ cb?: Callback<unknown | null>,
140
136
  ): Promise<unknown | null> => {
141
137
  if (!pair || !pair.pub) {
142
138
  if (cb) cb(null)
@@ -149,9 +145,9 @@ const SEA = {
149
145
  const key = await utils.subtle.importKey(
150
146
  "jwk",
151
147
  utils.jwk(pair.pub),
152
- { name: "ECDSA", namedCurve: "P-256" },
148
+ {name: "ECDSA", namedCurve: "P-256"},
153
149
  false,
154
- ["verify"]
150
+ ["verify"],
155
151
  )
156
152
 
157
153
  let msg: string | Record<string, unknown> = {}
@@ -162,13 +158,13 @@ const SEA = {
162
158
  // (which should not be part of signature, so not verified)
163
159
  for (const k of Object.keys(signed.m).sort()) {
164
160
  if (k !== "_" && k !== userPublicKey && k !== userSignature) {
165
- (msg as Record<string, unknown>)[k] = signed.m[k]
161
+ ;(msg as Record<string, unknown>)[k] = signed.m[k]
166
162
  }
167
163
  }
168
164
  }
169
165
  const hash = await utils.sha256(msg)
170
166
  const sig = new Uint8Array(SafeBuffer.from(signed.s, "base64"))
171
- const alg = { name: "ECDSA", hash: { name: "SHA-256" } }
167
+ const alg = {name: "ECDSA", hash: {name: "SHA-256"}}
172
168
  if (await utils.subtle.verify(alg, key, sig, new Uint8Array(hash))) {
173
169
  const verified = utils.parse(signed.m as string)
174
170
  if (cb) cb(verified)
@@ -185,7 +181,7 @@ const SEA = {
185
181
  sign: async (
186
182
  data: string | Record<string, unknown>,
187
183
  pair: Partial<UserPair> | null,
188
- cb?: Callback<SignedData | null>
184
+ cb?: Callback<SignedData | null>,
189
185
  ): Promise<SignedData | null> => {
190
186
  if (!pair || !pair.pub || !pair.priv) {
191
187
  if (cb) cb(null)
@@ -199,21 +195,21 @@ const SEA = {
199
195
  const check = utils.parse(data as never) as Record<string, unknown>
200
196
  for (const k of Object.keys(check).sort()) {
201
197
  if (k !== "_" && k !== userPublicKey && k !== userSignature) {
202
- (msg as Record<string, unknown>)[k] = check[k]
198
+ ;(msg as Record<string, unknown>)[k] = check[k]
203
199
  }
204
200
  }
205
201
  }
206
202
  const hash = await utils.sha256(msg)
207
203
  const jwk = utils.jwk(pair.pub, pair.priv)
208
- const alg = { name: "ECDSA", namedCurve: "P-256" }
204
+ const alg = {name: "ECDSA", namedCurve: "P-256"}
209
205
  const sig = await utils.subtle
210
206
  .importKey("jwk", jwk, alg, false, ["sign"])
211
207
  .then(key =>
212
208
  utils.subtle.sign(
213
- { name: "ECDSA", hash: { name: "SHA-256" } },
209
+ {name: "ECDSA", hash: {name: "SHA-256"}},
214
210
  key,
215
- new Uint8Array(hash)
216
- )
211
+ new Uint8Array(hash),
212
+ ),
217
213
  )
218
214
  const signed: SignedData = {
219
215
  m: msg,
@@ -230,7 +226,7 @@ const SEA = {
230
226
  signTimestamp: async (
231
227
  timestamp: number,
232
228
  pair: Partial<UserPair> | null,
233
- cb?: Callback<string | null>
229
+ cb?: Callback<string | null>,
234
230
  ): Promise<string | null> => {
235
231
  if (!pair || !pair.pub || !pair.priv) {
236
232
  if (cb) cb(null)
@@ -241,16 +237,16 @@ const SEA = {
241
237
  const timestampStr = timestamp.toString()
242
238
  const hash = await utils.sha256(timestampStr)
243
239
  const jwk = utils.jwk(pair.pub, pair.priv)
244
- const alg = { name: "ECDSA", namedCurve: "P-256" }
240
+ const alg = {name: "ECDSA", namedCurve: "P-256"}
245
241
 
246
242
  const sig = await utils.subtle
247
243
  .importKey("jwk", jwk, alg, false, ["sign"])
248
244
  .then(key =>
249
245
  utils.subtle.sign(
250
- { name: "ECDSA", hash: { name: "SHA-256" } },
246
+ {name: "ECDSA", hash: {name: "SHA-256"}},
251
247
  key,
252
- new Uint8Array(hash)
253
- )
248
+ new Uint8Array(hash),
249
+ ),
254
250
  )
255
251
 
256
252
  const signature = SafeBuffer.from(sig, "binary").toString("base64")
@@ -266,7 +262,7 @@ const SEA = {
266
262
  timestamp: number,
267
263
  signature: string,
268
264
  publicKey: string,
269
- cb?: Callback<boolean>
265
+ cb?: Callback<boolean>,
270
266
  ): Promise<boolean> => {
271
267
  if (!publicKey || !timestamp || !signature) {
272
268
  if (cb) cb(false)
@@ -281,25 +277,27 @@ const SEA = {
281
277
  const key = await utils.subtle.importKey(
282
278
  "jwk",
283
279
  utils.jwk(publicKey),
284
- { name: "ECDSA", namedCurve: "P-256" },
280
+ {name: "ECDSA", namedCurve: "P-256"},
285
281
  false,
286
- ["verify"]
282
+ ["verify"],
287
283
  )
288
284
 
289
285
  const sig = new Uint8Array(SafeBuffer.from(signature, "base64"))
290
- const alg = { name: "ECDSA", hash: { name: "SHA-256" } }
286
+ const alg = {name: "ECDSA", hash: {name: "SHA-256"}}
291
287
 
292
288
  const isValid = await utils.subtle.verify(
293
289
  alg,
294
290
  key,
295
291
  sig,
296
- new Uint8Array(hash)
292
+ new Uint8Array(hash),
297
293
  )
298
294
 
299
295
  if (cb) cb(isValid)
300
296
  return isValid
301
297
  } catch (err) {
302
- console.log(`error verifying timestamp signature: ${(err as Error).message}`)
298
+ console.log(
299
+ `error verifying timestamp signature: ${(err as Error).message}`,
300
+ )
303
301
  if (cb) cb(false)
304
302
  return false
305
303
  }
@@ -311,13 +309,13 @@ const SEA = {
311
309
  work: async (
312
310
  data: unknown,
313
311
  salt?: unknown,
314
- cb?: Callback<{ epriv: string }>
315
- ): Promise<{ epriv: string }> => {
312
+ cb?: Callback<{epriv: string}>,
313
+ ): Promise<{epriv: string}> => {
316
314
  let _cb = cb
317
315
  let _salt = salt
318
316
 
319
317
  if (typeof salt === "function") {
320
- _cb = salt as Callback<{ epriv: string }>
318
+ _cb = salt as Callback<{epriv: string}>
321
319
  _salt = undefined
322
320
  }
323
321
  if (typeof _salt === "undefined") _salt = utils.random(9)
@@ -325,19 +323,19 @@ const SEA = {
325
323
  const key = await utils.subtle.importKey(
326
324
  "raw",
327
325
  new TextEncoder().encode(utils.stringify(data)),
328
- { name: "PBKDF2" },
326
+ {name: "PBKDF2"},
329
327
  false,
330
- ["deriveBits"]
328
+ ["deriveBits"],
331
329
  )
332
330
  const alg = {
333
331
  name: "PBKDF2",
334
332
  iterations: 100000,
335
333
  salt: new TextEncoder().encode(_salt as string),
336
- hash: { name: "SHA-256" },
334
+ hash: {name: "SHA-256"},
337
335
  }
338
336
  const work = await utils.subtle.deriveBits(alg, key, 512)
339
337
  // Use "pair" format so that work can be used as epriv by decrypt
340
- const pair = { epriv: SafeBuffer.from(work, "binary").toString("base64") }
338
+ const pair = {epriv: SafeBuffer.from(work, "binary").toString("base64")}
341
339
  if (_cb) _cb(pair)
342
340
  return pair
343
341
  },
@@ -348,41 +346,40 @@ const SEA = {
348
346
  secret: async (
349
347
  to: Partial<UserPair> | null,
350
348
  from: Partial<UserPair> | null,
351
- cb?: Callback<{ epriv: string } | null>
352
- ): Promise<{ epriv: string } | null> => {
349
+ cb?: Callback<{epriv: string} | null>,
350
+ ): Promise<{epriv: string} | null> => {
353
351
  if (!to || !to.epub || !from || !from.epub || !from.epriv) {
354
352
  if (cb) cb(null)
355
353
  return null
356
354
  }
357
355
 
358
- const alg = { name: "ECDH", namedCurve: "P-256" }
356
+ const alg = {name: "ECDH", namedCurve: "P-256"}
359
357
  const pub = utils.jwk(to.epub)
360
358
  const pubKey = await utils.subtle.importKey("jwk", pub, alg, true, [])
361
359
  const priv = utils.jwk(from.epub, from.epriv)
362
360
  // utils.jwk provides default key_ops but it shouldn't be used here
363
- delete (priv as { key_ops?: unknown }).key_ops
361
+ delete (priv as {key_ops?: unknown}).key_ops
364
362
  const derived = await utils.subtle
365
363
  .importKey("jwk", priv, alg, false, ["deriveBits"])
366
364
  .then(async key => {
367
365
  const derivedBits = await utils.subtle.deriveBits(
368
- { public: pubKey, name: "ECDH", namedCurve: "P-256" } as never,
366
+ {public: pubKey, name: "ECDH", namedCurve: "P-256"} as never,
369
367
  key,
370
- 256
368
+ 256,
371
369
  )
372
370
  const derivedKey = await utils.subtle.importKey(
373
371
  "raw",
374
372
  new Uint8Array(derivedBits),
375
- { name: "AES-GCM", length: 256 },
373
+ {name: "AES-GCM", length: 256},
376
374
  true,
377
- ["encrypt", "decrypt"]
375
+ ["encrypt", "decrypt"],
378
376
  )
379
- return utils.subtle.exportKey("jwk", derivedKey).then(({ k }) => k!)
377
+ return utils.subtle.exportKey("jwk", derivedKey).then(({k}) => k!)
380
378
  })
381
379
  // Use "pair" format so that secret can be used as epriv by encrypt
382
- if (cb) cb({ epriv: derived })
383
- return { epriv: derived }
380
+ if (cb) cb({epriv: derived})
381
+ return {epriv: derived}
384
382
  },
385
383
  }
386
384
 
387
385
  export default SEA
388
-
package/ts/src/store.ts CHANGED
@@ -18,9 +18,7 @@ import type {
18
18
  const isNode = typeof process !== "undefined" && process.versions?.node != null
19
19
 
20
20
  // Dynamic import for Node.js fs module - won't execute in browser/service worker
21
- const fs = isNode
22
- ? await import(/*webpackIgnore: true*/ "node:fs")
23
- : undefined
21
+ const fs = isNode ? await import(/*webpackIgnore: true*/ "node:fs") : undefined
24
22
 
25
23
  // ASCII character for enquiry
26
24
  const enq = String.fromCharCode(5)
@@ -41,39 +39,49 @@ const fileSystem = (opt: StoreOptions): FileSystemInterface => {
41
39
 
42
40
  return {
43
41
  get: (file, cb) => {
44
- fs!.readFile(dir + "/" + file, (err: NodeJS.ErrnoException | null, data?: Buffer) => {
45
- if (err) {
46
- if ((err as { code?: string }).code === "ENOENT") {
47
- cb()
48
- return
42
+ fs!.readFile(
43
+ dir + "/" + file,
44
+ (err: NodeJS.ErrnoException | null, data?: Buffer) => {
45
+ if (err) {
46
+ if ((err as {code?: string}).code === "ENOENT") {
47
+ cb()
48
+ return
49
+ }
50
+ console.log("fs.readFile error:", err)
49
51
  }
50
- console.log("fs.readFile error:", err)
51
- }
52
- const dataStr = data ? data.toString() : undefined
53
- cb(err as never, dataStr)
54
- })
52
+ const dataStr = data ? data.toString() : undefined
53
+ cb(err as never, dataStr)
54
+ },
55
+ )
55
56
  },
56
57
  put: (file, data, cb) => {
57
58
  const tmp = file + "." + utils.text.random(9) + ".tmp"
58
- fs!.writeFile(tmp, data as string, (err: NodeJS.ErrnoException | null) => {
59
- if (err) {
60
- console.log("fs.writeFile error:", err)
61
- cb(err as never)
62
- return
63
- }
64
- fs!.rename(tmp, dir + "/" + file, cb as never)
65
- })
59
+ fs!.writeFile(
60
+ tmp,
61
+ data as string,
62
+ (err: NodeJS.ErrnoException | null) => {
63
+ if (err) {
64
+ console.log("fs.writeFile error:", err)
65
+ cb(err as never)
66
+ return
67
+ }
68
+ fs!.rename(tmp, dir + "/" + file, cb as never)
69
+ },
70
+ )
66
71
  },
67
72
  list: cb => {
68
- fs!.readdir(dir, (err: NodeJS.ErrnoException | null, files?: string[]) => {
69
- if (err) {
70
- console.log("fs.readdir error:", err)
73
+ fs!.readdir(
74
+ dir,
75
+ (err: NodeJS.ErrnoException | null, files?: string[]) => {
76
+ if (err) {
77
+ console.log("fs.readdir error:", err)
78
+ cb()
79
+ return
80
+ }
81
+ if (files) files.forEach(cb)
71
82
  cb()
72
- return
73
- }
74
- if (files) files.forEach(cb)
75
- cb()
76
- })
83
+ },
84
+ )
77
85
  },
78
86
  }
79
87
  }
@@ -122,7 +130,10 @@ const fileSystem = (opt: StoreOptions): FileSystemInterface => {
122
130
 
123
131
  return {
124
132
  get: (file, cb) => {
125
- const _get = (file: string, cb: (err?: string | null, data?: string) => void) => {
133
+ const _get = (
134
+ file: string,
135
+ cb: (err?: string | null, data?: string) => void,
136
+ ) => {
126
137
  const tx = db.transaction([dir], "readonly")
127
138
  const req = tx.objectStore(dir).get(file)
128
139
  req.onerror = () => {
@@ -146,7 +157,7 @@ const fileSystem = (opt: StoreOptions): FileSystemInterface => {
146
157
  const _put = (
147
158
  file: string,
148
159
  data: string,
149
- cb: (err?: string | null) => void
160
+ cb: (err?: string | null) => void,
150
161
  ) => {
151
162
  const tx = db.transaction([dir], "readwrite")
152
163
  const req = tx.objectStore(dir).put(data, file)
@@ -209,10 +220,7 @@ const fileSystem = (opt: StoreOptions): FileSystemInterface => {
209
220
  }
210
221
 
211
222
  export interface StoreInterface {
212
- get: (
213
- lex: Lex,
214
- cb: (err?: string | null, graph?: Graph) => void,
215
- ) => void
223
+ get: (lex: Lex, cb: (err?: string | null, graph?: Graph) => void) => void
216
224
  put: (graph: Graph, cb: (err?: string | null) => void) => void
217
225
  }
218
226
 
@@ -244,7 +252,7 @@ const Store = (opt?: StoreOptions): StoreInterface => {
244
252
  const each = (value: EncodedValue, k: string): void => {
245
253
  if (k !== utils.userPublicKey && !utils.match(lex["."], k)) return
246
254
 
247
- if (!node) node = { _: { "#": soul, ">": {} } }
255
+ if (!node) node = {_: {"#": soul, ">": {}}}
248
256
  node[k] = value[0]
249
257
  node._[">"][k] = value[1]
250
258
  // If signature is present, store it in _["s"]
@@ -262,7 +270,7 @@ const Store = (opt?: StoreOptions): StoreInterface => {
262
270
  const finish = (err: string | null | undefined): void => {
263
271
  if (err && !cbErr) cbErr = err
264
272
  if (--pending > 0) return
265
- const graph: Graph = { [soul]: node! }
273
+ const graph: Graph = {[soul]: node!}
266
274
  if (node && Object.keys(signatures).length > 0) {
267
275
  node._["s"] = signatures
268
276
  }
@@ -281,7 +289,8 @@ const Store = (opt?: StoreOptions): StoreInterface => {
281
289
 
282
290
  if (needsPubKey) {
283
291
  radisk(soul + enq + utils.userPublicKey, (err, value) => {
284
- if (value && !utils.obj.is(value)) each(value as EncodedValue, utils.userPublicKey)
292
+ if (value && !utils.obj.is(value))
293
+ each(value as EncodedValue, utils.userPublicKey)
285
294
  finish(err)
286
295
  })
287
296
  }
@@ -319,9 +328,7 @@ const Store = (opt?: StoreOptions): StoreInterface => {
319
328
  const value = node[key]!
320
329
  const state = node._[">"][key]!
321
330
  const sig = node._["s"] && node._["s"][state]
322
- const data: EncodedValue = sig
323
- ? [value, state, sig]
324
- : [value, state]
331
+ const data: EncodedValue = sig ? [value, state, sig] : [value, state]
325
332
  radisk(soul + enq + key, data, ack)
326
333
  })
327
334
  })
@@ -330,4 +337,3 @@ const Store = (opt?: StoreOptions): StoreInterface => {
330
337
  }
331
338
 
332
339
  export default Store
333
-