@request-seal/core 0.1.0 → 0.2.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +341 -114
  3. package/dist/accept-signature.d.ts +11 -0
  4. package/dist/accept-signature.js +7 -0
  5. package/dist/adapters/fetch/index.d.ts +10 -0
  6. package/dist/adapters/fetch/index.js +21 -4
  7. package/dist/adapters/fetch/runtime.d.ts +1 -0
  8. package/dist/adapters/fetch/runtime.js +1 -0
  9. package/dist/adapters/node-http/index.d.ts +16 -0
  10. package/dist/adapters/node-http/index.js +8 -4
  11. package/dist/brand.d.ts +5 -0
  12. package/dist/brand.js +12 -0
  13. package/dist/bytes.d.ts +4 -0
  14. package/dist/bytes.js +5 -0
  15. package/dist/constants.d.ts +61 -1
  16. package/dist/constants.js +61 -1
  17. package/dist/context.d.ts +4 -0
  18. package/dist/context.js +3 -0
  19. package/dist/crypto/algorithm.d.ts +5 -0
  20. package/dist/crypto/index.d.ts +12 -2
  21. package/dist/crypto/index.js +34 -11
  22. package/dist/crypto/options.d.ts +3 -0
  23. package/dist/crypto/options.js +15 -0
  24. package/dist/crypto/primitive.d.ts +7 -3
  25. package/dist/crypto/primitive.js +36 -10
  26. package/dist/custody/content_key.d.ts +4 -0
  27. package/dist/custody/content_key.js +4 -2
  28. package/dist/custody/index.d.ts +2 -0
  29. package/dist/custody/index.js +5 -1
  30. package/dist/custody/local.d.ts +17 -1
  31. package/dist/custody/local.js +10 -5
  32. package/dist/custody/ssh-agent/index.d.ts +2 -0
  33. package/dist/custody/ssh-agent/index.js +11 -181
  34. package/dist/custody/ssh-agent/port.d.ts +19 -0
  35. package/dist/custody/ssh-agent/port.js +183 -0
  36. package/dist/digest.d.ts +22 -0
  37. package/dist/digest.js +15 -0
  38. package/dist/discovery/body.d.ts +7 -0
  39. package/dist/discovery/body.js +3 -0
  40. package/dist/discovery/cache.d.ts +9 -0
  41. package/dist/discovery/cache.js +2 -0
  42. package/dist/discovery/headers.d.ts +1 -0
  43. package/dist/discovery/index.d.ts +5 -0
  44. package/dist/discovery/index.js +10 -2
  45. package/dist/discovery/node-connection.d.ts +1 -1
  46. package/dist/discovery/node-connection.js +2 -2
  47. package/dist/discovery/node-trust.d.ts +2 -0
  48. package/dist/discovery/node-trust.js +9 -0
  49. package/dist/discovery/node.d.ts +4 -0
  50. package/dist/discovery/node.js +29 -11
  51. package/dist/discovery/source.d.ts +7 -0
  52. package/dist/discovery/source.js +5 -0
  53. package/dist/discovery/support.js +2 -3
  54. package/dist/errors/parity.d.ts +16 -1
  55. package/dist/errors/parity.js +14 -2
  56. package/dist/errors/types.d.ts +13 -1
  57. package/dist/errors/types.js +2 -0
  58. package/dist/index.d.ts +1 -0
  59. package/dist/index.js +1 -0
  60. package/dist/jose/header.d.ts +1 -0
  61. package/dist/jose/jwe.d.ts +9 -0
  62. package/dist/jose/jwe.js +17 -3
  63. package/dist/jose/jws.d.ts +11 -0
  64. package/dist/jose/jws.js +14 -2
  65. package/dist/jose/key-management.d.ts +11 -0
  66. package/dist/jose/key-management.js +13 -2
  67. package/dist/jose/nested.d.ts +6 -0
  68. package/dist/jose/nested.js +9 -1
  69. package/dist/json.d.ts +7 -0
  70. package/dist/key_handle.d.ts +12 -3
  71. package/dist/key_handle.js +32 -25
  72. package/dist/key_identity.d.ts +7 -0
  73. package/dist/key_identity.js +10 -2
  74. package/dist/message/index.d.ts +24 -0
  75. package/dist/message/index.js +20 -0
  76. package/dist/numbers.d.ts +6 -0
  77. package/dist/numbers.js +5 -0
  78. package/dist/policy.d.ts +15 -0
  79. package/dist/policy.js +30 -21
  80. package/dist/profile.d.ts +36 -0
  81. package/dist/profile.js +215 -0
  82. package/dist/public_key.d.ts +11 -2
  83. package/dist/public_key.js +31 -8
  84. package/dist/quorum/verification.d.ts +2 -0
  85. package/dist/quorum/verification.js +1 -0
  86. package/dist/quorum.d.ts +9 -0
  87. package/dist/quorum.js +7 -1
  88. package/dist/replay/claim.d.ts +5 -0
  89. package/dist/replay/claim.js +9 -1
  90. package/dist/replay/index.d.ts +6 -0
  91. package/dist/replay/index.js +13 -1
  92. package/dist/replay/postgres.d.ts +1 -0
  93. package/dist/replay/postgres.js +1 -0
  94. package/dist/replay/receipt.d.ts +1 -0
  95. package/dist/replay/receipt.js +1 -0
  96. package/dist/replay/store.d.ts +1 -0
  97. package/dist/replay/store.js +1 -0
  98. package/dist/replay-port.d.ts +6 -1
  99. package/dist/result.d.ts +3 -0
  100. package/dist/result.js +2 -0
  101. package/dist/sign.d.ts +5 -0
  102. package/dist/sign.js +1 -0
  103. package/dist/signature-base/index.d.ts +2 -0
  104. package/dist/signature-base/index.js +5 -1
  105. package/dist/structured-fields/index.d.ts +21 -0
  106. package/dist/structured-fields/index.js +14 -1
  107. package/dist/verification.d.ts +5 -1
  108. package/dist/verification.js +9 -6
  109. package/dist/verify-quorum.d.ts +1 -0
  110. package/dist/verify-quorum.js +1 -0
  111. package/dist/verify.d.ts +5 -0
  112. package/dist/verify.js +16 -11
  113. package/dist/web-bot-auth/index.d.ts +3 -0
  114. package/dist/web-bot-auth/index.js +3 -0
  115. package/dist/web-bot-auth/policy.d.ts +8 -0
  116. package/dist/web-bot-auth/policy.js +8 -4
  117. package/dist/web-bot-auth/verification.d.ts +8 -0
  118. package/dist/web-bot-auth/verification.js +4 -2
  119. package/package.json +17 -6
package/dist/jose/jws.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { denoInspection } from "../brand.js";
1
2
  import { encodeBase64Url, decodeBase64Url } from "../bytes.js";
2
3
  import { encodeUtf8 } from "../runtime.js";
3
4
  import { isKeyHandle } from "../key_handle.js";
@@ -10,9 +11,11 @@ import { joseError, isRequestSealError } from "../errors/types.js";
10
11
  import { bytes, json, serialize, parse, compact, need, } from "./header.js";
11
12
  import { JWS_ALGORITHMS, context, policy, resolve, selected, bounded, publicSafe, alive, take, ok, err, } from "./support.js";
12
13
  const branded = new WeakSet(), token = Object.freeze({});
14
+ /** Check the private brand of a verified JWS result. */
13
15
  export function isJwsResult(value) {
14
16
  return typeof value === "object" && value !== null && branded.has(value);
15
17
  }
18
+ /** A branded verified JWS result that confines payload bytes. */
16
19
  export class JwsResult {
17
20
  algorithm;
18
21
  header;
@@ -34,6 +37,7 @@ export class JwsResult {
34
37
  get payload() {
35
38
  return new Uint8Array(this.#payload);
36
39
  }
40
+ /** Return a bounded public inspection view without retained cryptographic bytes. */
37
41
  toJSON() {
38
42
  return Object.freeze({
39
43
  algorithm: this.algorithm,
@@ -45,8 +49,8 @@ export class JwsResult {
45
49
  [Symbol.for("nodejs.util.inspect.custom")]() {
46
50
  return this.toJSON();
47
51
  }
48
- [Symbol.for("Deno.customInspect")]() {
49
- return this.toJSON();
52
+ [Symbol.for("Deno.customInspect")](render) {
53
+ return denoInspection(render, this.toJSON());
50
54
  }
51
55
  }
52
56
  Object.freeze(JwsResult.prototype);
@@ -106,6 +110,7 @@ async function signing(protectedPart, h, payload, handle, c) {
106
110
  data.fill(0);
107
111
  }
108
112
  }
113
+ /** Sign a compact JWS with an explicit algorithm and caller-owned signer. */
109
114
  export async function sign(protectedPairs, payload, handle, options = {}) {
110
115
  return publicSafe(async () => {
111
116
  const c = context(options, 5000, true);
@@ -120,6 +125,7 @@ export async function sign(protectedPairs, payload, handle, options = {}) {
120
125
  }
121
126
  });
122
127
  }
128
+ /** Sign a compact JWS while preserving the supplied protected-header bytes. */
123
129
  export async function signProtected(protectedBytes, payload, handle, options = {}) {
124
130
  return publicSafe(async () => {
125
131
  const c = context(options, 5000, true);
@@ -134,6 +140,7 @@ export async function signProtected(protectedBytes, payload, handle, options = {
134
140
  }
135
141
  });
136
142
  }
143
+ /** Verify a compact JWS under an explicit algorithm and key policy. */
137
144
  export async function verify(input, inputPolicy, options = {}) {
138
145
  return publicSafe(async () => {
139
146
  const p = policy(inputPolicy, "jws"), c = context(options, p.timeout);
@@ -183,9 +190,14 @@ export async function verify(input, inputPolicy, options = {}) {
183
190
  }, "invalid_signature");
184
191
  });
185
192
  }
193
+ /** Compact JWS signing and verification operations. */
186
194
  export const JWS = Object.freeze({
195
+ /** Sign a compact JWS with an explicit algorithm and caller-owned signer. */
187
196
  sign,
197
+ /** Sign a compact JWS while preserving the supplied protected-header bytes. */
188
198
  signProtected,
199
+ /** Verify a compact JWS under an explicit algorithm and key policy. */
189
200
  verify,
201
+ /** Check the private brand of the operation result. */
190
202
  isResult: isJwsResult,
191
203
  });
@@ -4,10 +4,12 @@ import { type PublicKey } from "../public_key.ts";
4
4
  import { type KeyHandle } from "../key_handle.ts";
5
5
  import { type Result } from "../result.ts";
6
6
  import { type Header } from "./header.ts";
7
+ /** Owned encrypted-key and content-encryption-key bytes from key wrapping. */
7
8
  export type WrapResult = Readonly<{
8
9
  encrypted_key: Uint8Array<ArrayBuffer>;
9
10
  header: Header;
10
11
  }>;
12
+ /** Check the private brand of a JOSE key-management handle. */
11
13
  export declare function isKeyManagementHandle(value: unknown): value is KeyManagementHandle;
12
14
  /** Caller-owned custodian primitives. RSA private and AES keys never escape as
13
15
  * CryptoKey references. Primitive decrypt releases bytes only after authentication. */
@@ -15,16 +17,25 @@ export declare class KeyManagementHandle {
15
17
  #private;
16
18
  readonly algorithm: string;
17
19
  private constructor();
20
+ /** Import caller-owned local key material into a confined capability. */
18
21
  static local(algorithm: unknown, material: unknown): Promise<Result<KeyManagementHandle>>;
22
+ /** Wrap a generated content-encryption key with the bound key-management algorithm. */
19
23
  wrap(algorithm: unknown, content: unknown, context?: Context): Promise<Result<WrapResult>>;
24
+ /** Authenticate and decrypt content using the bound direct-encryption key. */
20
25
  decrypt(encryptedKey: unknown, header: Header, context?: Context): Promise<Result<Uint8Array<ArrayBuffer>>>;
26
+ /** Confine an unwrapped content-encryption key in a decryption capability. */
21
27
  unwrap(encryptedKey: unknown, header: Header, context?: Context): Promise<Result<UnwrappedKey>>;
28
+ /** Release retained key-management custody and invalidate the capability. */
22
29
  release(): Promise<Result<true>>;
30
+ /** Return a bounded public inspection view without retained cryptographic bytes. */
23
31
  toJSON(): unknown;
24
32
  }
25
33
  export declare function random(length: number): Uint8Array<ArrayBuffer>;
26
34
  export declare function wrapPublic(key: PublicKey | KeyHandle, algorithm: string, content: Uint8Array, context: Context): Promise<Result<WrapResult>>;
35
+ /** Constructors and branding checks for JOSE key-management capabilities. */
27
36
  export declare const KeyManagement: Readonly<{
37
+ /** Import caller-owned local key material into a confined capability. */
28
38
  local: typeof KeyManagementHandle.local;
39
+ /** Check the private brand of a key-management capability. */
29
40
  isHandle: typeof isKeyManagementHandle;
30
41
  }>;
@@ -1,3 +1,4 @@
1
+ import { denoInspection } from "../brand.js";
1
2
  import { now } from "../runtime.js";
2
3
  import { isContext, withDeadline } from "../context.js";
3
4
  import { Custody } from "../custody/index.js";
@@ -14,6 +15,7 @@ import { resolveAlgorithm, importParameters } from "../crypto/algorithm.js";
14
15
  import { bytes, headerValue, need, take } from "./header.js";
15
16
  import { bounded, publicSafe, alive, JWE_ALGORITHMS } from "./support.js";
16
17
  const branded = new WeakSet(), token = Object.freeze({});
18
+ /** Check the private brand of a JOSE key-management handle. */
17
19
  export function isKeyManagementHandle(value) {
18
20
  return typeof value === "object" && value !== null && branded.has(value);
19
21
  }
@@ -32,6 +34,7 @@ export class KeyManagementHandle {
32
34
  branded.add(this);
33
35
  Object.freeze(this);
34
36
  }
37
+ /** Import caller-owned local key material into a confined capability. */
35
38
  static async local(algorithm, material) {
36
39
  return publicSafe(async () => {
37
40
  need(typeof algorithm === "string" && JWE_ALGORITHMS.includes(algorithm), "algorithm_not_permitted");
@@ -96,6 +99,7 @@ export class KeyManagementHandle {
96
99
  }
97
100
  });
98
101
  }
102
+ /** Wrap a generated content-encryption key with the bound key-management algorithm. */
99
103
  async wrap(algorithm, content, context) {
100
104
  return publicSafe(async () => {
101
105
  need(algorithm === this.algorithm && this.algorithm.endsWith("GCMKW"), "algorithm_mismatch", "key");
@@ -128,6 +132,7 @@ export class KeyManagementHandle {
128
132
  }, "signer_failed");
129
133
  });
130
134
  }
135
+ /** Authenticate and decrypt content using the bound direct-encryption key. */
131
136
  async decrypt(encryptedKey, header, context) {
132
137
  return publicSafe(async () => {
133
138
  const c = context === undefined ? take(withDeadline()) : context;
@@ -170,6 +175,7 @@ export class KeyManagementHandle {
170
175
  }, "decryption_failed");
171
176
  });
172
177
  }
178
+ /** Confine an unwrapped content-encryption key in a decryption capability. */
173
179
  async unwrap(encryptedKey, header, context) {
174
180
  return publicSafe(async () => {
175
181
  const c = context === undefined ? take(withDeadline()) : context;
@@ -214,10 +220,12 @@ export class KeyManagementHandle {
214
220
  }, "decryption_failed");
215
221
  });
216
222
  }
223
+ /** Release retained key-management custody and invalidate the capability. */
217
224
  async release() {
218
225
  this.#key = undefined;
219
226
  return ok(true);
220
227
  }
228
+ /** Return a bounded public inspection view without retained cryptographic bytes. */
221
229
  toJSON() {
222
230
  return Object.freeze({
223
231
  kind: "KeyManagementHandle",
@@ -227,8 +235,8 @@ export class KeyManagementHandle {
227
235
  [Symbol.for("nodejs.util.inspect.custom")]() {
228
236
  return this.toJSON();
229
237
  }
230
- [Symbol.for("Deno.customInspect")]() {
231
- return this.toJSON();
238
+ [Symbol.for("Deno.customInspect")](render) {
239
+ return denoInspection(render, this.toJSON());
232
240
  }
233
241
  }
234
242
  Object.freeze(KeyManagementHandle.prototype);
@@ -259,7 +267,10 @@ export async function wrapPublic(key, algorithm, content, context) {
259
267
  return ok(Object.freeze({ encrypted_key: encrypted, header: Object.freeze([]) }));
260
268
  }, "signer_failed");
261
269
  }
270
+ /** Constructors and branding checks for JOSE key-management capabilities. */
262
271
  export const KeyManagement = Object.freeze({
272
+ /** Import caller-owned local key material into a confined capability. */
263
273
  local: KeyManagementHandle.local,
274
+ /** Check the private brand of a key-management capability. */
264
275
  isHandle: isKeyManagementHandle,
265
276
  });
@@ -1,14 +1,20 @@
1
1
  import { type Result } from "../result.ts";
2
2
  import { type JweResult } from "./jwe.ts";
3
3
  import { type JwsResult } from "./jws.ts";
4
+ /** A branded pair of authenticated outer JWE and verified inner JWS results. */
4
5
  export type NestedResult = Readonly<{
5
6
  jwe: JweResult;
6
7
  jws: JwsResult;
7
8
  }>;
9
+ /** Check the private brand of a nested JOSE result. */
8
10
  export declare function isNestedResult(value: unknown): value is NestedResult;
9
11
  export declare function innerCompact(input: Uint8Array): Result<readonly string[]>;
12
+ /** Authenticate an outer JWE and verify its inner compact JWS. */
10
13
  export declare function decrypt(input: unknown, policy: unknown, options: unknown): Promise<Result<NestedResult>>;
14
+ /** One-level nested JWE decryption and JWS verification operations. */
11
15
  export declare const Nested: Readonly<{
16
+ /** Authenticate an outer JWE and verify its inner compact JWS. */
12
17
  decrypt: typeof decrypt;
18
+ /** Check the private brand of the operation result. */
13
19
  isResult: typeof isNestedResult;
14
20
  }>;
@@ -7,6 +7,7 @@ import { compact, headerValue, need, take } from "./header.js";
7
7
  import { joseError } from "../errors/types.js";
8
8
  import { publicSafe, ok } from "./support.js";
9
9
  const branded = new WeakSet();
10
+ /** Check the private brand of a nested JOSE result. */
10
11
  export function isNestedResult(value) {
11
12
  return typeof value === "object" && value !== null && branded.has(value);
12
13
  }
@@ -14,6 +15,7 @@ export function innerCompact(input) {
14
15
  const framed = compact(input, 3, false);
15
16
  return !framed.ok && framed.error.reason === 'invalid_serialization' ? err(joseError('nesting_depth', 'nesting')) : framed;
16
17
  }
18
+ /** Authenticate an outer JWE and verify its inner compact JWS. */
17
19
  export async function decrypt(input, policy, options) {
18
20
  return publicSafe(async () => {
19
21
  const opts = snapshotOptions(options, ["content_types", "context"]);
@@ -52,4 +54,10 @@ export async function decrypt(input, policy, options) {
52
54
  }
53
55
  });
54
56
  }
55
- export const Nested = Object.freeze({ decrypt, isResult: isNestedResult });
57
+ /** One-level nested JWE decryption and JWS verification operations. */
58
+ export const Nested = Object.freeze({
59
+ /** Authenticate an outer JWE and verify its inner compact JWS. */
60
+ decrypt,
61
+ /** Check the private brand of the operation result. */
62
+ isResult: isNestedResult
63
+ });
package/dist/json.d.ts CHANGED
@@ -1,17 +1,22 @@
1
1
  import { type Result } from './result.ts';
2
+ /** An ordered JSON tree that retains numeric source text. */
2
3
  export type JsonNode = null | boolean | string | JsonNumber | JsonObject | JsonArray;
4
+ /** A JSON number represented by its original lexeme. */
3
5
  export type JsonNumber = {
4
6
  readonly kind: 'number';
5
7
  readonly lexeme: string;
6
8
  };
9
+ /** A JSON object represented by ordered key-value pairs. */
7
10
  export type JsonObject = {
8
11
  readonly kind: 'object';
9
12
  readonly pairs: readonly (readonly [string, JsonNode])[];
10
13
  };
14
+ /** A JSON array represented by an ordered sequence of nodes. */
11
15
  export type JsonArray = {
12
16
  readonly kind: 'array';
13
17
  readonly items: readonly JsonNode[];
14
18
  };
19
+ /** Byte, depth, and container limits for JSON decoding. */
15
20
  export type JsonLimits = {
16
21
  maxBytes: number;
17
22
  maxDepth: number;
@@ -19,10 +24,12 @@ export type JsonLimits = {
19
24
  maxArrayLength: number;
20
25
  maxStringBytes: number;
21
26
  };
27
+ /** JSON limits and explicit duplicate and noncharacter policies. */
22
28
  export type JsonDecodeOptions = Partial<JsonLimits> & {
23
29
  rejectDuplicates?: boolean;
24
30
  acceptNoncharacters?: boolean;
25
31
  };
32
+ /** A decoded JSON tree and its duplicate member names. */
26
33
  export type DecodedJson = {
27
34
  readonly node: JsonNode;
28
35
  readonly duplicates: readonly string[];
@@ -4,18 +4,27 @@ import { type CustodyAlgorithm } from './crypto/algorithm.ts';
4
4
  import { type PublicKey } from './public_key.ts';
5
5
  import { type KeyIdentity } from './key_identity.ts';
6
6
  import { type UnwrappedKey } from './custody/content_key.ts';
7
+ /** A branded algorithm-bound capability that confines private key operations. */
7
8
  export type KeyHandle = Readonly<{
8
9
  algorithm: CustodyAlgorithm;
9
10
  capabilities: readonly ('sign' | 'verify' | 'unwrap')[];
10
- sign: (algorithm: unknown, data: unknown, context: Context) => Promise<Result<Uint8Array<ArrayBuffer>>>;
11
- verify: (algorithm: unknown, signature: unknown, data: unknown, context: Context) => Promise<Result<true>>;
11
+ /** Sign bounded bytes with the algorithm-bound custody capability. */
12
+ sign: (algorithm: unknown, data: unknown, context: Context, options?: unknown) => Promise<Result<Uint8Array<ArrayBuffer>>>;
13
+ /** Verify a signature through the algorithm-bound custody capability. */
14
+ verify: (algorithm: unknown, signature: unknown, data: unknown, context: Context, options?: unknown) => Promise<Result<true>>;
15
+ /** Return the handle's branded public key. */
12
16
  publicKey: (context?: Context) => Promise<Result<PublicKey>>;
17
+ /** Return the handle's branded key identity. */
13
18
  identity: (context?: Context) => Promise<Result<KeyIdentity>>;
19
+ /** Unwrap an encrypted key into a confined decryption capability. */
14
20
  unwrap: (algorithm: unknown, wrappedKey: unknown, context: Context, options?: unknown) => Promise<Result<UnwrappedKey>>;
21
+ /** Release retained custody and invalidate the capability. */
15
22
  release: () => Promise<Result<true>>;
16
23
  }>;
24
+ /** The caller-provided operation port wrapped by a branded key handle. */
17
25
  export type Custodian = Pick<KeyHandle, 'sign' | 'verify' | 'publicKey' | 'identity' | 'unwrap' | 'release'>;
26
+ /** Check the private brand of a custody handle. */
18
27
  export declare function isKeyHandle(value: unknown): value is KeyHandle;
19
28
  /** Wrap trusted custodian code under deadline, result validation, and fault containment. */
20
29
  export declare function createKeyHandle(algorithm: unknown, port: Custodian, capabilities?: KeyHandle['capabilities']): Result<KeyHandle>;
21
- export declare function verifyHandle(key: KeyHandle, algorithm: unknown, signature: unknown, data: unknown, context: Context): Promise<Result<true>>;
30
+ export declare function verifyHandle(key: KeyHandle, algorithm: unknown, signature: unknown, data: unknown, context: Context, options?: unknown): Promise<Result<true>>;
@@ -1,15 +1,18 @@
1
+ import { denoInspection } from './brand.js';
2
+ import { byteLimit } from './crypto/options.js';
1
3
  import { snapshotOptions } from './brand.js';
2
4
  import { withDeadline, isContext, remainingMs, run, } from './context.js';
3
- import { SIGNATURE_MAX_BYTES, CUSTODY_SIGNATURE_MAX_BYTES } from './constants.js';
5
+ import { CRYPTO_MAX_BYTES_CEILING, SIGNATURE_MAX_BYTES, CUSTODY_SIGNATURE_MAX_BYTES } from './constants.js';
4
6
  import { custodyError, isRequestSealError } from './errors/types.js';
5
7
  import { ok, err } from './result.js';
6
8
  import { resolveAlgorithm, selection, } from './crypto/algorithm.js';
7
- import { dataBytes } from './crypto/primitive.js';
9
+ import { dataBytes, boundedBinary, binaryByteLength } from './crypto/primitive.js';
8
10
  import { isPublicKey } from './public_key.js';
9
11
  import { isKeyIdentity } from './key_identity.js';
10
12
  import { isUnwrappedKey } from './custody/content_key.js';
11
13
  const handles = new WeakSet();
12
14
  const token = Object.freeze({});
15
+ /** Check the private brand of a custody handle. */
13
16
  export function isKeyHandle(value) {
14
17
  return typeof value === 'object' && value !== null && handles.has(value);
15
18
  }
@@ -71,7 +74,10 @@ class ManagedHandle {
71
74
  const resolved = resolveAlgorithm(algorithm, true);
72
75
  return resolved.ok && resolved.value.id === this.#spec.id;
73
76
  }
74
- async sign(algorithm, data, context) {
77
+ async sign(algorithm, data, context, options = {}) {
78
+ const maximum = byteLimit(options);
79
+ if (!maximum.ok)
80
+ return err(custodyError('invalid_options'));
75
81
  try {
76
82
  if (!this.#port)
77
83
  return err(custodyError('key_not_found'));
@@ -81,25 +87,27 @@ class ManagedHandle {
81
87
  return err(custodyError('key_mismatch'));
82
88
  if (!this.capabilities.includes('sign'))
83
89
  return err(custodyError('unsupported_operation'));
84
- const bytes = dataBytes(data);
90
+ const bytes = dataBytes(data, maximum.value);
85
91
  if (!bytes.ok)
86
92
  return err(custodyError('invalid_data'));
87
93
  return this.#invoke('sign', context, async (p, c) => {
88
94
  try {
89
- return await p.sign(this.algorithm, bytes.value, c);
95
+ return await p.sign(this.algorithm, bytes.value, c, { maxBytes: CRYPTO_MAX_BYTES_CEILING });
90
96
  }
91
97
  finally {
92
98
  bytes.value.fill(0);
93
99
  }
94
- }, (v) => v instanceof Uint8Array &&
95
- v.length > 0 &&
96
- v.length <= CUSTODY_SIGNATURE_MAX_BYTES);
100
+ }, (v) => boundedBinary(v, CUSTODY_SIGNATURE_MAX_BYTES) &&
101
+ binaryByteLength(v) > 0);
97
102
  }
98
103
  catch {
99
104
  return err(custodyError('invalid_data'));
100
105
  }
101
106
  }
102
- async verify(algorithm, signature, data, context) {
107
+ async verify(algorithm, signature, data, context, options = {}) {
108
+ const maximum = byteLimit(options);
109
+ if (!maximum.ok)
110
+ return err(custodyError('invalid_options'));
103
111
  try {
104
112
  if (!this.#port)
105
113
  return err(custodyError('key_not_found'));
@@ -109,17 +117,17 @@ class ManagedHandle {
109
117
  return err(custodyError('key_mismatch'));
110
118
  if (!this.capabilities.includes('verify'))
111
119
  return err(custodyError('unsupported_operation'));
112
- const bytes = dataBytes(data);
120
+ const bytes = dataBytes(data, maximum.value);
113
121
  if (!bytes.ok)
114
122
  return err(custodyError('invalid_data'));
115
123
  if (!(signature instanceof Uint8Array))
116
124
  return err(custodyError('invalid_signature'));
117
- if (signature.length > CUSTODY_SIGNATURE_MAX_BYTES)
125
+ if (!boundedBinary(signature, CUSTODY_SIGNATURE_MAX_BYTES))
118
126
  return err(custodyError('limit'));
119
127
  const copied = new Uint8Array(signature);
120
128
  return this.#invoke('verify', context, async (p, c) => {
121
129
  try {
122
- return await p.verify(this.algorithm, copied, bytes.value, c);
130
+ return await p.verify(this.algorithm, copied, bytes.value, c, { maxBytes: CRYPTO_MAX_BYTES_CEILING });
123
131
  }
124
132
  finally {
125
133
  copied.fill(0);
@@ -138,6 +146,12 @@ class ManagedHandle {
138
146
  return this.#invoke('identity', context, (p, c) => p.identity(c), isKeyIdentity);
139
147
  }
140
148
  async unwrap(algorithm, wrappedKey, context, options = {}) {
149
+ const opts = snapshotOptions(options, ['length']);
150
+ if (!opts.ok ||
151
+ (Object.hasOwn(opts.value, 'length') &&
152
+ opts.value.length !== 128 &&
153
+ opts.value.length !== 256))
154
+ return err(custodyError('invalid_options'));
141
155
  try {
142
156
  if (!this.#port)
143
157
  return err(custodyError('key_not_found'));
@@ -147,16 +161,9 @@ class ManagedHandle {
147
161
  return err(custodyError('key_mismatch'));
148
162
  if (!this.capabilities.includes('unwrap'))
149
163
  return err(custodyError('unsupported_operation'));
150
- if (!(wrappedKey instanceof Uint8Array) ||
151
- wrappedKey.length === 0 ||
152
- wrappedKey.length > SIGNATURE_MAX_BYTES)
164
+ if (!boundedBinary(wrappedKey, SIGNATURE_MAX_BYTES) ||
165
+ binaryByteLength(wrappedKey) === 0)
153
166
  return err(custodyError('invalid_data'));
154
- const opts = snapshotOptions(options, ['length']);
155
- if (!opts.ok ||
156
- (Object.hasOwn(opts.value, 'length') &&
157
- opts.value.length !== 128 &&
158
- opts.value.length !== 256))
159
- return err(custodyError('invalid_options'));
160
167
  const copied = new Uint8Array(wrappedKey);
161
168
  return this.#invoke('unwrap', context, async (p, c) => {
162
169
  try {
@@ -196,8 +203,8 @@ class ManagedHandle {
196
203
  [Symbol.for('nodejs.util.inspect.custom')]() {
197
204
  return this.toJSON();
198
205
  }
199
- [Symbol.for('Deno.customInspect')]() {
200
- return this.toJSON();
206
+ [Symbol.for('Deno.customInspect')](render) {
207
+ return denoInspection(render, this.toJSON());
201
208
  }
202
209
  }
203
210
  /** Wrap trusted custodian code under deadline, result validation, and fault containment. */
@@ -251,6 +258,6 @@ export function createKeyHandle(algorithm, port, capabilities) {
251
258
  Object.freeze(ManagedHandle.prototype);
252
259
  Object.freeze(ManagedHandle);
253
260
  const managedVerify = Function.prototype.call.bind(ManagedHandle.prototype.verify);
254
- export function verifyHandle(key, algorithm, signature, data, context) {
255
- return managedVerify(key, algorithm, signature, data, context);
261
+ export function verifyHandle(key, algorithm, signature, data, context, options = {}) {
262
+ return managedVerify(key, algorithm, signature, data, context, options);
256
263
  }
@@ -2,15 +2,22 @@ import { type Result } from './result.ts';
2
2
  import { type PublicKey } from './public_key.ts';
3
3
  /** Internal, nonserialized canonical equivalence key for scoped identity merging. */
4
4
  export declare function identityClassKey(identity: KeyIdentity): string | undefined;
5
+ /** Check the private brand of a key identity. */
5
6
  export declare function isKeyIdentity(value: unknown): value is KeyIdentity;
7
+ /** A branded public, symmetric, or unknown key identity. */
6
8
  export declare class KeyIdentity {
7
9
  #private;
8
10
  readonly kind: 'public' | 'symmetric' | 'unknown';
9
11
  private constructor();
12
+ /** Derive an identity from a branded public key's canonical components. */
10
13
  static fromPublic(key: PublicKey): Result<KeyIdentity>;
14
+ /** Create a symmetric identity from caller-supplied nonsecret bytes. */
11
15
  static symmetric(value: unknown): Result<KeyIdentity>;
16
+ /** Create an identity that cannot establish key equivalence. */
12
17
  static unknown(): Result<KeyIdentity>;
18
+ /** Compare branded key identities using their canonical identity bytes. */
13
19
  static same(a: unknown, b: unknown): Result<boolean>;
20
+ /** Return a bounded public inspection view without retained cryptographic bytes. */
14
21
  toJSON(): Readonly<{
15
22
  kind: KeyIdentity['kind'];
16
23
  }>;
@@ -1,3 +1,4 @@
1
+ import { denoInspection } from './brand.js';
1
2
  import { ok, err } from './result.js';
2
3
  import { custodyError } from './errors/types.js';
3
4
  import { KEY_IDENTITY_MAX_BYTES } from './constants.js';
@@ -8,9 +9,11 @@ const classKeys = new WeakMap();
8
9
  /** Internal, nonserialized canonical equivalence key for scoped identity merging. */
9
10
  export function identityClassKey(identity) { return classKeys.get(identity); }
10
11
  const token = Object.freeze({});
12
+ /** Check the private brand of a key identity. */
11
13
  export function isKeyIdentity(value) {
12
14
  return typeof value === 'object' && value !== null && identities.has(value);
13
15
  }
16
+ /** A branded public, symmetric, or unknown key identity. */
14
17
  export class KeyIdentity {
15
18
  kind;
16
19
  #bytes;
@@ -24,6 +27,7 @@ export class KeyIdentity {
24
27
  identities.add(this);
25
28
  Object.freeze(this);
26
29
  }
30
+ /** Derive an identity from a branded public key's canonical components. */
27
31
  static fromPublic(key) {
28
32
  if (!isPublicKey(key))
29
33
  return err(custodyError('invalid_key'));
@@ -32,6 +36,7 @@ export class KeyIdentity {
32
36
  ? ok(new KeyIdentity(token, 'public', r.value))
33
37
  : err(custodyError('invalid_key'));
34
38
  }
39
+ /** Create a symmetric identity from caller-supplied nonsecret bytes. */
35
40
  static symmetric(value) {
36
41
  try {
37
42
  if ((!(value instanceof Uint8Array) && typeof value !== 'string') ||
@@ -48,9 +53,11 @@ export class KeyIdentity {
48
53
  return err(custodyError('invalid_options'));
49
54
  }
50
55
  }
56
+ /** Create an identity that cannot establish key equivalence. */
51
57
  static unknown() {
52
58
  return ok(new KeyIdentity(token, 'unknown'));
53
59
  }
60
+ /** Compare branded key identities using their canonical identity bytes. */
54
61
  static same(a, b) {
55
62
  if (!isKeyIdentity(a) || !isKeyIdentity(b))
56
63
  return err(custodyError('invalid_key'));
@@ -63,14 +70,15 @@ export class KeyIdentity {
63
70
  diff |= a.#bytes[i] ^ b.#bytes[i];
64
71
  return ok(diff === 0);
65
72
  }
73
+ /** Return a bounded public inspection view without retained cryptographic bytes. */
66
74
  toJSON() {
67
75
  return Object.freeze({ kind: this.kind });
68
76
  }
69
77
  [Symbol.for('nodejs.util.inspect.custom')]() {
70
78
  return this.toJSON();
71
79
  }
72
- [Symbol.for('Deno.customInspect')]() {
73
- return this.toJSON();
80
+ [Symbol.for('Deno.customInspect')](render) {
81
+ return denoInspection(render, this.toJSON());
74
82
  }
75
83
  }
76
84
  Object.freeze(KeyIdentity.prototype);
@@ -1,10 +1,12 @@
1
1
  import { type Result } from '../result.ts';
2
+ /** An owned HTTP field occurrence with its section and byte value. */
2
3
  export type FieldOccurrence = Readonly<{
3
4
  name: string;
4
5
  value: Uint8Array;
5
6
  section: 'headers' | 'trailers';
6
7
  provenance: 'caller' | 'http1' | 'http2' | 'http3';
7
8
  }>;
9
+ /** Owned or unavailable body bytes with explicit retention and completeness facts. */
8
10
  export type Body = Readonly<{
9
11
  state: 'retained' | 'streaming' | 'consumed' | 'unavailable';
10
12
  bytes: Uint8Array | null;
@@ -12,11 +14,13 @@ export type Body = Readonly<{
12
14
  ownership: 'caller';
13
15
  maxBytes: number;
14
16
  }>;
17
+ /** Branded TLS and peer-certificate facts captured by an adapter. */
15
18
  export type TransportFacts = Readonly<{
16
19
  httpVersion: 'unknown' | 'http1_0' | 'http1_1' | 'http2' | 'http3';
17
20
  tls: 'unknown' | 'plain' | 'tls';
18
21
  evidence: 'declared';
19
22
  }>;
23
+ /** A branded HTTP request or response preserving target and field bytes. */
20
24
  export type Message = Readonly<{
21
25
  kind: 'request' | 'response';
22
26
  method: string | null;
@@ -31,30 +35,50 @@ export type Message = Readonly<{
31
35
  relatedRequest: Message | null;
32
36
  transport: TransportFacts;
33
37
  }>;
38
+ /** Check the private brand of an HTTP message. */
34
39
  export declare function isMessage(value: unknown): value is Message;
40
+ /** Check the private brand of a field occurrence. */
35
41
  export declare function isFieldOccurrence(value: unknown): value is FieldOccurrence;
42
+ /** Check the private brand of a body snapshot. */
36
43
  export declare function isBody(value: unknown): value is Body;
44
+ /** Check the private brand of captured transport facts. */
37
45
  export declare function isTransportFacts(value: unknown): value is TransportFacts;
38
46
  export declare function token(value: unknown, max?: number): value is string;
39
47
  export declare function uriBytes(s: string): boolean;
40
48
  export declare function authority(value: unknown, requirePort?: boolean): value is string;
49
+ /** Field occurrence construction and validation. */
41
50
  export declare const FieldOccurrence: Readonly<{
51
+ /** Validate and brand the supplied HTTP snapshot. */
42
52
  create: (a: unknown) => Result<FieldOccurrence>;
53
+ /** Validate the snapshot using the same boundary as construction. */
43
54
  validate: (a: unknown) => Result<FieldOccurrence>;
44
55
  }>;
56
+ /** Body snapshot construction and validation. */
45
57
  export declare const Body: Readonly<{
58
+ /** Validate and brand the supplied HTTP snapshot. */
46
59
  create: (a: unknown) => Result<Body>;
60
+ /** Validate the snapshot using the same boundary as construction. */
47
61
  validate: (a: unknown) => Result<Body>;
48
62
  }>;
63
+ /** Transport snapshot construction and validation. */
49
64
  export declare const TransportFacts: Readonly<{
65
+ /** Validate and brand the supplied HTTP snapshot. */
50
66
  create: (a: unknown) => Result<TransportFacts>;
67
+ /** Validate the snapshot using the same boundary as construction. */
51
68
  validate: (a: unknown) => Result<TransportFacts>;
52
69
  }>;
70
+ /** Validate and brand a lossless HTTP message snapshot. */
53
71
  export declare function createMessage(a: unknown): Result<Message>;
72
+ /** Validate a message snapshot through the same constructor boundary. */
54
73
  export declare const validateMessage: typeof createMessage;
74
+ /** Lossless HTTP message construction and validation. */
55
75
  export declare const Message: Readonly<{
76
+ /** Validate and brand the supplied HTTP snapshot. */
56
77
  create: typeof createMessage;
78
+ /** Validate the snapshot using the same boundary as construction. */
57
79
  validate: typeof createMessage;
80
+ /** Build a branded HTTP request with owned field and body bytes. */
58
81
  request: (method: unknown, target: unknown, fields: unknown, bytes: unknown, options?: unknown) => Promise<Result<Message>>;
82
+ /** Build a branded HTTP response with owned field and body bytes. */
59
83
  response: (status: unknown, fields: unknown, bytes: unknown, options?: unknown) => Promise<Result<Message>>;
60
84
  }>;