@arkstack/encryption 0.18.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/LICENSE +21 -0
- package/README.md +224 -0
- package/dist/index.d.ts +914 -0
- package/dist/index.js +1213 -0
- package/dist/node.d.ts +78 -0
- package/dist/node.js +112 -0
- package/package.json +43 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,914 @@
|
|
|
1
|
+
//#region src/EncryptionKey.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* A symmetric key, held as raw bytes and convertible to every representation
|
|
4
|
+
* the rest of the library (and the wire) needs.
|
|
5
|
+
*
|
|
6
|
+
* Keys are values: two keys with the same bytes are equal regardless of how
|
|
7
|
+
* they were produced, and comparison is constant time.
|
|
8
|
+
*/
|
|
9
|
+
declare class EncryptionKey {
|
|
10
|
+
readonly bytes: Uint8Array;
|
|
11
|
+
/**
|
|
12
|
+
* @param bytes Raw key material.
|
|
13
|
+
*/
|
|
14
|
+
constructor(bytes: Uint8Array);
|
|
15
|
+
/**
|
|
16
|
+
* Generate a random key.
|
|
17
|
+
*
|
|
18
|
+
* @param length Key length in bytes, defaults to 32 (AES-256).
|
|
19
|
+
* @returns
|
|
20
|
+
*/
|
|
21
|
+
static generate(length?: number): EncryptionKey;
|
|
22
|
+
/**
|
|
23
|
+
* Derive a key from an arbitrary secret by hashing it with SHA-256.
|
|
24
|
+
*
|
|
25
|
+
* This mirrors how Arkstack turns `APP_KEY` into a cipher key, so a value
|
|
26
|
+
* encrypted on the server with the app key can be decrypted in the browser
|
|
27
|
+
* from the same secret.
|
|
28
|
+
*
|
|
29
|
+
* @param secret
|
|
30
|
+
* @returns
|
|
31
|
+
*/
|
|
32
|
+
static fromSecret(secret: string): Promise<EncryptionKey>;
|
|
33
|
+
/**
|
|
34
|
+
* Restore a key from its base64url representation.
|
|
35
|
+
*
|
|
36
|
+
* @param value
|
|
37
|
+
* @returns
|
|
38
|
+
*/
|
|
39
|
+
static fromBase64Url(value: string): EncryptionKey;
|
|
40
|
+
/**
|
|
41
|
+
* Restore a key from its hex representation.
|
|
42
|
+
*
|
|
43
|
+
* @param value
|
|
44
|
+
* @returns
|
|
45
|
+
*/
|
|
46
|
+
static fromHex(value: string): EncryptionKey;
|
|
47
|
+
/**
|
|
48
|
+
* Stretch a password into a key using PBKDF2-HMAC-SHA256.
|
|
49
|
+
*
|
|
50
|
+
* Prefer this over {@link fromSecret} for anything a human typed; the
|
|
51
|
+
* returned salt and iteration count must be stored alongside the
|
|
52
|
+
* ciphertext to reproduce the key later.
|
|
53
|
+
*
|
|
54
|
+
* @param password
|
|
55
|
+
* @param options
|
|
56
|
+
* @returns
|
|
57
|
+
*/
|
|
58
|
+
static derive(password: string, options?: DeriveOptions): Promise<DerivedKey>;
|
|
59
|
+
/**
|
|
60
|
+
* Expand shared secret material into a key using HKDF-SHA256.
|
|
61
|
+
*
|
|
62
|
+
* Used internally by the ECDH channel and sealed box helpers, and exposed
|
|
63
|
+
* because deriving sub-keys from one root key is a common need.
|
|
64
|
+
*
|
|
65
|
+
* @param material
|
|
66
|
+
* @param salt
|
|
67
|
+
* @param info
|
|
68
|
+
* @param length
|
|
69
|
+
* @returns
|
|
70
|
+
*/
|
|
71
|
+
static expand(material: Uint8Array, salt: Uint8Array, info: string, length?: number): Promise<EncryptionKey>;
|
|
72
|
+
/**
|
|
73
|
+
* Coerce any accepted key representation into an `EncryptionKey`.
|
|
74
|
+
*
|
|
75
|
+
* A string of exactly `length` bytes once base64url decoded is treated as
|
|
76
|
+
* raw key material; anything else is treated as a passphrase and hashed.
|
|
77
|
+
*
|
|
78
|
+
* @param input
|
|
79
|
+
* @param length Expected key length in bytes.
|
|
80
|
+
* @returns
|
|
81
|
+
*/
|
|
82
|
+
static resolve(input: KeyInput, length?: number): Promise<EncryptionKey>;
|
|
83
|
+
/**
|
|
84
|
+
* Constant time comparison of two keys, in any representation that does not
|
|
85
|
+
* require asynchronous work.
|
|
86
|
+
*
|
|
87
|
+
* @param left
|
|
88
|
+
* @param right
|
|
89
|
+
* @returns
|
|
90
|
+
*/
|
|
91
|
+
static compare(left: EncryptionKey | Uint8Array | string, right: EncryptionKey | Uint8Array | string): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Import this key into Web Crypto for the given algorithm.
|
|
94
|
+
*
|
|
95
|
+
* @param algorithm
|
|
96
|
+
* @param usages
|
|
97
|
+
* @returns
|
|
98
|
+
*/
|
|
99
|
+
cryptoKey(algorithm?: AlgorithmIdentifier | AesKeyAlgorithm | HmacImportParams, usages?: KeyUsage[]): Promise<CryptoKey>;
|
|
100
|
+
/**
|
|
101
|
+
* A stable, shareable digest of this key. Safe to log or display; it does
|
|
102
|
+
* not reveal the key itself.
|
|
103
|
+
*
|
|
104
|
+
* @param options
|
|
105
|
+
* @returns
|
|
106
|
+
*/
|
|
107
|
+
fingerprint(options?: FingerprintOptions): Promise<string>;
|
|
108
|
+
/**
|
|
109
|
+
* Constant time comparison against another key.
|
|
110
|
+
*
|
|
111
|
+
* @param other
|
|
112
|
+
* @returns
|
|
113
|
+
*/
|
|
114
|
+
equals(other: EncryptionKey | Uint8Array | string): boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Key length in bytes.
|
|
117
|
+
*
|
|
118
|
+
* @returns
|
|
119
|
+
*/
|
|
120
|
+
get length(): number;
|
|
121
|
+
/**
|
|
122
|
+
* Base64url representation, the format used to persist and transport keys.
|
|
123
|
+
*
|
|
124
|
+
* @returns
|
|
125
|
+
*/
|
|
126
|
+
toBase64Url(): string;
|
|
127
|
+
/**
|
|
128
|
+
* Hex representation.
|
|
129
|
+
*
|
|
130
|
+
* @returns
|
|
131
|
+
*/
|
|
132
|
+
toHex(): string;
|
|
133
|
+
/**
|
|
134
|
+
* Base64url representation.
|
|
135
|
+
*
|
|
136
|
+
* @returns
|
|
137
|
+
*/
|
|
138
|
+
toString(): string;
|
|
139
|
+
/**
|
|
140
|
+
* Keep keys out of accidental `JSON.stringify` output of surrounding
|
|
141
|
+
* objects by requiring an explicit `toBase64Url()` call.
|
|
142
|
+
*
|
|
143
|
+
* @returns
|
|
144
|
+
*/
|
|
145
|
+
toJSON(): string;
|
|
146
|
+
/**
|
|
147
|
+
* Reduce a comparable key representation to bytes.
|
|
148
|
+
*
|
|
149
|
+
* @param value
|
|
150
|
+
* @returns
|
|
151
|
+
*/
|
|
152
|
+
private static materialize;
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/types.d.ts
|
|
156
|
+
/**
|
|
157
|
+
* Anything that can stand in for a symmetric key.
|
|
158
|
+
*
|
|
159
|
+
* - `EncryptionKey`: an already materialised key.
|
|
160
|
+
* - `Uint8Array`: raw key bytes (must match the cipher key length).
|
|
161
|
+
* - `CryptoKey`: a Web Crypto key, used as-is.
|
|
162
|
+
* - `string`: a base64url encoded key of the right length, otherwise treated as
|
|
163
|
+
* a passphrase and hashed with SHA-256 (matching Arkstack's `APP_KEY` behaviour).
|
|
164
|
+
*/
|
|
165
|
+
type KeyInput = EncryptionKey | Uint8Array | CryptoKey | string;
|
|
166
|
+
/**
|
|
167
|
+
* A serialised, transport safe key pair. Both halves are base64url strings:
|
|
168
|
+
* the public key is SPKI DER, the private key is PKCS#8 DER.
|
|
169
|
+
*/
|
|
170
|
+
interface SerializedKeyPair {
|
|
171
|
+
publicKey: string;
|
|
172
|
+
privateKey: string;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Optional per-operation cipher settings.
|
|
176
|
+
*/
|
|
177
|
+
interface CipherOptions {
|
|
178
|
+
/**
|
|
179
|
+
* Additional authenticated data. Not encrypted, but bound to the
|
|
180
|
+
* ciphertext: decryption fails unless the same value is supplied.
|
|
181
|
+
*/
|
|
182
|
+
aad?: Uint8Array | string;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Options for password based key derivation (PBKDF2-HMAC-SHA256).
|
|
186
|
+
*/
|
|
187
|
+
interface DeriveOptions {
|
|
188
|
+
/**
|
|
189
|
+
* Salt bytes or a base64url encoded salt. Generated when omitted.
|
|
190
|
+
*/
|
|
191
|
+
salt?: Uint8Array | string;
|
|
192
|
+
/**
|
|
193
|
+
* PBKDF2 iteration count. Defaults to 210,000 (OWASP 2023 guidance).
|
|
194
|
+
*/
|
|
195
|
+
iterations?: number;
|
|
196
|
+
/**
|
|
197
|
+
* Derived key length in bytes. Defaults to 32 (AES-256).
|
|
198
|
+
*/
|
|
199
|
+
length?: number;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The result of a password based derivation, including the salt needed to
|
|
203
|
+
* reproduce it.
|
|
204
|
+
*/
|
|
205
|
+
interface DerivedKey {
|
|
206
|
+
key: EncryptionKey;
|
|
207
|
+
salt: string;
|
|
208
|
+
iterations: number;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Options controlling how a shared secret is stretched into a channel key.
|
|
212
|
+
*/
|
|
213
|
+
interface ChannelOptions {
|
|
214
|
+
/**
|
|
215
|
+
* Domain separation string mixed into the HKDF `info` parameter. Two peers
|
|
216
|
+
* must use the same value to land on the same key.
|
|
217
|
+
*/
|
|
218
|
+
info?: string;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Options for rendering a key fingerprint.
|
|
222
|
+
*/
|
|
223
|
+
interface FingerprintOptions {
|
|
224
|
+
/**
|
|
225
|
+
* Number of digest bytes to render. Defaults to 32 (the full SHA-256).
|
|
226
|
+
*/
|
|
227
|
+
length?: number;
|
|
228
|
+
/**
|
|
229
|
+
* Output encoding. Defaults to `'hex'`.
|
|
230
|
+
*/
|
|
231
|
+
encoding?: 'hex' | 'base64url';
|
|
232
|
+
/**
|
|
233
|
+
* When set, group the output into blocks of this many characters,
|
|
234
|
+
* separated by spaces. Makes fingerprints readable for manual comparison.
|
|
235
|
+
*/
|
|
236
|
+
group?: number;
|
|
237
|
+
}
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/Cipher.d.ts
|
|
240
|
+
/**
|
|
241
|
+
* AES-256-GCM symmetric encryption built on the Web Crypto API.
|
|
242
|
+
*
|
|
243
|
+
* Payloads are colon delimited base64url triples — `<iv>:<authTag>:<ciphertext>`
|
|
244
|
+
* — which is byte for byte the format Arkstack has always written. A value
|
|
245
|
+
* encrypted by a Node server decrypts in the browser and vice versa, provided
|
|
246
|
+
* both sides hold the same key.
|
|
247
|
+
*/
|
|
248
|
+
declare class Cipher {
|
|
249
|
+
readonly key: EncryptionKey;
|
|
250
|
+
/** Initialisation vector length in bytes. */
|
|
251
|
+
static readonly ivLength = 12;
|
|
252
|
+
/** GCM authentication tag length in bytes. */
|
|
253
|
+
static readonly tagLength = 16;
|
|
254
|
+
/**
|
|
255
|
+
* @param key The symmetric key this cipher operates with.
|
|
256
|
+
*/
|
|
257
|
+
constructor(key: EncryptionKey);
|
|
258
|
+
/**
|
|
259
|
+
* Build a cipher from any accepted key representation.
|
|
260
|
+
*
|
|
261
|
+
* @param key
|
|
262
|
+
* @returns
|
|
263
|
+
*/
|
|
264
|
+
static from(key: KeyInput): Promise<Cipher>;
|
|
265
|
+
/**
|
|
266
|
+
* Build a cipher backed by a freshly generated random key.
|
|
267
|
+
*
|
|
268
|
+
* @returns
|
|
269
|
+
*/
|
|
270
|
+
static create(): Cipher;
|
|
271
|
+
/**
|
|
272
|
+
* Encrypt a string.
|
|
273
|
+
*
|
|
274
|
+
* @param value
|
|
275
|
+
* @param key
|
|
276
|
+
* @param options
|
|
277
|
+
* @returns
|
|
278
|
+
*/
|
|
279
|
+
static encrypt(value: string, key: KeyInput, options?: CipherOptions): Promise<string>;
|
|
280
|
+
/**
|
|
281
|
+
* Decrypt a payload produced by {@link encrypt}.
|
|
282
|
+
*
|
|
283
|
+
* @param payload
|
|
284
|
+
* @param key
|
|
285
|
+
* @param options
|
|
286
|
+
* @returns
|
|
287
|
+
*/
|
|
288
|
+
static decrypt(payload: string, key: KeyInput, options?: CipherOptions): Promise<string>;
|
|
289
|
+
/**
|
|
290
|
+
* Whether a string is shaped like a cipher payload. A cheap structural
|
|
291
|
+
* check, not an authenticity check.
|
|
292
|
+
*
|
|
293
|
+
* @param value
|
|
294
|
+
* @returns
|
|
295
|
+
*/
|
|
296
|
+
static looksLikePayload(value: unknown): value is string;
|
|
297
|
+
/**
|
|
298
|
+
* Encrypt a UTF-8 string.
|
|
299
|
+
*
|
|
300
|
+
* @param value
|
|
301
|
+
* @param options
|
|
302
|
+
* @returns
|
|
303
|
+
*/
|
|
304
|
+
encrypt(value: string, options?: CipherOptions): Promise<string>;
|
|
305
|
+
/**
|
|
306
|
+
* Decrypt a payload back into a UTF-8 string.
|
|
307
|
+
*
|
|
308
|
+
* @param payload
|
|
309
|
+
* @param options
|
|
310
|
+
* @returns
|
|
311
|
+
*/
|
|
312
|
+
decrypt(payload: string, options?: CipherOptions): Promise<string>;
|
|
313
|
+
/**
|
|
314
|
+
* Encrypt arbitrary bytes.
|
|
315
|
+
*
|
|
316
|
+
* @param bytes
|
|
317
|
+
* @param options
|
|
318
|
+
* @returns
|
|
319
|
+
*/
|
|
320
|
+
encryptBytes(bytes: Uint8Array, options?: CipherOptions): Promise<string>;
|
|
321
|
+
/**
|
|
322
|
+
* Decrypt a payload back into raw bytes.
|
|
323
|
+
*
|
|
324
|
+
* @param payload
|
|
325
|
+
* @param options
|
|
326
|
+
* @returns
|
|
327
|
+
*/
|
|
328
|
+
decryptBytes(payload: string, options?: CipherOptions): Promise<Uint8Array>;
|
|
329
|
+
/**
|
|
330
|
+
* Import the key once per cipher instance.
|
|
331
|
+
*
|
|
332
|
+
* @returns
|
|
333
|
+
*/
|
|
334
|
+
private cryptoKey;
|
|
335
|
+
/**
|
|
336
|
+
* Build the AES-GCM parameters for a single operation.
|
|
337
|
+
*
|
|
338
|
+
* @param iv
|
|
339
|
+
* @param options
|
|
340
|
+
* @returns
|
|
341
|
+
*/
|
|
342
|
+
private parameters;
|
|
343
|
+
private imported?;
|
|
344
|
+
}
|
|
345
|
+
//#endregion
|
|
346
|
+
//#region src/KeyPair.d.ts
|
|
347
|
+
/**
|
|
348
|
+
* An ECDH P-256 key pair — the identity half of end-to-end encryption.
|
|
349
|
+
*
|
|
350
|
+
* P-256 is the curve every mainstream Web Crypto implementation supports, so a
|
|
351
|
+
* key pair generated in Node imports cleanly in the browser and vice versa.
|
|
352
|
+
* Keys serialise to base64url DER (SPKI for public, PKCS#8 for private), which
|
|
353
|
+
* survives JSON, headers, query strings and database columns unchanged.
|
|
354
|
+
*/
|
|
355
|
+
declare class KeyPair {
|
|
356
|
+
readonly publicKey: CryptoKey;
|
|
357
|
+
readonly privateKey?: CryptoKey | undefined;
|
|
358
|
+
/**
|
|
359
|
+
* @param publicKey
|
|
360
|
+
* @param privateKey Absent for peer key pairs, where only the public half is known.
|
|
361
|
+
*/
|
|
362
|
+
constructor(publicKey: CryptoKey, privateKey?: CryptoKey | undefined);
|
|
363
|
+
/**
|
|
364
|
+
* Generate a new key pair.
|
|
365
|
+
*
|
|
366
|
+
* @returns
|
|
367
|
+
*/
|
|
368
|
+
static generate(): Promise<KeyPair>;
|
|
369
|
+
/**
|
|
370
|
+
* Restore a key pair from its serialised form.
|
|
371
|
+
*
|
|
372
|
+
* @param serialized
|
|
373
|
+
* @returns
|
|
374
|
+
*/
|
|
375
|
+
static import(serialized: SerializedKeyPair): Promise<KeyPair>;
|
|
376
|
+
/**
|
|
377
|
+
* Restore a full key pair from the private half alone; the public key is
|
|
378
|
+
* recovered from the private key's curve point.
|
|
379
|
+
*
|
|
380
|
+
* @param privateKey
|
|
381
|
+
* @returns
|
|
382
|
+
*/
|
|
383
|
+
static fromPrivateKey(privateKey: string | CryptoKey): Promise<KeyPair>;
|
|
384
|
+
/**
|
|
385
|
+
* Wrap a peer's public key. The result can verify fingerprints and receive
|
|
386
|
+
* sealed messages, but cannot derive shared secrets on its own.
|
|
387
|
+
*
|
|
388
|
+
* @param publicKey
|
|
389
|
+
* @returns
|
|
390
|
+
*/
|
|
391
|
+
static fromPublicKey(publicKey: string | CryptoKey): Promise<KeyPair>;
|
|
392
|
+
/**
|
|
393
|
+
* Import a base64url SPKI public key.
|
|
394
|
+
*
|
|
395
|
+
* @param publicKey
|
|
396
|
+
* @returns
|
|
397
|
+
*/
|
|
398
|
+
static importPublicKey(publicKey: string): Promise<CryptoKey>;
|
|
399
|
+
/**
|
|
400
|
+
* Import a base64url PKCS#8 private key.
|
|
401
|
+
*
|
|
402
|
+
* @param privateKey
|
|
403
|
+
* @returns
|
|
404
|
+
*/
|
|
405
|
+
static importPrivateKey(privateKey: string): Promise<CryptoKey>;
|
|
406
|
+
/**
|
|
407
|
+
* Export a public key to its base64url SPKI form.
|
|
408
|
+
*
|
|
409
|
+
* @param publicKey
|
|
410
|
+
* @returns
|
|
411
|
+
*/
|
|
412
|
+
static exportPublicKey(publicKey: CryptoKey): Promise<string>;
|
|
413
|
+
/**
|
|
414
|
+
* Derive raw ECDH shared bits between a private key and a peer public key.
|
|
415
|
+
*
|
|
416
|
+
* The result is the raw curve point and must be stretched with a KDF before
|
|
417
|
+
* use as a cipher key — {@link SecureChannel} does that for you.
|
|
418
|
+
*
|
|
419
|
+
* @param privateKey
|
|
420
|
+
* @param peerPublicKey
|
|
421
|
+
* @param length Output length in bits, defaults to the P-256 field size.
|
|
422
|
+
* @returns
|
|
423
|
+
*/
|
|
424
|
+
static sharedBits(privateKey: CryptoKey, peerPublicKey: CryptoKey, length?: number): Promise<Uint8Array>;
|
|
425
|
+
/**
|
|
426
|
+
* A human comparable digest of a public key. Two peers reading the same
|
|
427
|
+
* fingerprint aloud are holding the same key.
|
|
428
|
+
*
|
|
429
|
+
* @param publicKey
|
|
430
|
+
* @param options
|
|
431
|
+
* @returns
|
|
432
|
+
*/
|
|
433
|
+
static fingerprintOf(publicKey: string | CryptoKey, options?: FingerprintOptions): Promise<string>;
|
|
434
|
+
/**
|
|
435
|
+
* The digest of both participants' public keys, ordered deterministically
|
|
436
|
+
* so each side computes the same value. Rendered as five digit groups in
|
|
437
|
+
* the style of a messaging app's safety number.
|
|
438
|
+
*
|
|
439
|
+
* @param first
|
|
440
|
+
* @param second
|
|
441
|
+
* @param groups How many five digit groups to render, defaults to 12.
|
|
442
|
+
* @returns
|
|
443
|
+
*/
|
|
444
|
+
static safetyNumber(first: string, second: string, groups?: number): Promise<string>;
|
|
445
|
+
/**
|
|
446
|
+
* Order two public keys deterministically so both peers derive identical
|
|
447
|
+
* salts and safety numbers regardless of who initiated.
|
|
448
|
+
*
|
|
449
|
+
* @param first
|
|
450
|
+
* @param second
|
|
451
|
+
* @returns
|
|
452
|
+
*/
|
|
453
|
+
static order(first: string, second: string): [string, string];
|
|
454
|
+
/**
|
|
455
|
+
* Whether the private half is available.
|
|
456
|
+
*
|
|
457
|
+
* @returns
|
|
458
|
+
*/
|
|
459
|
+
get isComplete(): boolean;
|
|
460
|
+
/**
|
|
461
|
+
* Serialise both halves. Throws when the private key is missing.
|
|
462
|
+
*
|
|
463
|
+
* @returns
|
|
464
|
+
*/
|
|
465
|
+
export(): Promise<SerializedKeyPair>;
|
|
466
|
+
/**
|
|
467
|
+
* The base64url SPKI public key, safe to publish.
|
|
468
|
+
*
|
|
469
|
+
* @returns
|
|
470
|
+
*/
|
|
471
|
+
exportPublicKey(): Promise<string>;
|
|
472
|
+
/**
|
|
473
|
+
* Derive the raw ECDH shared bits with a peer.
|
|
474
|
+
*
|
|
475
|
+
* @param peerPublicKey
|
|
476
|
+
* @returns
|
|
477
|
+
*/
|
|
478
|
+
sharedBits(peerPublicKey: string | CryptoKey | KeyPair): Promise<Uint8Array>;
|
|
479
|
+
/**
|
|
480
|
+
* A comparable digest of this key pair's public key.
|
|
481
|
+
*
|
|
482
|
+
* @param options
|
|
483
|
+
* @returns
|
|
484
|
+
*/
|
|
485
|
+
fingerprint(options?: FingerprintOptions): Promise<string>;
|
|
486
|
+
/**
|
|
487
|
+
* Normalise anything that can stand in for a public key.
|
|
488
|
+
*
|
|
489
|
+
* @param value
|
|
490
|
+
* @returns
|
|
491
|
+
*/
|
|
492
|
+
static resolvePublic(value: string | CryptoKey | KeyPair): Promise<CryptoKey>;
|
|
493
|
+
}
|
|
494
|
+
//#endregion
|
|
495
|
+
//#region src/Keys.d.ts
|
|
496
|
+
/**
|
|
497
|
+
* Key generation and comparison helpers.
|
|
498
|
+
*
|
|
499
|
+
* Generating keys is easy to get wrong quietly and comparing them is easy to
|
|
500
|
+
* get wrong dangerously, so both live here: every comparison in this class runs
|
|
501
|
+
* in constant time, and every generator draws from the platform CSPRNG.
|
|
502
|
+
*/
|
|
503
|
+
declare class Keys {
|
|
504
|
+
/**
|
|
505
|
+
* Generate a random symmetric key.
|
|
506
|
+
*
|
|
507
|
+
* @param length Key length in bytes, defaults to 32 (AES-256).
|
|
508
|
+
* @returns
|
|
509
|
+
*/
|
|
510
|
+
static generate(length?: number): EncryptionKey;
|
|
511
|
+
/**
|
|
512
|
+
* Generate a random symmetric key as a base64url string, ready to store in
|
|
513
|
+
* an environment variable or a database column.
|
|
514
|
+
*
|
|
515
|
+
* @param length
|
|
516
|
+
* @returns
|
|
517
|
+
*/
|
|
518
|
+
static generateString(length?: number): string;
|
|
519
|
+
/**
|
|
520
|
+
* Generate a random, URL safe token. Not a key — use it for invites,
|
|
521
|
+
* one-time links and other opaque identifiers.
|
|
522
|
+
*
|
|
523
|
+
* @param bytes
|
|
524
|
+
* @returns
|
|
525
|
+
*/
|
|
526
|
+
static token(bytes?: number): string;
|
|
527
|
+
/**
|
|
528
|
+
* Generate an end-to-end encryption identity: an ECDH key pair whose public
|
|
529
|
+
* half is published and whose private half never leaves its owner.
|
|
530
|
+
*
|
|
531
|
+
* @returns
|
|
532
|
+
*/
|
|
533
|
+
static generatePair(): Promise<KeyPair>;
|
|
534
|
+
/**
|
|
535
|
+
* Generate an identity and return it already serialised for storage or
|
|
536
|
+
* transport.
|
|
537
|
+
*
|
|
538
|
+
* @returns
|
|
539
|
+
*/
|
|
540
|
+
static generateSerializedPair(): Promise<SerializedKeyPair>;
|
|
541
|
+
/**
|
|
542
|
+
* Hash an arbitrary secret into a key with SHA-256, the same way Arkstack
|
|
543
|
+
* turns `APP_KEY` into a cipher key.
|
|
544
|
+
*
|
|
545
|
+
* @param secret
|
|
546
|
+
* @returns
|
|
547
|
+
*/
|
|
548
|
+
static fromSecret(secret: string): Promise<EncryptionKey>;
|
|
549
|
+
/**
|
|
550
|
+
* Stretch a user supplied password into a key with PBKDF2-HMAC-SHA256.
|
|
551
|
+
*
|
|
552
|
+
* @param password
|
|
553
|
+
* @param options
|
|
554
|
+
* @returns
|
|
555
|
+
*/
|
|
556
|
+
static derive(password: string, options?: DeriveOptions): Promise<DerivedKey>;
|
|
557
|
+
/**
|
|
558
|
+
* Constant time comparison of two keys already in key form.
|
|
559
|
+
*
|
|
560
|
+
* @param left
|
|
561
|
+
* @param right
|
|
562
|
+
* @returns
|
|
563
|
+
*/
|
|
564
|
+
static compare(left: EncryptionKey | Uint8Array | string, right: EncryptionKey | Uint8Array | string): boolean;
|
|
565
|
+
/**
|
|
566
|
+
* Constant time comparison that first resolves both sides through the same
|
|
567
|
+
* rules the ciphers use, so a passphrase can be checked against the key it
|
|
568
|
+
* produces.
|
|
569
|
+
*
|
|
570
|
+
* @param left
|
|
571
|
+
* @param right
|
|
572
|
+
* @param length Expected key length in bytes.
|
|
573
|
+
* @returns
|
|
574
|
+
*/
|
|
575
|
+
static matches(left: KeyInput, right: KeyInput, length?: number): Promise<boolean>;
|
|
576
|
+
/**
|
|
577
|
+
* A displayable digest of a symmetric key.
|
|
578
|
+
*
|
|
579
|
+
* @param key
|
|
580
|
+
* @param options
|
|
581
|
+
* @returns
|
|
582
|
+
*/
|
|
583
|
+
static fingerprint(key: KeyInput, options?: FingerprintOptions): Promise<string>;
|
|
584
|
+
/**
|
|
585
|
+
* A displayable digest of a public key, for comparing identities.
|
|
586
|
+
*
|
|
587
|
+
* @param publicKey
|
|
588
|
+
* @param options
|
|
589
|
+
* @returns
|
|
590
|
+
*/
|
|
591
|
+
static fingerprintPublicKey(publicKey: string | CryptoKey, options?: FingerprintOptions): Promise<string>;
|
|
592
|
+
/**
|
|
593
|
+
* The safety number for a conversation between two public keys. Both peers
|
|
594
|
+
* compute the same string; showing it side by side proves no third party
|
|
595
|
+
* substituted a key in transit.
|
|
596
|
+
*
|
|
597
|
+
* @param first
|
|
598
|
+
* @param second
|
|
599
|
+
* @param groups
|
|
600
|
+
* @returns
|
|
601
|
+
*/
|
|
602
|
+
static safetyNumber(first: string, second: string, groups?: number): Promise<string>;
|
|
603
|
+
/**
|
|
604
|
+
* Confirm a safety number a user read out or scanned, in constant time.
|
|
605
|
+
*
|
|
606
|
+
* @param first
|
|
607
|
+
* @param second
|
|
608
|
+
* @param expected
|
|
609
|
+
* @returns
|
|
610
|
+
*/
|
|
611
|
+
static confirmSafetyNumber(first: string, second: string, expected: string): Promise<boolean>;
|
|
612
|
+
/**
|
|
613
|
+
* Whether two public keys refer to the same identity.
|
|
614
|
+
*
|
|
615
|
+
* @param left
|
|
616
|
+
* @param right
|
|
617
|
+
* @returns
|
|
618
|
+
*/
|
|
619
|
+
static samePublicKey(left: string | CryptoKey | KeyPair, right: string | CryptoKey | KeyPair): Promise<boolean>;
|
|
620
|
+
}
|
|
621
|
+
//#endregion
|
|
622
|
+
//#region src/SealedBox.d.ts
|
|
623
|
+
/**
|
|
624
|
+
* Anonymous encryption to a public key.
|
|
625
|
+
*
|
|
626
|
+
* The sender needs no identity of their own: a throwaway key pair is generated
|
|
627
|
+
* per message, agreed with the recipient's public key over ECDH, and its public
|
|
628
|
+
* half is carried in the payload so the recipient can reproduce the secret.
|
|
629
|
+
* Only the holder of the matching private key can open the result — including
|
|
630
|
+
* the sender, who cannot decrypt their own message afterwards.
|
|
631
|
+
*
|
|
632
|
+
* Payloads look like `ark1:<ephemeralPublicKey>:<iv>:<authTag>:<ciphertext>`.
|
|
633
|
+
*/
|
|
634
|
+
declare class SealedBox {
|
|
635
|
+
/** Payload discriminator. */
|
|
636
|
+
static readonly prefix = "ark1";
|
|
637
|
+
/**
|
|
638
|
+
* Encrypt a message to a recipient's public key.
|
|
639
|
+
*
|
|
640
|
+
* @param message
|
|
641
|
+
* @param recipientPublicKey
|
|
642
|
+
* @param options
|
|
643
|
+
* @returns
|
|
644
|
+
*/
|
|
645
|
+
static seal(message: string, recipientPublicKey: string | CryptoKey | KeyPair, options?: CipherOptions): Promise<string>;
|
|
646
|
+
/**
|
|
647
|
+
* Open a sealed payload with the recipient's private key.
|
|
648
|
+
*
|
|
649
|
+
* @param payload
|
|
650
|
+
* @param recipientPrivateKey
|
|
651
|
+
* @param options
|
|
652
|
+
* @returns
|
|
653
|
+
*/
|
|
654
|
+
static open(payload: string, recipientPrivateKey: string | CryptoKey | KeyPair, options?: CipherOptions): Promise<string>;
|
|
655
|
+
/**
|
|
656
|
+
* Whether a string is shaped like a sealed payload.
|
|
657
|
+
*
|
|
658
|
+
* @param value
|
|
659
|
+
* @returns
|
|
660
|
+
*/
|
|
661
|
+
static looksLikePayload(value: unknown): value is string;
|
|
662
|
+
/**
|
|
663
|
+
* Derive the one-off message key. Both sides feed the same ordered pair of
|
|
664
|
+
* public keys into the salt, so sender and recipient agree.
|
|
665
|
+
*
|
|
666
|
+
* @param owner The side holding a private key.
|
|
667
|
+
* @param peer The other side's public key.
|
|
668
|
+
* @param ephemeralPublicKey
|
|
669
|
+
* @param recipientPublicKey
|
|
670
|
+
* @returns
|
|
671
|
+
*/
|
|
672
|
+
private static derive;
|
|
673
|
+
}
|
|
674
|
+
//#endregion
|
|
675
|
+
//#region src/SecureChannel.d.ts
|
|
676
|
+
/**
|
|
677
|
+
* A two party end-to-end encrypted channel.
|
|
678
|
+
*
|
|
679
|
+
* Each side combines its own private key with the other side's public key over
|
|
680
|
+
* ECDH, stretches the result with HKDF-SHA256, and ends up holding the exact
|
|
681
|
+
* same AES-256-GCM key without that key ever crossing the wire. Messages
|
|
682
|
+
* encrypted by either peer — in Node or in a browser — decrypt on the other.
|
|
683
|
+
*
|
|
684
|
+
* ```ts
|
|
685
|
+
* const alice = await KeyPair.generate()
|
|
686
|
+
* const bob = await KeyPair.generate()
|
|
687
|
+
*
|
|
688
|
+
* const outbound = await SecureChannel.between(alice, await bob.exportPublicKey())
|
|
689
|
+
* const inbound = await SecureChannel.between(bob, await alice.exportPublicKey())
|
|
690
|
+
*
|
|
691
|
+
* await inbound.decrypt(await outbound.encrypt('hey')) // 'hey'
|
|
692
|
+
* ```
|
|
693
|
+
*/
|
|
694
|
+
declare class SecureChannel {
|
|
695
|
+
readonly cipher: Cipher;
|
|
696
|
+
readonly localPublicKey: string;
|
|
697
|
+
readonly remotePublicKey: string;
|
|
698
|
+
/**
|
|
699
|
+
* @param cipher The cipher bound to the derived shared key.
|
|
700
|
+
* @param localPublicKey This side's public key, base64url.
|
|
701
|
+
* @param remotePublicKey The peer's public key, base64url.
|
|
702
|
+
*/
|
|
703
|
+
private constructor();
|
|
704
|
+
/**
|
|
705
|
+
* Open a channel between a local key pair (or private key) and a peer's
|
|
706
|
+
* public key.
|
|
707
|
+
*
|
|
708
|
+
* @param local
|
|
709
|
+
* @param peerPublicKey
|
|
710
|
+
* @param options
|
|
711
|
+
* @returns
|
|
712
|
+
*/
|
|
713
|
+
static between(local: KeyPair | string | CryptoKey, peerPublicKey: KeyPair | string | CryptoKey, options?: ChannelOptions): Promise<SecureChannel>;
|
|
714
|
+
/**
|
|
715
|
+
* The HKDF salt for a pair of participants: a digest over both public keys
|
|
716
|
+
* in a deterministic order, so both sides compute the same value.
|
|
717
|
+
*
|
|
718
|
+
* @param first
|
|
719
|
+
* @param second
|
|
720
|
+
* @returns
|
|
721
|
+
*/
|
|
722
|
+
static salt(first: string, second: string): Promise<Uint8Array>;
|
|
723
|
+
/**
|
|
724
|
+
* The shared key both peers derived. Persist it only if you intend to skip
|
|
725
|
+
* the handshake later; it is as sensitive as the messages themselves.
|
|
726
|
+
*
|
|
727
|
+
* @returns
|
|
728
|
+
*/
|
|
729
|
+
get key(): EncryptionKey;
|
|
730
|
+
/**
|
|
731
|
+
* Encrypt a message for the peer.
|
|
732
|
+
*
|
|
733
|
+
* @param message
|
|
734
|
+
* @param options
|
|
735
|
+
* @returns
|
|
736
|
+
*/
|
|
737
|
+
encrypt(message: string, options?: CipherOptions): Promise<string>;
|
|
738
|
+
/**
|
|
739
|
+
* Decrypt a message from the peer.
|
|
740
|
+
*
|
|
741
|
+
* @param payload
|
|
742
|
+
* @param options
|
|
743
|
+
* @returns
|
|
744
|
+
*/
|
|
745
|
+
decrypt(payload: string, options?: CipherOptions): Promise<string>;
|
|
746
|
+
/**
|
|
747
|
+
* Fingerprint of the derived shared key. Identical on both sides, and the
|
|
748
|
+
* cheapest way to assert two peers really did agree on the same secret.
|
|
749
|
+
*
|
|
750
|
+
* @param options
|
|
751
|
+
* @returns
|
|
752
|
+
*/
|
|
753
|
+
fingerprint(options?: FingerprintOptions): Promise<string>;
|
|
754
|
+
/**
|
|
755
|
+
* The conversation's safety number: show it to both participants so they
|
|
756
|
+
* can confirm out of band that nobody is sitting in the middle.
|
|
757
|
+
*
|
|
758
|
+
* @param groups
|
|
759
|
+
* @returns
|
|
760
|
+
*/
|
|
761
|
+
safetyNumber(groups?: number): Promise<string>;
|
|
762
|
+
}
|
|
763
|
+
//#endregion
|
|
764
|
+
//#region src/support/codec.d.ts
|
|
765
|
+
/**
|
|
766
|
+
* Runtime agnostic binary/text conversion helpers.
|
|
767
|
+
*
|
|
768
|
+
* Everything here is implemented against `Uint8Array`, `TextEncoder` and
|
|
769
|
+
* `TextDecoder` so the exact same code path runs in Node, Deno, Bun, browsers
|
|
770
|
+
* and workers. No `Buffer`, no `node:crypto`.
|
|
771
|
+
*/
|
|
772
|
+
declare class Codec {
|
|
773
|
+
private static readonly encoder;
|
|
774
|
+
private static readonly decoder;
|
|
775
|
+
/**
|
|
776
|
+
* Encode a UTF-8 string to bytes.
|
|
777
|
+
*
|
|
778
|
+
* @param value
|
|
779
|
+
* @returns
|
|
780
|
+
*/
|
|
781
|
+
static encodeUtf8(value: string): Uint8Array;
|
|
782
|
+
/**
|
|
783
|
+
* Decode bytes back to a UTF-8 string.
|
|
784
|
+
*
|
|
785
|
+
* @param bytes
|
|
786
|
+
* @returns
|
|
787
|
+
*/
|
|
788
|
+
static decodeUtf8(bytes: Uint8Array): string;
|
|
789
|
+
/**
|
|
790
|
+
* Encode bytes as standard (padded) base64.
|
|
791
|
+
*
|
|
792
|
+
* @param bytes
|
|
793
|
+
* @returns
|
|
794
|
+
*/
|
|
795
|
+
static encodeBase64(bytes: Uint8Array): string;
|
|
796
|
+
/**
|
|
797
|
+
* Decode standard (padded or unpadded) base64 to bytes.
|
|
798
|
+
*
|
|
799
|
+
* @param value
|
|
800
|
+
* @returns
|
|
801
|
+
*/
|
|
802
|
+
static decodeBase64(value: string): Uint8Array;
|
|
803
|
+
/**
|
|
804
|
+
* Encode bytes as unpadded base64url, the wire format used by every
|
|
805
|
+
* Arkstack encryption payload.
|
|
806
|
+
*
|
|
807
|
+
* @param bytes
|
|
808
|
+
* @returns
|
|
809
|
+
*/
|
|
810
|
+
static encodeBase64Url(bytes: Uint8Array): string;
|
|
811
|
+
/**
|
|
812
|
+
* Decode a base64url string to bytes.
|
|
813
|
+
*
|
|
814
|
+
* @param value
|
|
815
|
+
* @returns
|
|
816
|
+
*/
|
|
817
|
+
static decodeBase64Url(value: string): Uint8Array;
|
|
818
|
+
/**
|
|
819
|
+
* Encode bytes as lowercase hex.
|
|
820
|
+
*
|
|
821
|
+
* @param bytes
|
|
822
|
+
* @returns
|
|
823
|
+
*/
|
|
824
|
+
static encodeHex(bytes: Uint8Array): string;
|
|
825
|
+
/**
|
|
826
|
+
* Decode a hex string to bytes.
|
|
827
|
+
*
|
|
828
|
+
* @param value
|
|
829
|
+
* @returns
|
|
830
|
+
*/
|
|
831
|
+
static decodeHex(value: string): Uint8Array;
|
|
832
|
+
/**
|
|
833
|
+
* Concatenate byte sequences into a single buffer.
|
|
834
|
+
*
|
|
835
|
+
* @param parts
|
|
836
|
+
* @returns
|
|
837
|
+
*/
|
|
838
|
+
static concat(...parts: Uint8Array[]): Uint8Array;
|
|
839
|
+
/**
|
|
840
|
+
* Compare two byte sequences without leaking their contents through timing.
|
|
841
|
+
*
|
|
842
|
+
* The length check is intentionally not constant time; key and digest
|
|
843
|
+
* lengths are public information.
|
|
844
|
+
*
|
|
845
|
+
* @param left
|
|
846
|
+
* @param right
|
|
847
|
+
* @returns
|
|
848
|
+
*/
|
|
849
|
+
static equals(left: Uint8Array, right: Uint8Array): boolean;
|
|
850
|
+
/**
|
|
851
|
+
* Normalize a `Uint8Array`, `ArrayBuffer` or `ArrayBufferView` to bytes.
|
|
852
|
+
*
|
|
853
|
+
* @param value
|
|
854
|
+
* @returns
|
|
855
|
+
*/
|
|
856
|
+
static toBytes(value: ArrayBuffer | ArrayBufferView): Uint8Array;
|
|
857
|
+
/**
|
|
858
|
+
* Restore base64 padding stripped by the base64url encoding.
|
|
859
|
+
*
|
|
860
|
+
* @param value
|
|
861
|
+
* @returns
|
|
862
|
+
*/
|
|
863
|
+
private static pad;
|
|
864
|
+
/**
|
|
865
|
+
* Pure JS base64 encoder used when `btoa` is unavailable.
|
|
866
|
+
*
|
|
867
|
+
* @param bytes
|
|
868
|
+
* @returns
|
|
869
|
+
*/
|
|
870
|
+
private static fallbackEncodeBase64;
|
|
871
|
+
/**
|
|
872
|
+
* Pure JS base64 decoder used when `atob` is unavailable.
|
|
873
|
+
*
|
|
874
|
+
* @param value
|
|
875
|
+
* @returns
|
|
876
|
+
*/
|
|
877
|
+
private static fallbackDecodeBase64;
|
|
878
|
+
}
|
|
879
|
+
//#endregion
|
|
880
|
+
//#region src/support/subtle.d.ts
|
|
881
|
+
/**
|
|
882
|
+
* Resolve the ambient Web Crypto implementation.
|
|
883
|
+
*
|
|
884
|
+
* Node exposes it as `globalThis.crypto` from v19 (and behind
|
|
885
|
+
* `node:crypto`'s `webcrypto` export from v15), browsers and workers expose it
|
|
886
|
+
* on `window`/`self`. Secure contexts are required in browsers, hence the
|
|
887
|
+
* explicit error message.
|
|
888
|
+
*
|
|
889
|
+
* @returns
|
|
890
|
+
*/
|
|
891
|
+
declare const webCrypto: () => Crypto;
|
|
892
|
+
/**
|
|
893
|
+
* Resolve `crypto.subtle`.
|
|
894
|
+
*
|
|
895
|
+
* @returns
|
|
896
|
+
*/
|
|
897
|
+
declare const subtle: () => SubtleCrypto;
|
|
898
|
+
/**
|
|
899
|
+
* Fill a buffer with cryptographically secure random bytes.
|
|
900
|
+
*
|
|
901
|
+
* @param length
|
|
902
|
+
* @returns
|
|
903
|
+
*/
|
|
904
|
+
declare const randomBytes: (length: number) => Uint8Array;
|
|
905
|
+
/**
|
|
906
|
+
* SHA digest helper returning bytes instead of an `ArrayBuffer`.
|
|
907
|
+
*
|
|
908
|
+
* @param data
|
|
909
|
+
* @param algorithm
|
|
910
|
+
* @returns
|
|
911
|
+
*/
|
|
912
|
+
declare const digest: (data: Uint8Array, algorithm?: "SHA-256" | "SHA-384" | "SHA-512") => Promise<Uint8Array>;
|
|
913
|
+
//#endregion
|
|
914
|
+
export { ChannelOptions, Cipher, CipherOptions, Codec, DeriveOptions, DerivedKey, EncryptionKey, FingerprintOptions, KeyInput, KeyPair, Keys, SealedBox, SecureChannel, SerializedKeyPair, digest, randomBytes, subtle, webCrypto };
|