@syncular/core 0.63.0 → 0.65.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/dist/crypto.d.ts CHANGED
@@ -10,14 +10,25 @@ export declare const NONCE_LENGTH = 12;
10
10
  export declare const KEY_LENGTH = 32;
11
11
  /**
12
12
  * Client-local decrypt failure (SPEC.md §5.11, §10.3). Never on the wire —
13
- * the `client.` family is client-only. Raised at the apply seam for an
14
- * unknown envelope version, unknown `keyId`, GCM auth failure (wrong key), a
15
- * malformed envelope, or a post-decrypt value-parse failure. Not retryable.
13
+ * the `client.` family is client-only. Raised at the apply seam. A `keyId`
14
+ * the keyring does not hold is `client.encryption_key_missing` with
15
+ * `details` naming the key id (and the table at the apply seam); an unknown
16
+ * envelope version, GCM auth failure (wrong key), a malformed envelope, or a
17
+ * post-decrypt value-parse failure is `client.decrypt_failed`. Not
18
+ * retryable.
16
19
  */
17
20
  export declare class DecryptError extends Error {
18
21
  readonly name = "DecryptError";
19
- readonly code = "client.decrypt_failed";
22
+ readonly code: 'client.decrypt_failed' | 'client.encryption_key_missing';
20
23
  readonly retryable = false;
24
+ readonly details: {
25
+ readonly table?: string | undefined;
26
+ readonly keyId: string;
27
+ } | undefined;
28
+ constructor(message: string, missingKey?: {
29
+ readonly table?: string | undefined;
30
+ readonly keyId: string;
31
+ });
21
32
  }
22
33
  /**
23
34
  * Client-local encrypt failure (SPEC.md §5.11, §10.3). Never on the wire.
@@ -65,7 +76,9 @@ export declare function decodeEnvelope(bytes: Uint8Array): Envelope;
65
76
  export declare function encryptValue(declaredType: DeclaredType, value: PlainValue, keyId: string, key: Uint8Array, nonceSource?: NonceSource): Promise<Uint8Array>;
66
77
  /**
67
78
  * Decrypt a §5.11 envelope back to a declared-type value. `keyProvider`
68
- * resolves the envelope's `keyId` to key bytes; a missing key or GCM tag
69
- * mismatch is {@link DecryptError} (`client.decrypt_failed`).
79
+ * resolves the envelope's `keyId` to key bytes. A missing key is a
80
+ * {@link DecryptError} coded `client.encryption_key_missing` whose details
81
+ * name the key id and, when given, `table`; a GCM tag mismatch is
82
+ * `client.decrypt_failed`.
70
83
  */
71
- export declare function decryptValue(declaredType: DeclaredType, envelopeBytes: Uint8Array, keyProvider: (keyId: string) => Uint8Array | undefined): Promise<PlainValue>;
84
+ export declare function decryptValue(declaredType: DeclaredType, envelopeBytes: Uint8Array, keyProvider: (keyId: string) => Uint8Array | undefined, table?: string): Promise<PlainValue>;
package/dist/crypto.js CHANGED
@@ -22,14 +22,25 @@ export const NONCE_LENGTH = 12;
22
22
  export const KEY_LENGTH = 32;
23
23
  /**
24
24
  * Client-local decrypt failure (SPEC.md §5.11, §10.3). Never on the wire —
25
- * the `client.` family is client-only. Raised at the apply seam for an
26
- * unknown envelope version, unknown `keyId`, GCM auth failure (wrong key), a
27
- * malformed envelope, or a post-decrypt value-parse failure. Not retryable.
25
+ * the `client.` family is client-only. Raised at the apply seam. A `keyId`
26
+ * the keyring does not hold is `client.encryption_key_missing` with
27
+ * `details` naming the key id (and the table at the apply seam); an unknown
28
+ * envelope version, GCM auth failure (wrong key), a malformed envelope, or a
29
+ * post-decrypt value-parse failure is `client.decrypt_failed`. Not
30
+ * retryable.
28
31
  */
29
32
  export class DecryptError extends Error {
30
33
  name = 'DecryptError';
31
- code = 'client.decrypt_failed';
34
+ code;
32
35
  retryable = false;
36
+ details;
37
+ constructor(message, missingKey) {
38
+ super(message);
39
+ this.code = missingKey
40
+ ? 'client.encryption_key_missing'
41
+ : 'client.decrypt_failed';
42
+ this.details = missingKey;
43
+ }
33
44
  }
34
45
  /**
35
46
  * Client-local encrypt failure (SPEC.md §5.11, §10.3). Never on the wire.
@@ -240,14 +251,19 @@ export async function encryptValue(declaredType, value, keyId, key, nonceSource
240
251
  }
241
252
  /**
242
253
  * Decrypt a §5.11 envelope back to a declared-type value. `keyProvider`
243
- * resolves the envelope's `keyId` to key bytes; a missing key or GCM tag
244
- * mismatch is {@link DecryptError} (`client.decrypt_failed`).
254
+ * resolves the envelope's `keyId` to key bytes. A missing key is a
255
+ * {@link DecryptError} coded `client.encryption_key_missing` whose details
256
+ * name the key id and, when given, `table`; a GCM tag mismatch is
257
+ * `client.decrypt_failed`.
245
258
  */
246
- export async function decryptValue(declaredType, envelopeBytes, keyProvider) {
259
+ export async function decryptValue(declaredType, envelopeBytes, keyProvider, table) {
247
260
  const envelope = decodeEnvelope(envelopeBytes);
248
261
  const key = keyProvider(envelope.keyId);
249
262
  if (key === undefined) {
250
- throw new DecryptError(`no key for keyId ${JSON.stringify(envelope.keyId)}`);
263
+ throw new DecryptError('no key for the envelope keyId', {
264
+ table,
265
+ keyId: envelope.keyId,
266
+ });
251
267
  }
252
268
  const cryptoKey = await importAesKey(key);
253
269
  let plaintext;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncular/core",
3
- "version": "0.63.0",
3
+ "version": "0.65.0",
4
4
  "description": "Core protocol types and shared utilities for the Syncular sync framework",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Benjamin Kniffler",
package/src/crypto.ts CHANGED
@@ -39,14 +39,34 @@ export const KEY_LENGTH = 32;
39
39
 
40
40
  /**
41
41
  * Client-local decrypt failure (SPEC.md §5.11, §10.3). Never on the wire —
42
- * the `client.` family is client-only. Raised at the apply seam for an
43
- * unknown envelope version, unknown `keyId`, GCM auth failure (wrong key), a
44
- * malformed envelope, or a post-decrypt value-parse failure. Not retryable.
42
+ * the `client.` family is client-only. Raised at the apply seam. A `keyId`
43
+ * the keyring does not hold is `client.encryption_key_missing` with
44
+ * `details` naming the key id (and the table at the apply seam); an unknown
45
+ * envelope version, GCM auth failure (wrong key), a malformed envelope, or a
46
+ * post-decrypt value-parse failure is `client.decrypt_failed`. Not
47
+ * retryable.
45
48
  */
46
49
  export class DecryptError extends Error {
47
50
  override readonly name = 'DecryptError';
48
- readonly code = 'client.decrypt_failed';
51
+ readonly code: 'client.decrypt_failed' | 'client.encryption_key_missing';
49
52
  readonly retryable = false;
53
+ readonly details:
54
+ | { readonly table?: string | undefined; readonly keyId: string }
55
+ | undefined;
56
+
57
+ constructor(
58
+ message: string,
59
+ missingKey?: {
60
+ readonly table?: string | undefined;
61
+ readonly keyId: string;
62
+ },
63
+ ) {
64
+ super(message);
65
+ this.code = missingKey
66
+ ? 'client.encryption_key_missing'
67
+ : 'client.decrypt_failed';
68
+ this.details = missingKey;
69
+ }
50
70
  }
51
71
 
52
72
  /**
@@ -313,20 +333,24 @@ export async function encryptValue(
313
333
 
314
334
  /**
315
335
  * Decrypt a §5.11 envelope back to a declared-type value. `keyProvider`
316
- * resolves the envelope's `keyId` to key bytes; a missing key or GCM tag
317
- * mismatch is {@link DecryptError} (`client.decrypt_failed`).
336
+ * resolves the envelope's `keyId` to key bytes. A missing key is a
337
+ * {@link DecryptError} coded `client.encryption_key_missing` whose details
338
+ * name the key id and, when given, `table`; a GCM tag mismatch is
339
+ * `client.decrypt_failed`.
318
340
  */
319
341
  export async function decryptValue(
320
342
  declaredType: DeclaredType,
321
343
  envelopeBytes: Uint8Array,
322
344
  keyProvider: (keyId: string) => Uint8Array | undefined,
345
+ table?: string,
323
346
  ): Promise<PlainValue> {
324
347
  const envelope = decodeEnvelope(envelopeBytes);
325
348
  const key = keyProvider(envelope.keyId);
326
349
  if (key === undefined) {
327
- throw new DecryptError(
328
- `no key for keyId ${JSON.stringify(envelope.keyId)}`,
329
- );
350
+ throw new DecryptError('no key for the envelope keyId', {
351
+ table,
352
+ keyId: envelope.keyId,
353
+ });
330
354
  }
331
355
  const cryptoKey = await importAesKey(key);
332
356
  let plaintext: Uint8Array;