@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.
- package/CHANGELOG.md +15 -0
- package/README.md +341 -114
- package/dist/accept-signature.d.ts +11 -0
- package/dist/accept-signature.js +7 -0
- package/dist/adapters/fetch/index.d.ts +10 -0
- package/dist/adapters/fetch/index.js +21 -4
- package/dist/adapters/fetch/runtime.d.ts +1 -0
- package/dist/adapters/fetch/runtime.js +1 -0
- package/dist/adapters/node-http/index.d.ts +16 -0
- package/dist/adapters/node-http/index.js +8 -4
- package/dist/brand.d.ts +5 -0
- package/dist/brand.js +12 -0
- package/dist/bytes.d.ts +4 -0
- package/dist/bytes.js +5 -0
- package/dist/constants.d.ts +61 -1
- package/dist/constants.js +61 -1
- package/dist/context.d.ts +4 -0
- package/dist/context.js +3 -0
- package/dist/crypto/algorithm.d.ts +5 -0
- package/dist/crypto/index.d.ts +12 -2
- package/dist/crypto/index.js +34 -11
- package/dist/crypto/options.d.ts +3 -0
- package/dist/crypto/options.js +15 -0
- package/dist/crypto/primitive.d.ts +7 -3
- package/dist/crypto/primitive.js +36 -10
- package/dist/custody/content_key.d.ts +4 -0
- package/dist/custody/content_key.js +4 -2
- package/dist/custody/index.d.ts +2 -0
- package/dist/custody/index.js +5 -1
- package/dist/custody/local.d.ts +17 -1
- package/dist/custody/local.js +10 -5
- package/dist/custody/ssh-agent/index.d.ts +2 -0
- package/dist/custody/ssh-agent/index.js +11 -181
- package/dist/custody/ssh-agent/port.d.ts +19 -0
- package/dist/custody/ssh-agent/port.js +183 -0
- package/dist/digest.d.ts +22 -0
- package/dist/digest.js +15 -0
- package/dist/discovery/body.d.ts +7 -0
- package/dist/discovery/body.js +3 -0
- package/dist/discovery/cache.d.ts +9 -0
- package/dist/discovery/cache.js +2 -0
- package/dist/discovery/headers.d.ts +1 -0
- package/dist/discovery/index.d.ts +5 -0
- package/dist/discovery/index.js +10 -2
- package/dist/discovery/node-connection.d.ts +1 -1
- package/dist/discovery/node-connection.js +2 -2
- package/dist/discovery/node-trust.d.ts +2 -0
- package/dist/discovery/node-trust.js +9 -0
- package/dist/discovery/node.d.ts +4 -0
- package/dist/discovery/node.js +29 -11
- package/dist/discovery/source.d.ts +7 -0
- package/dist/discovery/source.js +5 -0
- package/dist/discovery/support.js +2 -3
- package/dist/errors/parity.d.ts +16 -1
- package/dist/errors/parity.js +14 -2
- package/dist/errors/types.d.ts +13 -1
- package/dist/errors/types.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/jose/header.d.ts +1 -0
- package/dist/jose/jwe.d.ts +9 -0
- package/dist/jose/jwe.js +17 -3
- package/dist/jose/jws.d.ts +11 -0
- package/dist/jose/jws.js +14 -2
- package/dist/jose/key-management.d.ts +11 -0
- package/dist/jose/key-management.js +13 -2
- package/dist/jose/nested.d.ts +6 -0
- package/dist/jose/nested.js +9 -1
- package/dist/json.d.ts +7 -0
- package/dist/key_handle.d.ts +12 -3
- package/dist/key_handle.js +32 -25
- package/dist/key_identity.d.ts +7 -0
- package/dist/key_identity.js +10 -2
- package/dist/message/index.d.ts +24 -0
- package/dist/message/index.js +20 -0
- package/dist/numbers.d.ts +6 -0
- package/dist/numbers.js +5 -0
- package/dist/policy.d.ts +15 -0
- package/dist/policy.js +30 -21
- package/dist/profile.d.ts +36 -0
- package/dist/profile.js +215 -0
- package/dist/public_key.d.ts +11 -2
- package/dist/public_key.js +31 -8
- package/dist/quorum/verification.d.ts +2 -0
- package/dist/quorum/verification.js +1 -0
- package/dist/quorum.d.ts +9 -0
- package/dist/quorum.js +7 -1
- package/dist/replay/claim.d.ts +5 -0
- package/dist/replay/claim.js +9 -1
- package/dist/replay/index.d.ts +6 -0
- package/dist/replay/index.js +13 -1
- package/dist/replay/postgres.d.ts +1 -0
- package/dist/replay/postgres.js +1 -0
- package/dist/replay/receipt.d.ts +1 -0
- package/dist/replay/receipt.js +1 -0
- package/dist/replay/store.d.ts +1 -0
- package/dist/replay/store.js +1 -0
- package/dist/replay-port.d.ts +6 -1
- package/dist/result.d.ts +3 -0
- package/dist/result.js +2 -0
- package/dist/sign.d.ts +5 -0
- package/dist/sign.js +1 -0
- package/dist/signature-base/index.d.ts +2 -0
- package/dist/signature-base/index.js +5 -1
- package/dist/structured-fields/index.d.ts +21 -0
- package/dist/structured-fields/index.js +14 -1
- package/dist/verification.d.ts +5 -1
- package/dist/verification.js +9 -6
- package/dist/verify-quorum.d.ts +1 -0
- package/dist/verify-quorum.js +1 -0
- package/dist/verify.d.ts +5 -0
- package/dist/verify.js +16 -11
- package/dist/web-bot-auth/index.d.ts +3 -0
- package/dist/web-bot-auth/index.js +3 -0
- package/dist/web-bot-auth/policy.d.ts +8 -0
- package/dist/web-bot-auth/policy.js +8 -4
- package/dist/web-bot-auth/verification.d.ts +8 -0
- package/dist/web-bot-auth/verification.js +4 -2
- package/package.json +17 -6
package/dist/constants.js
CHANGED
|
@@ -1,59 +1,119 @@
|
|
|
1
|
-
|
|
1
|
+
/** The public package version. */
|
|
2
|
+
export const VERSION = '0.2.0';
|
|
3
|
+
/** The immutable public conformance corpus release and index digest. */
|
|
2
4
|
export const CORPUS_PIN = Object.freeze({ tag: 'v0.4.0', commit: 'b2392871933d99641a7cb2ca2726aefd20081dee', indexSha256: 'f37435eca377666ab45c2b8f580287263145268e03d9224c76e324e4afaded6f' });
|
|
3
5
|
// RequestSeal.StructuredFields ceilings at v0.4.0 (b239287).
|
|
6
|
+
/** Default bounded Structured Fields parser limits. */
|
|
4
7
|
export const STRUCTURED_FIELDS_LIMITS = Object.freeze({ maxBytes: 65_536, maxMembers: 1024, maxInnerItems: 256, maxParameters: 256, maxKeyBytes: 256, maxValueBytes: 16_384, maxNodes: 4096 });
|
|
5
8
|
// RequestSeal.JOSE.Support: 16,384 bytes, four containers deep, 64 entries per container.
|
|
6
9
|
// String bytes are bounded by the same complete-header byte ceiling.
|
|
10
|
+
/** Default bounded JSON parser limits. */
|
|
7
11
|
export const JSON_LIMITS = Object.freeze({ maxBytes: 16_384, maxDepth: 4, maxMembers: 64, maxArrayLength: 64, maxStringBytes: 16_384 });
|
|
12
|
+
/** Maximum compact JOSE wire bytes. */
|
|
8
13
|
export const JOSE_MAX_COMPACT_BYTES = 1_048_576; // RequestSeal.JOSE.Support.bytes compact wire ceiling.
|
|
14
|
+
/** Maximum JOSE plaintext bytes. */
|
|
9
15
|
export const JOSE_MAX_PLAINTEXT_BYTES = 1_048_576; // RequestSeal.JOSE.Support.
|
|
16
|
+
/** Default maximum retained body bytes. */
|
|
10
17
|
export const BODY_MAX_BYTES = 1_048_576; // RequestSeal.Body default retention.
|
|
18
|
+
/** Default maximum digest input bytes. */
|
|
11
19
|
export const DIGEST_MAX_BYTES = 16_777_216; // RequestSeal.Digest default work ceiling.
|
|
20
|
+
/** Fixed maximum cryptographic input byte limit. */
|
|
21
|
+
export const CRYPTO_MAX_BYTES_CEILING = 16_777_216; // RequestSeal.Crypto.max_bytes_ceiling at v0.4.1.
|
|
22
|
+
/** Default cryptographic input byte limit. */
|
|
23
|
+
export const CRYPTO_DEFAULT_MAX_BYTES = 1_048_576; // Independent of the fixed SignatureBase ceiling.
|
|
24
|
+
/** Maximum signature-base bytes. */
|
|
12
25
|
export const SIGNATURE_BASE_MAX_BYTES = 1_048_576; // RequestSeal.SignatureBase.
|
|
26
|
+
/** Maximum covered signature-base components. */
|
|
13
27
|
export const SIGNATURE_BASE_MAX_COMPONENTS = 256; // RequestSeal.SignatureBase.
|
|
28
|
+
/** Maximum encoded signature or nonce bytes. */
|
|
14
29
|
export const SIGNATURE_MAX_BYTES = 1024; // RequestSeal.SignatureFields (signature and nonce).
|
|
30
|
+
/** Maximum raw HTTP target bytes. */
|
|
15
31
|
export const MESSAGE_MAX_TARGET_BYTES = 16_384; // RequestSeal.Message.
|
|
32
|
+
/** Maximum bytes in one HTTP field. */
|
|
16
33
|
export const MESSAGE_MAX_FIELD_BYTES = 65_536; // RequestSeal.Message.Validation.
|
|
34
|
+
/** Maximum total HTTP field bytes. */
|
|
17
35
|
export const MESSAGE_MAX_FIELDS_BYTES = 1_048_576; // RequestSeal.Message.
|
|
36
|
+
/** Maximum quorum evaluation nodes. */
|
|
18
37
|
export const QUORUM_MAX_NODES = 131_072; // RequestSeal.Quorum.Evaluation.
|
|
38
|
+
/** Maximum signatures evaluated by a quorum. */
|
|
19
39
|
export const QUORUM_MAX_SIGNATURES = 64; // RequestSeal.Quorum.
|
|
40
|
+
/** Default maximum discovery response bytes. */
|
|
20
41
|
export const DISCOVERY_DEFAULT_MAX_BYTES = 65_536; // RequestSeal.Discovery.Source.
|
|
42
|
+
/** Fixed maximum discovery response bytes. */
|
|
21
43
|
export const DISCOVERY_MAX_BYTES = 1_048_576; // RequestSeal.Discovery.Source maximum.
|
|
44
|
+
/** Maximum supported Unix timestamp in seconds. */
|
|
22
45
|
export const CLOCK_MAX_SECONDS = 253_402_300_799; // Shared port contract, end of year 9999.
|
|
46
|
+
/** Maximum operation timeout in milliseconds. */
|
|
23
47
|
export const TIMEOUT_MAX_MS = 300_000; // RequestSeal.Custody and JOSE.Support.
|
|
48
|
+
/** Default operation timeout in milliseconds. */
|
|
24
49
|
export const TIMEOUT_DEFAULT_MS = 5000; // RequestSeal.Custody and JOSE.Support.
|
|
50
|
+
/** Maximum absolute Structured Fields integer value. */
|
|
25
51
|
export const SF_MAX_INTEGER = 999_999_999_999_999; // RequestSeal.StructuredFields, RFC 9651.
|
|
52
|
+
/** Maximum profile metadata members. */
|
|
26
53
|
export const PROFILE_MAX_MEMBERS = 8; // RequestSeal.Profile.
|
|
54
|
+
/** Maximum profile metadata value bytes. */
|
|
27
55
|
export const PROFILE_MAX_VALUE_BYTES = 256; // RequestSeal.Profile.
|
|
56
|
+
/** Maximum profile name or key code points. */
|
|
57
|
+
export const PROFILE_MAX_ATOM_CODE_POINTS = 255; // BEAM atom limit for profile names and keys.
|
|
58
|
+
/** Maximum explicitly permitted discovery addresses. */
|
|
28
59
|
export const DISCOVERY_MAX_PERMITTED_ADDRESSES = 16; // RequestSeal.Discovery.Source.
|
|
60
|
+
/** Fixed maximum discovery keys per source. */
|
|
29
61
|
export const DISCOVERY_MAX_KEYS = 256; // RequestSeal.Discovery.Source.
|
|
62
|
+
/** Default maximum discovery keys per source. */
|
|
30
63
|
export const DISCOVERY_DEFAULT_MAX_KEYS = 32; // RequestSeal.Discovery.Source.
|
|
64
|
+
/** Maximum discovery redirect hops. */
|
|
31
65
|
export const DISCOVERY_MAX_REDIRECTS = 3; // RequestSeal.Discovery.Source.
|
|
66
|
+
/** Maximum discovery cache lifetime in seconds. */
|
|
32
67
|
export const DISCOVERY_MAX_TTL_SECONDS = 604_800; // RequestSeal.Discovery.Source.
|
|
68
|
+
/** Maximum negative discovery cache lifetime in seconds. */
|
|
33
69
|
export const DISCOVERY_MAX_NEGATIVE_TTL_SECONDS = 300; // RequestSeal.Discovery.Source.
|
|
70
|
+
/** Maximum client metadata document bytes. */
|
|
34
71
|
export const DISCOVERY_CIMD_MAX_BYTES = 5120; // RequestSeal.Discovery.Source.
|
|
35
72
|
// RequestSeal.PublicKey and Custody.Local container/component ceilings at v0.4.0 (b239287).
|
|
73
|
+
/** Maximum encoded key container bytes. */
|
|
36
74
|
export const KEY_MAX_BYTES = 16_384;
|
|
75
|
+
/** Maximum key container members. */
|
|
37
76
|
export const KEY_MAX_MEMBERS = 32;
|
|
77
|
+
/** Maximum encoded public key component bytes. */
|
|
38
78
|
export const KEY_COMPONENT_MAX_ENCODED_BYTES = 21_846;
|
|
79
|
+
/** Maximum encoded RSA component bytes. */
|
|
39
80
|
export const RSA_COMPONENT_MAX_ENCODED_BYTES = 1366;
|
|
81
|
+
/** Minimum permitted RSA modulus bits. */
|
|
40
82
|
export const RSA_MIN_BITS = 2048;
|
|
83
|
+
/** Maximum permitted RSA modulus bits. */
|
|
41
84
|
export const RSA_MAX_BITS = 8192;
|
|
85
|
+
/** Maximum permitted RSA public exponent. */
|
|
42
86
|
export const RSA_MAX_EXPONENT = 0xffff_ffff;
|
|
87
|
+
/** Maximum caller-supplied key identity bytes. */
|
|
43
88
|
export const KEY_IDENTITY_MAX_BYTES = 256;
|
|
89
|
+
/** Minimum HMAC secret bytes. */
|
|
44
90
|
export const HMAC_MIN_BYTES = 32;
|
|
91
|
+
/** Maximum HMAC secret bytes. */
|
|
45
92
|
export const HMAC_MAX_BYTES = 16_384;
|
|
46
93
|
// Owned codec traversal ceilings; complete container bytes remain bounded above.
|
|
94
|
+
/** Maximum DER container nesting depth. */
|
|
47
95
|
export const DER_MAX_DEPTH = 32;
|
|
96
|
+
/** Maximum DER traversal nodes. */
|
|
48
97
|
export const DER_MAX_NODES = 4096;
|
|
98
|
+
/** Maximum JWK algorithm identifier bytes. */
|
|
49
99
|
export const JWK_ALGORITHM_MAX_BYTES = 64;
|
|
100
|
+
/** Maximum JWK operation identifiers. */
|
|
50
101
|
export const JWK_MAX_OPERATIONS = 8;
|
|
102
|
+
/** Maximum custody signature bytes. */
|
|
51
103
|
export const CUSTODY_SIGNATURE_MAX_BYTES = 16_384; // RequestSeal.Custody verify and successful signature bounds.
|
|
104
|
+
/** Maximum combined HTTP header and trailer occurrences. */
|
|
52
105
|
export const MESSAGE_MAX_OCCURRENCES = 1024; // Headers and trailers combined.
|
|
106
|
+
/** Maximum HTTP method or field-name bytes. */
|
|
53
107
|
export const MESSAGE_MAX_TOKEN_BYTES = 256; // Method and field names.
|
|
108
|
+
/** Maximum HTTP scheme bytes. */
|
|
54
109
|
export const MESSAGE_MAX_SCHEME_BYTES = 64;
|
|
110
|
+
/** Maximum HTTP authority bytes. */
|
|
55
111
|
export const MESSAGE_MAX_AUTHORITY_BYTES = 1024;
|
|
112
|
+
/** Maximum combined HTTP URL bytes. */
|
|
56
113
|
export const MESSAGE_MAX_URL_BYTES = MESSAGE_MAX_TARGET_BYTES + MESSAGE_MAX_AUTHORITY_BYTES + MESSAGE_MAX_SCHEME_BYTES + 8;
|
|
114
|
+
/** Default maximum signature dictionary members. */
|
|
57
115
|
export const SIGNATURE_DEFAULT_MAX_MEMBERS = 16; // RequestSeal.Authentication signing dictionary ceiling.
|
|
116
|
+
/** Maximum freshness clock skew in seconds. */
|
|
58
117
|
export const FRESHNESS_MAX_SKEW_SECONDS = 86_400; // RequestSeal.Policy.
|
|
118
|
+
/** Random nonce bytes generated during signing. */
|
|
59
119
|
export const SIGNING_NONCE_BYTES = 32; // RequestSeal.Signing entropy length.
|
package/dist/context.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { type Result } from './result.ts';
|
|
2
|
+
/** A branded deadline and cancellation signal for an operation. */
|
|
2
3
|
export type Context = Readonly<{
|
|
3
4
|
deadline: number;
|
|
4
5
|
signal: AbortSignal;
|
|
5
6
|
}>;
|
|
7
|
+
/** Check the private brand of an operation context. */
|
|
6
8
|
export declare function isContext(value: unknown): value is Context;
|
|
9
|
+
/** Create a bounded context from timeout, parent, and cancellation options. */
|
|
7
10
|
export declare function withDeadline(options?: unknown): Result<Context>;
|
|
8
11
|
/** Internal ownership release for a settled child; never releases its parent. */
|
|
9
12
|
export declare function releaseContext(context: Context): void;
|
|
13
|
+
/** Return the context's remaining time, or zero after cancellation. */
|
|
10
14
|
export declare function remainingMs(context: Context): number;
|
|
11
15
|
/** Runs share the absolute deadline. Each run holds a referenced timer until it
|
|
12
16
|
* settles or the deadline passes, so a callback that never settles still ends with
|
package/dist/context.js
CHANGED
|
@@ -20,9 +20,11 @@ function then(promise, fulfilled, rejected) {
|
|
|
20
20
|
}
|
|
21
21
|
const contexts = new WeakSet();
|
|
22
22
|
const lifetimes = new WeakMap();
|
|
23
|
+
/** Check the private brand of an operation context. */
|
|
23
24
|
export function isContext(value) {
|
|
24
25
|
return typeof value === 'object' && value !== null && contexts.has(value);
|
|
25
26
|
}
|
|
27
|
+
/** Create a bounded context from timeout, parent, and cancellation options. */
|
|
26
28
|
export function withDeadline(options = {}) {
|
|
27
29
|
try {
|
|
28
30
|
const snapshot = snapshotOptions(options, ['signal', 'timeoutMs', 'parent']);
|
|
@@ -129,6 +131,7 @@ export function withDeadline(options = {}) {
|
|
|
129
131
|
export function releaseContext(context) {
|
|
130
132
|
lifetimes.get(context)?.dispose();
|
|
131
133
|
}
|
|
134
|
+
/** Return the context's remaining time, or zero after cancellation. */
|
|
132
135
|
export function remainingMs(context) {
|
|
133
136
|
return isContext(context) && !context.signal.aborted ? Math.max(0, context.deadline - now()) : 0;
|
|
134
137
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { type Result } from '../result.ts';
|
|
2
|
+
/** Supported HTTP message signature algorithm tokens. */
|
|
2
3
|
export type HttpAlgorithm = 'rsa-pss-sha512' | 'rsa-v1_5-sha256' | 'hmac-sha256' | 'ecdsa-p256-sha256' | 'ecdsa-p384-sha384' | 'ed25519';
|
|
4
|
+
/** Supported JOSE signing algorithm identifiers. */
|
|
3
5
|
export type JwsAlgorithm = 'RS256' | 'PS256' | 'PS384' | 'PS512' | 'HS256' | 'ES256' | 'ES384' | 'EdDSA';
|
|
6
|
+
/** An HTTP algorithm token or an explicit JWS algorithm selector. */
|
|
4
7
|
export type SigningAlgorithm = HttpAlgorithm | Readonly<{
|
|
5
8
|
jws: JwsAlgorithm;
|
|
6
9
|
}>;
|
|
10
|
+
/** An explicit RSA-OAEP JWE unwrapping selector. */
|
|
7
11
|
export type UnwrapAlgorithm = Readonly<{
|
|
8
12
|
jwe: 'RSA-OAEP' | 'RSA-OAEP-256';
|
|
9
13
|
}>;
|
|
14
|
+
/** A signing or key-unwrapping algorithm selector. */
|
|
10
15
|
export type CustodyAlgorithm = SigningAlgorithm | UnwrapAlgorithm;
|
|
11
16
|
export type Spec = Readonly<{
|
|
12
17
|
id: string;
|
package/dist/crypto/index.d.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { type Result } from '../result.ts';
|
|
2
2
|
import { algorithms } from './algorithm.ts';
|
|
3
|
-
|
|
4
|
-
export declare function
|
|
3
|
+
/** Sign bounded bytes with a branded custody handle. */
|
|
4
|
+
export declare function sign(algorithm: unknown, key: unknown, data: unknown, options?: unknown): Promise<Result<Uint8Array<ArrayBuffer>>>;
|
|
5
|
+
/** Verify a signature using a branded key or custody handle. */
|
|
6
|
+
export declare function verify(algorithm: unknown, key: unknown, signature: unknown, data: unknown, options?: unknown): Promise<Result<true>>;
|
|
7
|
+
/** Return the fixed maximum cryptographic input byte limit. */
|
|
8
|
+
export declare function maxBytesCeiling(): number;
|
|
9
|
+
/** Bounded cryptographic signing and verification operations. */
|
|
5
10
|
export declare const Crypto: Readonly<{
|
|
11
|
+
/** List supported HTTP signature algorithm tokens. */
|
|
6
12
|
algorithms: typeof algorithms;
|
|
13
|
+
/** Sign bounded bytes with a branded custody handle. */
|
|
7
14
|
sign: typeof sign;
|
|
15
|
+
/** Verify a signature using a branded key or custody handle. */
|
|
8
16
|
verify: typeof verify;
|
|
17
|
+
/** Return the fixed maximum cryptographic input byte limit. */
|
|
18
|
+
maxBytesCeiling: typeof maxBytesCeiling;
|
|
9
19
|
}>;
|
package/dist/crypto/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { byteLimit } from './options.js';
|
|
2
|
+
import { CRYPTO_MAX_BYTES_CEILING, SIGNATURE_MAX_BYTES } from '../constants.js';
|
|
2
3
|
import { err } from '../result.js';
|
|
3
4
|
import { cryptoError } from '../errors/types.js';
|
|
4
5
|
import { isKeyHandle, verifyHandle } from '../key_handle.js';
|
|
5
6
|
import { isPublicKey, verifyPublicKey, bindPublicKey } from '../public_key.js';
|
|
6
7
|
import { withDeadline } from '../context.js';
|
|
7
8
|
import { resolveAlgorithm, algorithms } from './algorithm.js';
|
|
8
|
-
import { dataBytes } from './primitive.js';
|
|
9
|
+
import { dataBytes, boundedData, boundedBinary } from './primitive.js';
|
|
9
10
|
function normalize(r) {
|
|
10
11
|
if (r.ok)
|
|
11
12
|
return r;
|
|
@@ -17,6 +18,7 @@ function normalize(r) {
|
|
|
17
18
|
'unsupported_algorithm',
|
|
18
19
|
'unsupported_format',
|
|
19
20
|
'invalid_key',
|
|
21
|
+
'invalid_options',
|
|
20
22
|
'key_mismatch',
|
|
21
23
|
'invalid_data',
|
|
22
24
|
'invalid_signature',
|
|
@@ -24,22 +26,32 @@ function normalize(r) {
|
|
|
24
26
|
? reason
|
|
25
27
|
: 'invalid_key'));
|
|
26
28
|
}
|
|
27
|
-
|
|
29
|
+
/** Sign bounded bytes with a branded custody handle. */
|
|
30
|
+
export async function sign(algorithm, key, data, options = {}) {
|
|
31
|
+
const maximum = byteLimit(options);
|
|
32
|
+
if (!maximum.ok)
|
|
33
|
+
return maximum;
|
|
28
34
|
const spec = resolveAlgorithm(algorithm);
|
|
29
35
|
if (!spec.ok)
|
|
30
36
|
return spec;
|
|
37
|
+
if (!boundedData(data, maximum.value))
|
|
38
|
+
return err(cryptoError('invalid_data'));
|
|
31
39
|
if (!isKeyHandle(key))
|
|
32
40
|
return err(cryptoError('invalid_key'));
|
|
33
41
|
const c = withDeadline();
|
|
34
42
|
if (!c.ok)
|
|
35
43
|
return err(cryptoError('invalid_key'));
|
|
36
|
-
return normalize(await key.sign(algorithm, data, c.value));
|
|
44
|
+
return normalize(await key.sign(algorithm, data, c.value, { maxBytes: maximum.value }));
|
|
37
45
|
}
|
|
38
|
-
|
|
46
|
+
/** Verify a signature using a branded key or custody handle. */
|
|
47
|
+
export async function verify(algorithm, key, signature, data, options = {}) {
|
|
48
|
+
const maximum = byteLimit(options);
|
|
49
|
+
if (!maximum.ok)
|
|
50
|
+
return maximum;
|
|
39
51
|
const spec = resolveAlgorithm(algorithm);
|
|
40
52
|
if (!spec.ok)
|
|
41
53
|
return spec;
|
|
42
|
-
const bytes = dataBytes(data);
|
|
54
|
+
const bytes = dataBytes(data, maximum.value);
|
|
43
55
|
if (!bytes.ok)
|
|
44
56
|
return bytes;
|
|
45
57
|
try {
|
|
@@ -55,15 +67,14 @@ export async function verify(algorithm, key, signature, data) {
|
|
|
55
67
|
}
|
|
56
68
|
else
|
|
57
69
|
return err(cryptoError('invalid_key'));
|
|
58
|
-
if (!(signature
|
|
59
|
-
signature.length > SIGNATURE_MAX_BYTES)
|
|
70
|
+
if (!boundedBinary(signature, SIGNATURE_MAX_BYTES))
|
|
60
71
|
return err(cryptoError('invalid_signature'));
|
|
61
72
|
if (isPublicKey(key))
|
|
62
|
-
return await verifyPublicKey(key, algorithm, signature, bytes.value);
|
|
73
|
+
return await verifyPublicKey(key, algorithm, signature, bytes.value, maximum.value);
|
|
63
74
|
const c = withDeadline();
|
|
64
75
|
if (!c.ok)
|
|
65
76
|
return c;
|
|
66
|
-
return normalize(await verifyHandle(key, algorithm, signature, bytes.value, c.value));
|
|
77
|
+
return normalize(await verifyHandle(key, algorithm, signature, bytes.value, c.value, { maxBytes: maximum.value }));
|
|
67
78
|
}
|
|
68
79
|
catch {
|
|
69
80
|
return err(cryptoError('invalid_signature'));
|
|
@@ -72,4 +83,16 @@ export async function verify(algorithm, key, signature, data) {
|
|
|
72
83
|
bytes.value.fill(0);
|
|
73
84
|
}
|
|
74
85
|
}
|
|
75
|
-
|
|
86
|
+
/** Return the fixed maximum cryptographic input byte limit. */
|
|
87
|
+
export function maxBytesCeiling() { return CRYPTO_MAX_BYTES_CEILING; }
|
|
88
|
+
/** Bounded cryptographic signing and verification operations. */
|
|
89
|
+
export const Crypto = Object.freeze({
|
|
90
|
+
/** List supported HTTP signature algorithm tokens. */
|
|
91
|
+
algorithms,
|
|
92
|
+
/** Sign bounded bytes with a branded custody handle. */
|
|
93
|
+
sign,
|
|
94
|
+
/** Verify a signature using a branded key or custody handle. */
|
|
95
|
+
verify,
|
|
96
|
+
/** Return the fixed maximum cryptographic input byte limit. */
|
|
97
|
+
maxBytesCeiling
|
|
98
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { snapshotOptions } from '../brand.js';
|
|
2
|
+
import { CRYPTO_MAX_BYTES_CEILING, CRYPTO_DEFAULT_MAX_BYTES } from '../constants.js';
|
|
3
|
+
import { cryptoError } from '../errors/types.js';
|
|
4
|
+
import { err, ok } from '../result.js';
|
|
5
|
+
/** Read caller-owned operation options once, before algorithm or key resolution. */
|
|
6
|
+
export function byteLimit(options) {
|
|
7
|
+
const snapshot = snapshotOptions(options, ['maxBytes']);
|
|
8
|
+
if (!snapshot.ok)
|
|
9
|
+
return err(cryptoError('invalid_options'));
|
|
10
|
+
const value = Object.hasOwn(snapshot.value, 'maxBytes')
|
|
11
|
+
? snapshot.value.maxBytes : CRYPTO_DEFAULT_MAX_BYTES;
|
|
12
|
+
return typeof value === 'number' && Number.isInteger(value) &&
|
|
13
|
+
value >= 1 && value <= CRYPTO_MAX_BYTES_CEILING
|
|
14
|
+
? ok(value) : err(cryptoError('invalid_options'));
|
|
15
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type Result } from '../result.ts';
|
|
2
2
|
import { type Spec } from './algorithm.ts';
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function binaryByteLength(input: Uint8Array): number;
|
|
4
|
+
export declare function boundedBinary(input: unknown, maximum: number): input is Uint8Array;
|
|
5
|
+
/** Check the octet-string or intrinsic binary size without allocating a copy. */
|
|
6
|
+
export declare function boundedData(input: unknown, maximum: number): boolean;
|
|
7
|
+
export declare function dataBytes(input: unknown, maximum?: number): Result<Uint8Array<ArrayBuffer>>;
|
|
4
8
|
export declare function unsigned(bytes: Uint8Array, little?: boolean): bigint;
|
|
5
|
-
export declare function signKey(spec: Spec, key: CryptoKey, input: unknown): Promise<Result<Uint8Array<ArrayBuffer>>>;
|
|
6
|
-
export declare function verifyKey(spec: Spec, key: CryptoKey, signature: unknown, input: unknown): Promise<Result<true>>;
|
|
9
|
+
export declare function signKey(spec: Spec, key: CryptoKey, input: unknown, maximum?: number): Promise<Result<Uint8Array<ArrayBuffer>>>;
|
|
10
|
+
export declare function verifyKey(spec: Spec, key: CryptoKey, signature: unknown, input: unknown, maximum?: number): Promise<Result<true>>;
|
package/dist/crypto/primitive.js
CHANGED
|
@@ -1,16 +1,41 @@
|
|
|
1
1
|
import { byteString } from '../bytes.js';
|
|
2
|
-
import {
|
|
2
|
+
import { CRYPTO_DEFAULT_MAX_BYTES, SIGNATURE_MAX_BYTES } from '../constants.js';
|
|
3
3
|
import { cryptoError } from '../errors/types.js';
|
|
4
4
|
import { err, ok } from '../result.js';
|
|
5
5
|
import { CURVE_ORDERS, ED25519_ORDER, operationParameters, } from './algorithm.js';
|
|
6
6
|
import * as subtle from './subtle.js';
|
|
7
|
-
|
|
7
|
+
// Caller properties can shadow length and byteLength; capture the shared TypedArray getter.
|
|
8
|
+
// Uint16Array reaches that intrinsic even when Uint8Array allocations are instrumented.
|
|
9
|
+
const byteLength = Object.getOwnPropertyDescriptor(Object.getPrototypeOf(Uint16Array.prototype), 'byteLength').get;
|
|
10
|
+
export function binaryByteLength(input) {
|
|
11
|
+
return byteLength.call(input);
|
|
12
|
+
}
|
|
13
|
+
export function boundedBinary(input, maximum) {
|
|
14
|
+
try {
|
|
15
|
+
return input instanceof Uint8Array && binaryByteLength(input) <= maximum;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/** Check the octet-string or intrinsic binary size without allocating a copy. */
|
|
22
|
+
export function boundedData(input, maximum) {
|
|
23
|
+
if (typeof input !== 'string')
|
|
24
|
+
return boundedBinary(input, maximum);
|
|
25
|
+
if (input.length > maximum)
|
|
26
|
+
return false;
|
|
27
|
+
// Mirrors byteString's octet-string rule without allocating the copy it makes.
|
|
28
|
+
for (let i = 0; i < input.length; i++)
|
|
29
|
+
if (input.charCodeAt(i) > 255)
|
|
30
|
+
return false;
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
export function dataBytes(input, maximum = CRYPTO_DEFAULT_MAX_BYTES) {
|
|
8
34
|
try {
|
|
9
|
-
if (
|
|
10
|
-
input.length > SIGNATURE_BASE_MAX_BYTES)
|
|
35
|
+
if (!boundedData(input, maximum))
|
|
11
36
|
return err(cryptoError('invalid_data'));
|
|
12
37
|
const r = byteString(input);
|
|
13
|
-
return r.ok ? r : err(cryptoError('invalid_data'));
|
|
38
|
+
return r.ok && r.value.length <= maximum ? r : err(cryptoError('invalid_data'));
|
|
14
39
|
}
|
|
15
40
|
catch {
|
|
16
41
|
return err(cryptoError('invalid_data'));
|
|
@@ -37,7 +62,8 @@ function signatureValid(spec, key, bytes) {
|
|
|
37
62
|
const width = spec.family === 'rsa'
|
|
38
63
|
? Math.ceil(key.algorithm.modulusLength / 8)
|
|
39
64
|
: spec.width;
|
|
40
|
-
|
|
65
|
+
const size = binaryByteLength(bytes);
|
|
66
|
+
if (size !== width || size > SIGNATURE_MAX_BYTES)
|
|
41
67
|
return false;
|
|
42
68
|
if (spec.family === 'ec') {
|
|
43
69
|
const r = unsigned(bytes.subarray(0, width / 2)), s = unsigned(bytes.subarray(width / 2)), order = CURVE_ORDERS[spec.curve];
|
|
@@ -47,8 +73,8 @@ function signatureValid(spec, key, bytes) {
|
|
|
47
73
|
return unsigned(bytes.subarray(32), true) < ED25519_ORDER;
|
|
48
74
|
return true;
|
|
49
75
|
}
|
|
50
|
-
export async function signKey(spec, key, input) {
|
|
51
|
-
const data = dataBytes(input);
|
|
76
|
+
export async function signKey(spec, key, input, maximum = CRYPTO_DEFAULT_MAX_BYTES) {
|
|
77
|
+
const data = dataBytes(input, maximum);
|
|
52
78
|
if (!data.ok)
|
|
53
79
|
return data;
|
|
54
80
|
try {
|
|
@@ -66,8 +92,8 @@ export async function signKey(spec, key, input) {
|
|
|
66
92
|
data.value.fill(0);
|
|
67
93
|
}
|
|
68
94
|
}
|
|
69
|
-
export async function verifyKey(spec, key, signature, input) {
|
|
70
|
-
const data = dataBytes(input);
|
|
95
|
+
export async function verifyKey(spec, key, signature, input, maximum = CRYPTO_DEFAULT_MAX_BYTES) {
|
|
96
|
+
const data = dataBytes(input, maximum);
|
|
71
97
|
if (!data.ok)
|
|
72
98
|
return data;
|
|
73
99
|
try {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { type Context } from '../context.ts';
|
|
2
2
|
import { type Result } from '../result.ts';
|
|
3
|
+
/** A confined AES decryption capability without an exported key. */
|
|
3
4
|
export type UnwrappedKey = Readonly<{
|
|
4
5
|
length: 128 | 256;
|
|
6
|
+
/** Authenticate and decrypt content using confined key material. */
|
|
5
7
|
decrypt: (ciphertext: unknown, options: unknown, context: Context) => Promise<Result<Uint8Array<ArrayBuffer>>>;
|
|
8
|
+
/** Release retained custody and invalidate the capability. */
|
|
6
9
|
release: () => Promise<Result<true>>;
|
|
7
10
|
}>;
|
|
11
|
+
/** Check the private brand of an unwrapped content key. */
|
|
8
12
|
export declare function isUnwrappedKey(value: unknown): value is UnwrappedKey;
|
|
9
13
|
export declare function confineContentKey(key: CryptoKey): UnwrappedKey;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { denoInspection } from '../brand.js';
|
|
1
2
|
import { snapshotOptions } from '../brand.js';
|
|
2
3
|
import { isContext, remainingMs, run } from '../context.js';
|
|
3
4
|
import { JOSE_MAX_PLAINTEXT_BYTES, JOSE_MAX_COMPACT_BYTES, } from '../constants.js';
|
|
@@ -6,6 +7,7 @@ import { err, ok } from '../result.js';
|
|
|
6
7
|
import * as subtle from '../crypto/subtle.js';
|
|
7
8
|
const keys = new WeakSet();
|
|
8
9
|
const token = Object.freeze({});
|
|
10
|
+
/** Check the private brand of an unwrapped content key. */
|
|
9
11
|
export function isUnwrappedKey(value) {
|
|
10
12
|
return typeof value === 'object' && value !== null && keys.has(value);
|
|
11
13
|
}
|
|
@@ -88,8 +90,8 @@ class ContentKey {
|
|
|
88
90
|
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
89
91
|
return this.toJSON();
|
|
90
92
|
}
|
|
91
|
-
[Symbol.for('Deno.customInspect')]() {
|
|
92
|
-
return this.toJSON();
|
|
93
|
+
[Symbol.for('Deno.customInspect')](render) {
|
|
94
|
+
return denoInspection(render, this.toJSON());
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
97
|
export function confineContentKey(key) {
|
package/dist/custody/index.d.ts
CHANGED
package/dist/custody/index.js
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import { local } from './local.js';
|
|
2
|
-
|
|
2
|
+
/** Caller-owned local cryptographic custody constructors. */
|
|
3
|
+
export const Custody = Object.freeze({
|
|
4
|
+
/** Import caller-owned local key material into a confined capability. */
|
|
5
|
+
local
|
|
6
|
+
});
|
package/dist/custody/local.d.ts
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
import { type Result } from '../result.ts';
|
|
2
|
-
import { type
|
|
2
|
+
import { type Context } from '../context.ts';
|
|
3
|
+
import { type KeyHandle, type Custodian } from '../key_handle.ts';
|
|
4
|
+
import { KeyIdentity } from '../key_identity.ts';
|
|
5
|
+
import { PublicKey } from '../public_key.ts';
|
|
6
|
+
import { type Spec } from '../crypto/algorithm.ts';
|
|
7
|
+
import { type UnwrappedKey } from './content_key.ts';
|
|
8
|
+
/** @internal Direct custodian implementation; use the public factory for handles. */
|
|
9
|
+
export declare class LocalPort implements Custodian {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(key: CryptoKey, verify: CryptoKey | undefined, publicKey: PublicKey | undefined, identity: KeyIdentity, spec: Spec);
|
|
12
|
+
sign(_algorithm: unknown, data: unknown, _context: Context): Promise<Result<Uint8Array<ArrayBuffer>>>;
|
|
13
|
+
verify(_algorithm: unknown, signature: unknown, data: unknown, _context: Context): Promise<Result<true>>;
|
|
14
|
+
publicKey(): Promise<Result<PublicKey>>;
|
|
15
|
+
identity(): Promise<Result<KeyIdentity>>;
|
|
16
|
+
unwrap(_algorithm: unknown, wrappedKey: unknown, _context: Context, options?: unknown): Promise<Result<UnwrappedKey>>;
|
|
17
|
+
release(): Promise<Result<true>>;
|
|
18
|
+
}
|
|
3
19
|
/** Import explicitly owned material. All persistent CryptoKey references stay in private fields. */
|
|
4
20
|
export declare function local(algorithm: unknown, material: unknown, options?: unknown): Promise<Result<KeyHandle>>;
|
package/dist/custody/local.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { snapshotOptions } from '../brand.js';
|
|
2
|
-
import { HMAC_MIN_BYTES, HMAC_MAX_BYTES, KEY_MAX_BYTES, SIGNATURE_MAX_BYTES, } from '../constants.js';
|
|
2
|
+
import { CRYPTO_MAX_BYTES_CEILING, HMAC_MIN_BYTES, HMAC_MAX_BYTES, KEY_MAX_BYTES, SIGNATURE_MAX_BYTES, } from '../constants.js';
|
|
3
3
|
import { custodyError } from '../errors/types.js';
|
|
4
4
|
import { err, ok } from '../result.js';
|
|
5
5
|
import { withDeadline, run } from '../context.js';
|
|
@@ -7,12 +7,13 @@ import { createKeyHandle, } from '../key_handle.js';
|
|
|
7
7
|
import { KeyIdentity } from '../key_identity.js';
|
|
8
8
|
import { PublicKey } from '../public_key.js';
|
|
9
9
|
import { resolveAlgorithm, selection, importParameters, } from '../crypto/algorithm.js';
|
|
10
|
-
import { signKey, verifyKey } from '../crypto/primitive.js';
|
|
10
|
+
import { boundedBinary, signKey, verifyKey } from '../crypto/primitive.js';
|
|
11
11
|
import * as subtle from '../crypto/subtle.js';
|
|
12
12
|
import { decodeKey, publicSpki } from './der.js';
|
|
13
13
|
import { component, snapshotJwk, publicMembers, publicMetadata, compatible, permitted, } from './jwk.js';
|
|
14
14
|
import { confineContentKey } from './content_key.js';
|
|
15
|
-
|
|
15
|
+
/** @internal Direct custodian implementation; use the public factory for handles. */
|
|
16
|
+
export class LocalPort {
|
|
16
17
|
#key;
|
|
17
18
|
#verify;
|
|
18
19
|
#public;
|
|
@@ -27,13 +28,17 @@ class LocalPort {
|
|
|
27
28
|
Object.freeze(this);
|
|
28
29
|
}
|
|
29
30
|
async sign(_algorithm, data, _context) {
|
|
31
|
+
if (!boundedBinary(data, CRYPTO_MAX_BYTES_CEILING))
|
|
32
|
+
return err(custodyError('invalid_data'));
|
|
30
33
|
return this.#key
|
|
31
|
-
? signKey(this.#spec, this.#key, data)
|
|
34
|
+
? signKey(this.#spec, this.#key, data, CRYPTO_MAX_BYTES_CEILING)
|
|
32
35
|
: err(custodyError('key_not_found'));
|
|
33
36
|
}
|
|
34
37
|
async verify(_algorithm, signature, data, _context) {
|
|
38
|
+
if (!boundedBinary(data, CRYPTO_MAX_BYTES_CEILING))
|
|
39
|
+
return err(custodyError('invalid_data'));
|
|
35
40
|
return this.#verify
|
|
36
|
-
? verifyKey(this.#spec, this.#verify, signature, data)
|
|
41
|
+
? verifyKey(this.#spec, this.#verify, signature, data, CRYPTO_MAX_BYTES_CEILING)
|
|
37
42
|
: err(custodyError('key_not_found'));
|
|
38
43
|
}
|
|
39
44
|
async publicKey() {
|
|
@@ -2,6 +2,8 @@ import { type KeyHandle } from "../../key_handle.ts";
|
|
|
2
2
|
import { type Result } from "../../result.ts";
|
|
3
3
|
/** Caller-selected local socket and trusted public components; no environment discovery. */
|
|
4
4
|
export declare function createSshAgent(algorithm: unknown, socketPath: unknown, publicKey: unknown, options?: unknown): Promise<Result<KeyHandle>>;
|
|
5
|
+
/** Caller-selected OpenSSH agent custody construction. */
|
|
5
6
|
export declare const SSHAgent: Readonly<{
|
|
7
|
+
/** Bind an algorithm and public key to a caller-selected OpenSSH agent socket. */
|
|
6
8
|
create: typeof createSshAgent;
|
|
7
9
|
}>;
|