@request-seal/core 0.1.0 → 0.3.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 +49 -1
- package/README.md +342 -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 +23 -0
- package/dist/adapters/node-http/index.js +28 -8
- 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 +64 -4
- package/dist/constants.js +62 -2
- package/dist/context.d.ts +4 -0
- package/dist/context.js +3 -0
- package/dist/crypto/algorithm.d.ts +9 -0
- package/dist/crypto/algorithm.js +12 -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 +19 -2
- package/dist/discovery/body.js +36 -11
- package/dist/discovery/cache.d.ts +21 -2
- package/dist/discovery/cache.js +105 -39
- package/dist/discovery/headers.d.ts +3 -0
- package/dist/discovery/headers.js +27 -1
- package/dist/discovery/index.d.ts +13 -1
- package/dist/discovery/index.js +31 -7
- 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 +9 -1
- package/dist/discovery/node.js +150 -26
- package/dist/discovery/proof.js +2 -2
- package/dist/discovery/snapshot.js +3 -0
- 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 +44 -3
- package/dist/errors/parity.js +45 -4
- package/dist/errors/types.d.ts +17 -3
- package/dist/errors/types.js +16 -2
- 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 +32 -2
- 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 +22 -1
- package/dist/numbers.d.ts +6 -0
- package/dist/numbers.js +5 -0
- package/dist/policy.d.ts +27 -0
- package/dist/policy.js +42 -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/evaluation.d.ts +1 -1
- package/dist/quorum/evaluation.js +18 -4
- 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 +3 -0
- package/dist/replay/postgres.js +7 -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 +26 -0
- package/dist/sign.js +25 -3
- package/dist/signature-base/index.d.ts +2 -0
- package/dist/signature-base/index.js +5 -1
- package/dist/signature-fields.d.ts +5 -1
- package/dist/signature-fields.js +9 -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 +30 -8
- package/dist/verify.d.ts +23 -0
- package/dist/verify.js +46 -14
- 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/dist/web-bot-auth/verify.js +2 -2
- package/package.json +29 -6
package/dist/replay/claim.d.ts
CHANGED
|
@@ -4,7 +4,9 @@ export declare function validSeconds(value: unknown): value is number;
|
|
|
4
4
|
/** Exact signed-64-bit lower bound; bigint preserves ends outside safe numbers. */
|
|
5
5
|
export declare function validRetention(value: unknown): value is number | bigint;
|
|
6
6
|
export declare function boundedReplayBytes(v: unknown): v is Uint8Array;
|
|
7
|
+
/** Check the private brand of an atomic replay claim. */
|
|
7
8
|
export declare function isReplayClaim(value: unknown): value is ReplayClaim;
|
|
9
|
+
/** Copy, validate, and brand a replay claim. */
|
|
8
10
|
export declare function createClaim(input: unknown): Result<ReplayClaim>;
|
|
9
11
|
/** Exclusive expiration, inclusive max age. Authenticate the window before deriving retention. */
|
|
10
12
|
export declare function retentionEnd(input: unknown): Result<number>;
|
|
@@ -13,7 +15,10 @@ export declare function retainUntil(input: unknown): Result<number>;
|
|
|
13
15
|
/** Injective byte encoding, also suitable for PostgreSQL decode($n,'hex'). */
|
|
14
16
|
export declare function hex(bytes: Uint8Array): string;
|
|
15
17
|
export declare function claimRow(input: unknown): Result<readonly [string, string, string]>;
|
|
18
|
+
/** Replay-claim construction and private-brand validation. */
|
|
16
19
|
export declare const Claim: Readonly<{
|
|
20
|
+
/** Copy, validate, and brand an atomic replay claim. */
|
|
17
21
|
create: typeof createClaim;
|
|
22
|
+
/** Check the private brand of a replay claim. */
|
|
18
23
|
valid: typeof isReplayClaim;
|
|
19
24
|
}>;
|
package/dist/replay/claim.js
CHANGED
|
@@ -22,9 +22,11 @@ export function boundedReplayBytes(v) {
|
|
|
22
22
|
return length >= 1 && length <= KEY_IDENTITY_MAX_BYTES;
|
|
23
23
|
}
|
|
24
24
|
;
|
|
25
|
+
/** Check the private brand of an atomic replay claim. */
|
|
25
26
|
export function isReplayClaim(value) {
|
|
26
27
|
return typeof value === 'object' && value !== null && claims.has(value);
|
|
27
28
|
}
|
|
29
|
+
/** Copy, validate, and brand a replay claim. */
|
|
28
30
|
export function createClaim(input) {
|
|
29
31
|
try {
|
|
30
32
|
const r = snapshotOptions(input, ['namespace', 'key', 'retainUntil']);
|
|
@@ -68,4 +70,10 @@ export function claimRow(input) {
|
|
|
68
70
|
return err(replayError('invalid_claim'));
|
|
69
71
|
return ok(Object.freeze([hex(input.namespace), hex(input.key), input.retainUntil.toString()]));
|
|
70
72
|
}
|
|
71
|
-
|
|
73
|
+
/** Replay-claim construction and private-brand validation. */
|
|
74
|
+
export const Claim = Object.freeze({
|
|
75
|
+
/** Copy, validate, and brand an atomic replay claim. */
|
|
76
|
+
create: createClaim,
|
|
77
|
+
/** Check the private brand of a replay claim. */
|
|
78
|
+
valid: isReplayClaim
|
|
79
|
+
});
|
package/dist/replay/index.d.ts
CHANGED
|
@@ -3,10 +3,16 @@ import { retainUntil, claimRow } from './claim.ts';
|
|
|
3
3
|
export { Claim, createClaim, isReplayClaim } from './claim.ts';
|
|
4
4
|
export { isReplayReceipt } from './receipt.ts';
|
|
5
5
|
export { createReplayStore, isReplayStore } from './store.ts';
|
|
6
|
+
/** Atomic claim, expiration sweep, and retention helper operations. */
|
|
6
7
|
export declare const Replay: Readonly<{
|
|
8
|
+
/** Atomically reserve the claim or report that it was already claimed. */
|
|
7
9
|
claim: typeof claim;
|
|
10
|
+
/** Remove expired replay claims and return the removal count. */
|
|
8
11
|
sweep: typeof sweep;
|
|
12
|
+
/** Compute the retention deadline from validated freshness facts. */
|
|
9
13
|
retainUntil: typeof retainUntil;
|
|
14
|
+
/** Encode a branded claim as namespace, key, and retention database values. */
|
|
10
15
|
row: typeof claimRow;
|
|
16
|
+
/** Validate and brand a caller-owned atomic replay store. */
|
|
11
17
|
store: typeof createReplayStore;
|
|
12
18
|
}>;
|
package/dist/replay/index.js
CHANGED
|
@@ -3,4 +3,16 @@ import { retainUntil, claimRow } from './claim.js';
|
|
|
3
3
|
export { Claim, createClaim, isReplayClaim } from './claim.js';
|
|
4
4
|
export { isReplayReceipt } from './receipt.js';
|
|
5
5
|
export { createReplayStore, isReplayStore } from './store.js';
|
|
6
|
-
|
|
6
|
+
/** Atomic claim, expiration sweep, and retention helper operations. */
|
|
7
|
+
export const Replay = Object.freeze({
|
|
8
|
+
/** Atomically reserve the claim or report that it was already claimed. */
|
|
9
|
+
claim,
|
|
10
|
+
/** Remove expired replay claims and return the removal count. */
|
|
11
|
+
sweep,
|
|
12
|
+
/** Compute the retention deadline from validated freshness facts. */
|
|
13
|
+
retainUntil,
|
|
14
|
+
/** Encode a branded claim as namespace, key, and retention database values. */
|
|
15
|
+
row: claimRow,
|
|
16
|
+
/** Validate and brand a caller-owned atomic replay store. */
|
|
17
|
+
store: createReplayStore
|
|
18
|
+
});
|
|
@@ -5,6 +5,9 @@ export type Query = (sql: string, params: readonly string[], context: Readonly<{
|
|
|
5
5
|
signal: AbortSignal;
|
|
6
6
|
remainingMs: number;
|
|
7
7
|
}>) => Promise<number>;
|
|
8
|
+
/** Validate a table name and return replay-store creation SQL. */
|
|
8
9
|
export declare function ddl(table: unknown): Result<string>;
|
|
10
|
+
/** Validate a table name and return the retention-sweep index SQL: without it the sweep's DELETE is a growing full-table scan. The name is bounded to 46 bytes so the derived index identifier fits PostgreSQL's 63-byte limit. */
|
|
11
|
+
export declare function ddlIndex(table: unknown): Result<string>;
|
|
9
12
|
/** No database, pool, transaction, or timer starts here. Late commits remain indeterminate. */
|
|
10
13
|
export declare function createStore(options: unknown): Result<ReplayStore>;
|
package/dist/replay/postgres.js
CHANGED
|
@@ -5,9 +5,16 @@ import { ok, err } from '../result.js';
|
|
|
5
5
|
import { claimRow, isReplayClaim, validSeconds } from './claim.js';
|
|
6
6
|
import { createReplayStore, storeOperation } from './store.js';
|
|
7
7
|
const tableName = (value) => typeof value === 'string' && value.length >= 1 && value.length <= 63 && /^[a-z][a-z0-9_]*$/.test(value);
|
|
8
|
+
/** Validate a table name and return replay-store creation SQL. */
|
|
8
9
|
export function ddl(table) {
|
|
9
10
|
return tableName(table) ? ok(`CREATE TABLE "${table}" (namespace bytea NOT NULL, key bytea NOT NULL, retain_until bigint NOT NULL, PRIMARY KEY (namespace, key))`) : err(replayError('invalid_store'));
|
|
10
11
|
}
|
|
12
|
+
/** Validate a table name and return the retention-sweep index SQL: without it the sweep's DELETE is a growing full-table scan. The name is bounded to 46 bytes so the derived index identifier fits PostgreSQL's 63-byte limit. */
|
|
13
|
+
export function ddlIndex(table) {
|
|
14
|
+
return tableName(table) && table.length <= 46
|
|
15
|
+
? ok(`CREATE INDEX "${table}_retain_until_idx" ON "${table}" (retain_until)`)
|
|
16
|
+
: err(replayError('invalid_store'));
|
|
17
|
+
}
|
|
11
18
|
function failure(error) {
|
|
12
19
|
try {
|
|
13
20
|
if (error !== null && typeof error === 'object') {
|
package/dist/replay/receipt.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Result } from '../result.ts';
|
|
2
2
|
import type { ReplayReceipt } from '../replay-port.ts';
|
|
3
|
+
/** Check the private brand of a successful replay receipt. */
|
|
3
4
|
export declare function isReplayReceipt(value: unknown): value is ReplayReceipt;
|
|
4
5
|
export declare function createReceipt(input: unknown): Result<ReplayReceipt>;
|
package/dist/replay/receipt.js
CHANGED
|
@@ -4,6 +4,7 @@ import { ok, err } from '../result.js';
|
|
|
4
4
|
import { validRetention } from './claim.js';
|
|
5
5
|
// Verification produces safe-number ends; share the claim bounds for those ends.
|
|
6
6
|
const receipts = new WeakSet();
|
|
7
|
+
/** Check the private brand of a successful replay receipt. */
|
|
7
8
|
export function isReplayReceipt(value) {
|
|
8
9
|
return typeof value === 'object' && value !== null && receipts.has(value);
|
|
9
10
|
}
|
package/dist/replay/store.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Context } from '../context.ts';
|
|
2
2
|
import { type Result } from '../result.ts';
|
|
3
3
|
import type { ReplayStore } from '../replay-port.ts';
|
|
4
|
+
/** Check the private brand of a replay store. */
|
|
4
5
|
export declare function isReplayStore(value: unknown): value is ReplayStore;
|
|
5
6
|
/** Normalize caller ports once, binding own methods to the caller's receiver. */
|
|
6
7
|
export declare function createReplayStore(input: unknown): Result<ReplayStore>;
|
package/dist/replay/store.js
CHANGED
|
@@ -4,6 +4,7 @@ import { isRequestSealError, replayError } from '../errors/types.js';
|
|
|
4
4
|
import { ok, err } from '../result.js';
|
|
5
5
|
import { isReplayClaim, validSeconds } from './claim.js';
|
|
6
6
|
const stores = new WeakSet();
|
|
7
|
+
/** Check the private brand of a replay store. */
|
|
7
8
|
export function isReplayStore(value) {
|
|
8
9
|
return typeof value === 'object' && value !== null && stores.has(value);
|
|
9
10
|
}
|
package/dist/replay-port.d.ts
CHANGED
|
@@ -8,16 +8,21 @@ export type ReplayClaim = Readonly<{
|
|
|
8
8
|
}>;
|
|
9
9
|
/** Bounded callback vocabulary; normalized by Replay.claim and Replay.sweep. */
|
|
10
10
|
export type ReplayStoreFailure = "unavailable" | "timeout" | "full" | "failure";
|
|
11
|
+
/** A store result with branded errors or the bounded callback failure vocabulary. */
|
|
11
12
|
export type ReplayStoreResult<T> = Result<T> | Readonly<{
|
|
12
13
|
ok: false;
|
|
13
14
|
error: ReplayStoreFailure;
|
|
14
15
|
}>;
|
|
16
|
+
/** A caller-owned atomic claim and expiration-sweep port. */
|
|
15
17
|
export type ReplayStore = Readonly<{
|
|
16
18
|
/** Public adapter identity; unnamed caller implementations use "caller". */
|
|
17
19
|
identity?: string;
|
|
20
|
+
/** Atomically reserve the claim or report that it was already claimed. */
|
|
18
21
|
claim: (claim: ReplayClaim, context: Context) => ReplayStoreResult<"claimed" | "already_claimed"> | Promise<ReplayStoreResult<"claimed" | "already_claimed">>;
|
|
22
|
+
/** Remove expired replay claims and return the removal count. */
|
|
19
23
|
sweep: (now: number, context: Context) => ReplayStoreResult<number> | Promise<ReplayStoreResult<number>>;
|
|
20
24
|
}>;
|
|
25
|
+
/** Verified signature and profile facts supplied to a replay commitment callback. */
|
|
21
26
|
export type ReplayFacts = Readonly<{
|
|
22
27
|
identifier: string;
|
|
23
28
|
algorithm: import("./crypto/algorithm.ts").SigningAlgorithm;
|
|
@@ -27,7 +32,7 @@ export type ReplayFacts = Readonly<{
|
|
|
27
32
|
expires: number | null;
|
|
28
33
|
profile: Readonly<{
|
|
29
34
|
name: "rfc9421";
|
|
30
|
-
}
|
|
35
|
+
}> | import("./profile.ts").ProfileStamp;
|
|
31
36
|
}>;
|
|
32
37
|
/** Successful atomic claim; contains only public adapter identity and retention. */
|
|
33
38
|
export type ReplayReceipt = Readonly<{
|
package/dist/result.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type RequestSealError } from './errors/types.ts';
|
|
2
|
+
/** A success value or a branded, bounded error. */
|
|
2
3
|
export type Result<T> = {
|
|
3
4
|
readonly ok: true;
|
|
4
5
|
readonly value: T;
|
|
@@ -6,5 +7,7 @@ export type Result<T> = {
|
|
|
6
7
|
readonly ok: false;
|
|
7
8
|
readonly error: RequestSealError;
|
|
8
9
|
};
|
|
10
|
+
/** Freeze a successful result containing the supplied value. */
|
|
9
11
|
export declare const ok: <T>(value: T) => Result<T>;
|
|
12
|
+
/** Freeze a failed result after checking its error brand. */
|
|
10
13
|
export declare const err: <T = never>(error: RequestSealError) => Result<T>;
|
package/dist/result.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { isRequestSealError } from './errors/types.js';
|
|
2
|
+
/** Freeze a successful result containing the supplied value. */
|
|
2
3
|
export const ok = (value) => Object.freeze({ ok: true, value });
|
|
4
|
+
/** Freeze a failed result after checking its error brand. */
|
|
3
5
|
export const err = (error) => {
|
|
4
6
|
if (!isRequestSealError(error))
|
|
5
7
|
throw new TypeError('Unbranded Result error');
|
package/dist/sign.d.ts
CHANGED
|
@@ -3,12 +3,15 @@ import { type SigningAlgorithm } from "./crypto/algorithm.ts";
|
|
|
3
3
|
import { type Message as HttpMessage } from "./message/index.ts";
|
|
4
4
|
import { type InnerList } from "./structured-fields/index.ts";
|
|
5
5
|
import { type Result } from "./result.ts";
|
|
6
|
+
/** A caller-owned signing callback receiving the algorithm, base bytes, and context. */
|
|
6
7
|
export type Signer = (algorithm: SigningAlgorithm, base: Uint8Array, context: Context) => Result<Uint8Array> | Promise<Result<Uint8Array>>;
|
|
8
|
+
/** A label, signature-input value, and signing algorithm. */
|
|
7
9
|
export type SignatureSpec = Readonly<{
|
|
8
10
|
label: string;
|
|
9
11
|
signatureInput: string | Uint8Array | InnerList;
|
|
10
12
|
algorithm: SigningAlgorithm;
|
|
11
13
|
}>;
|
|
14
|
+
/** Explicit freshness, key identifier, nonce, and tag parameters for signing. */
|
|
12
15
|
export type SigningParameters = Readonly<{
|
|
13
16
|
created?: boolean;
|
|
14
17
|
expiresIn: number | null;
|
|
@@ -17,6 +20,7 @@ export type SigningParameters = Readonly<{
|
|
|
17
20
|
keyid?: string | null;
|
|
18
21
|
tag?: string | null;
|
|
19
22
|
}>;
|
|
23
|
+
/** Components, algorithm, and optional digest and freshness settings for signing. */
|
|
20
24
|
export type SigningSpec = Readonly<{
|
|
21
25
|
label: string;
|
|
22
26
|
components: string;
|
|
@@ -31,4 +35,26 @@ export type SigningSpec = Readonly<{
|
|
|
31
35
|
digest?: readonly string[] | null;
|
|
32
36
|
fieldSchemas?: unknown;
|
|
33
37
|
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Sign a lossless HTTP message with explicit components and caller-owned custody.
|
|
40
|
+
*
|
|
41
|
+
* Appends one `Signature-Input` and one `Signature` field occurrence and returns a new
|
|
42
|
+
* branded Message. The input message is never modified, and existing signatures stay.
|
|
43
|
+
*
|
|
44
|
+
* @param message - A branded Message from `Message.request`, `Message.response`, or `Message.create`.
|
|
45
|
+
* @param spec - A generated spec with `label`, `components`, `algorithm`, and `expiresIn`, plus
|
|
46
|
+
* optional `created`, `nonce`, `alg`, `keyid`, `tag` (or the same choices nested under
|
|
47
|
+
* `parameters`), `digest`, and `fieldSchemas`; or an exact-input spec with only `label`,
|
|
48
|
+
* `signatureInput`, and `algorithm`.
|
|
49
|
+
* @param signer - A KeyHandle for the same algorithm, or a {@link Signer} callback.
|
|
50
|
+
* @param options - Generated specs accept `clock`, `nonce`, `signingTimeout`, and `context`;
|
|
51
|
+
* exact-input specs accept `fieldSchemas`, `signingTimeout`, and `context`. `signingTimeout`
|
|
52
|
+
* is 1 through 300,000 milliseconds (default 5,000).
|
|
53
|
+
* @returns `ok(message)`, or a `core` error such as `input.invalid_options`,
|
|
54
|
+
* `fields.label_in_use`, `input.signer_algorithm_mismatch`, `crypto.signing_failed`, or
|
|
55
|
+
* `crypto.signer_failed`.
|
|
56
|
+
* @see [Signing and verifying guide](https://github.com/baselabs/request-seal-typescript/blob/main/docs/guides/signing-and-verifying.md),
|
|
57
|
+
* [reference](https://github.com/baselabs/request-seal-typescript/blob/main/docs/reference/sign-and-verify.md), and the
|
|
58
|
+
* [error reasons](https://github.com/baselabs/request-seal-typescript/blob/main/docs/reference/errors.md).
|
|
59
|
+
*/
|
|
34
60
|
export declare function sign(message: unknown, spec: unknown, signer: unknown, options?: unknown): Promise<Result<HttpMessage>>;
|
package/dist/sign.js
CHANGED
|
@@ -11,7 +11,7 @@ import { build } from "./signature-base/index.js";
|
|
|
11
11
|
import { serialize, } from "./structured-fields/index.js";
|
|
12
12
|
import { checkDigest, digest } from "./digest.js";
|
|
13
13
|
import { fieldSchemas } from "./policy.js";
|
|
14
|
-
import { inner, label, dictionaries, signatureSchema, parameters, fail, ensure, bounded, } from "./signature-fields.js";
|
|
14
|
+
import { inner, label, dictionaries, signatureSchema, parameters, fail, ensure, bounded, envelope, } from "./signature-fields.js";
|
|
15
15
|
import { coreError, isRequestSealError } from "./errors/types.js";
|
|
16
16
|
import { timeoutMs } from "./numbers.js";
|
|
17
17
|
import { ok, err } from "./result.js";
|
|
@@ -36,6 +36,28 @@ function append(m, name, bytes) {
|
|
|
36
36
|
result(FieldOccurrence.create({ name, value: bytes, section: "headers" })),
|
|
37
37
|
]);
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Sign a lossless HTTP message with explicit components and caller-owned custody.
|
|
41
|
+
*
|
|
42
|
+
* Appends one `Signature-Input` and one `Signature` field occurrence and returns a new
|
|
43
|
+
* branded Message. The input message is never modified, and existing signatures stay.
|
|
44
|
+
*
|
|
45
|
+
* @param message - A branded Message from `Message.request`, `Message.response`, or `Message.create`.
|
|
46
|
+
* @param spec - A generated spec with `label`, `components`, `algorithm`, and `expiresIn`, plus
|
|
47
|
+
* optional `created`, `nonce`, `alg`, `keyid`, `tag` (or the same choices nested under
|
|
48
|
+
* `parameters`), `digest`, and `fieldSchemas`; or an exact-input spec with only `label`,
|
|
49
|
+
* `signatureInput`, and `algorithm`.
|
|
50
|
+
* @param signer - A KeyHandle for the same algorithm, or a {@link Signer} callback.
|
|
51
|
+
* @param options - Generated specs accept `clock`, `nonce`, `signingTimeout`, and `context`;
|
|
52
|
+
* exact-input specs accept `fieldSchemas`, `signingTimeout`, and `context`. `signingTimeout`
|
|
53
|
+
* is 1 through 300,000 milliseconds (default 5,000).
|
|
54
|
+
* @returns `ok(message)`, or a `core` error such as `input.invalid_options`,
|
|
55
|
+
* `fields.label_in_use`, `input.signer_algorithm_mismatch`, `crypto.signing_failed`, or
|
|
56
|
+
* `crypto.signer_failed`.
|
|
57
|
+
* @see [Signing and verifying guide](https://github.com/baselabs/request-seal-typescript/blob/main/docs/guides/signing-and-verifying.md),
|
|
58
|
+
* [reference](https://github.com/baselabs/request-seal-typescript/blob/main/docs/reference/sign-and-verify.md), and the
|
|
59
|
+
* [error reasons](https://github.com/baselabs/request-seal-typescript/blob/main/docs/reference/errors.md).
|
|
60
|
+
*/
|
|
39
61
|
export async function sign(message, spec, signer, options = {}) {
|
|
40
62
|
try {
|
|
41
63
|
const s = take(spec, [
|
|
@@ -116,7 +138,7 @@ export async function sign(message, spec, signer, options = {}) {
|
|
|
116
138
|
}
|
|
117
139
|
input = r.value;
|
|
118
140
|
if (Object.hasOwn(o, "context") || !isKeyHandle(signer)) {
|
|
119
|
-
c = Object.hasOwn(o, "context") ? (isContext(o.context) ? ok(o.context) : err(bounded(null, "invalid_options", "input"))) : withDeadline({ timeoutMs:
|
|
141
|
+
c = Object.hasOwn(o, "context") ? (isContext(o.context) ? ok(o.context) : err(bounded(null, "invalid_options", "input"))) : withDeadline({ timeoutMs: signingTimeout });
|
|
120
142
|
ensure(c.ok, "invalid_options", "input");
|
|
121
143
|
}
|
|
122
144
|
}
|
|
@@ -308,6 +330,6 @@ export async function sign(message, spec, signer, options = {}) {
|
|
|
308
330
|
catch (e) {
|
|
309
331
|
if (isRequestSealError(e) && e.kind === "custody" && e.reason === "deadline_exceeded")
|
|
310
332
|
return err(e);
|
|
311
|
-
return err(
|
|
333
|
+
return err(envelope(e));
|
|
312
334
|
}
|
|
313
335
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type Result } from '../result.ts';
|
|
2
2
|
/** Ordered identifiers and parameters are serialized exactly once for each output line. */
|
|
3
3
|
export declare function build(input: unknown, parameters: unknown, options?: unknown): Result<Uint8Array>;
|
|
4
|
+
/** RFC 9421 signature-base construction operations. */
|
|
4
5
|
export declare const SignatureBase: Readonly<{
|
|
6
|
+
/** Build RFC 9421 signature-base bytes from a lossless message. */
|
|
5
7
|
build: typeof build;
|
|
6
8
|
}>;
|
|
@@ -317,4 +317,8 @@ export function build(input, parameters, options = {}) {
|
|
|
317
317
|
: signatureBaseError('invalid_signature_parameters'));
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
|
-
|
|
320
|
+
/** RFC 9421 signature-base construction operations. */
|
|
321
|
+
export const SignatureBase = Object.freeze({
|
|
322
|
+
/** Build RFC 9421 signature-base bytes from a lossless message. */
|
|
323
|
+
build
|
|
324
|
+
});
|
|
@@ -10,11 +10,15 @@ export declare function bounded(e: unknown, reason: Reason<"core">, layer: Layer
|
|
|
10
10
|
reason: Reason<'core'>;
|
|
11
11
|
layer: Layer<'core'>;
|
|
12
12
|
correlation: string;
|
|
13
|
-
retryable:
|
|
13
|
+
retryable: boolean;
|
|
14
14
|
detail: CoreDetail | null;
|
|
15
15
|
message: string | null;
|
|
16
16
|
source: import("./errors/types.ts").CustodyError | null;
|
|
17
17
|
}>;
|
|
18
|
+
/** The entry-point fault envelope: verify and sign are total, and an
|
|
19
|
+
* unexpected fault is a bounded internal error — never an escaping throw —
|
|
20
|
+
* with the exception class observable in detail. */
|
|
21
|
+
export declare function envelope(e: unknown): import("./errors/types.ts").RequestSealError;
|
|
18
22
|
export declare function label(value: unknown): value is string;
|
|
19
23
|
export declare const signatureSchema: Schema;
|
|
20
24
|
export declare function identities(value: InnerList): string[];
|
package/dist/signature-fields.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parse, parseField, serialize, } from "./structured-fields/index.js";
|
|
2
2
|
import { byteString } from "./bytes.js";
|
|
3
3
|
import { token } from "./message/index.js";
|
|
4
|
-
import { coreError, isRequestSealError, } from "./errors/types.js";
|
|
4
|
+
import { coreError, isRequestSealError, faultClass, } from "./errors/types.js";
|
|
5
5
|
import { ok, err } from "./result.js";
|
|
6
6
|
import { SIGNATURE_MAX_BYTES, SIGNATURE_BASE_MAX_COMPONENTS, MESSAGE_MAX_TOKEN_BYTES, STRUCTURED_FIELDS_LIMITS, } from "./constants.js";
|
|
7
7
|
export function fail(reason, layer, detail = null) {
|
|
@@ -16,6 +16,14 @@ export function bounded(e, reason, layer) {
|
|
|
16
16
|
? e
|
|
17
17
|
: coreError(reason, layer);
|
|
18
18
|
}
|
|
19
|
+
/** The entry-point fault envelope: verify and sign are total, and an
|
|
20
|
+
* unexpected fault is a bounded internal error — never an escaping throw —
|
|
21
|
+
* with the exception class observable in detail. */
|
|
22
|
+
export function envelope(e) {
|
|
23
|
+
return isRequestSealError(e)
|
|
24
|
+
? e
|
|
25
|
+
: coreError("internal", "internal", faultClass(e));
|
|
26
|
+
}
|
|
19
27
|
export function label(value) {
|
|
20
28
|
return (typeof value === "string" &&
|
|
21
29
|
value.length > 0 &&
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { STRUCTURED_FIELDS_LIMITS as CEILINGS } from '../constants.ts';
|
|
2
2
|
import { type Result } from '../result.ts';
|
|
3
|
+
/** A typed Structured Fields bare value. */
|
|
3
4
|
export type Bare = Readonly<{
|
|
4
5
|
kind: 'integer' | 'date';
|
|
5
6
|
value: number;
|
|
@@ -21,29 +22,39 @@ export type Bare = Readonly<{
|
|
|
21
22
|
}>;
|
|
22
23
|
export declare function decimalSource(value: Bare): string | undefined;
|
|
23
24
|
export declare function copyDecimalSource(from: Bare, to: Bare): void;
|
|
25
|
+
/** Ordered Structured Fields parameter names and bare values. */
|
|
24
26
|
export type Parameters = readonly (readonly [string, Bare])[];
|
|
27
|
+
/** A Structured Fields item and its ordered parameters. */
|
|
25
28
|
export type Item = Readonly<{
|
|
26
29
|
kind: 'item';
|
|
27
30
|
value: Bare;
|
|
28
31
|
parameters: Parameters;
|
|
29
32
|
}>;
|
|
33
|
+
/** A Structured Fields inner list and its ordered parameters. */
|
|
30
34
|
export type InnerList = Readonly<{
|
|
31
35
|
kind: 'inner_list';
|
|
32
36
|
value: readonly Item[];
|
|
33
37
|
parameters: Parameters;
|
|
34
38
|
}>;
|
|
39
|
+
/** A Structured Fields item or inner list. */
|
|
35
40
|
export type Member = Item | InnerList;
|
|
41
|
+
/** An ordered Structured Fields list. */
|
|
36
42
|
export type List = Readonly<{
|
|
37
43
|
kind: 'list';
|
|
38
44
|
value: readonly Member[];
|
|
39
45
|
}>;
|
|
46
|
+
/** An ordered Structured Fields dictionary. */
|
|
40
47
|
export type Dictionary = Readonly<{
|
|
41
48
|
kind: 'dictionary';
|
|
42
49
|
value: readonly (readonly [string, Member])[];
|
|
43
50
|
}>;
|
|
51
|
+
/** A branded Structured Fields item, inner list, list, or dictionary. */
|
|
44
52
|
export type Value = Item | InnerList | List | Dictionary;
|
|
53
|
+
/** The supported Structured Fields root kinds. */
|
|
45
54
|
export type FieldType = 'item' | 'list' | 'dictionary';
|
|
55
|
+
/** The supported Structured Fields specification revisions. */
|
|
46
56
|
export type Revision = 'rfc8941' | 'rfc9651';
|
|
57
|
+
/** A branded field schema with revision, combination, and byte policies. */
|
|
47
58
|
export type Schema = Readonly<{
|
|
48
59
|
revision: Revision;
|
|
49
60
|
type: FieldType;
|
|
@@ -51,7 +62,9 @@ export type Schema = Readonly<{
|
|
|
51
62
|
parameterTypes: readonly Bare['kind'][];
|
|
52
63
|
innerLists: boolean;
|
|
53
64
|
}>;
|
|
65
|
+
/** Structured Fields parser ceilings. @inline */
|
|
54
66
|
export type Limits = typeof CEILINGS;
|
|
67
|
+
/** Parser limits, schema selection, and duplicate policies. */
|
|
55
68
|
export type Options = {
|
|
56
69
|
schema: Revision | Schema;
|
|
57
70
|
uniqueKeys?: 'all' | string[];
|
|
@@ -61,8 +74,11 @@ export type Options = {
|
|
|
61
74
|
-readonly [K in keyof Limits]: number;
|
|
62
75
|
}>;
|
|
63
76
|
};
|
|
77
|
+
/** Check the private brand of a Structured Fields value. */
|
|
64
78
|
export declare function isStructuredFieldsValue(value: unknown): value is Value;
|
|
79
|
+
/** Check the private brand of a Structured Fields schema. */
|
|
65
80
|
export declare function isStructuredFieldsSchema(value: unknown): value is Schema;
|
|
81
|
+
/** Validate and brand a Structured Fields schema. */
|
|
66
82
|
export declare function createSchema(input: unknown): Result<Schema>;
|
|
67
83
|
/** A single cursor, bounded container depth, and encounter counts before deduplication. */
|
|
68
84
|
export declare function parse(fieldType: FieldType | unknown, raw: unknown, options?: unknown): Result<Value>;
|
|
@@ -70,9 +86,14 @@ export declare function parse(fieldType: FieldType | unknown, raw: unknown, opti
|
|
|
70
86
|
export declare function serialize(value: unknown, options?: unknown): Result<Uint8Array>;
|
|
71
87
|
/** Select ordered occurrences, combine with comma-space, and parse under explicit policy. */
|
|
72
88
|
export declare function parseField(message: unknown, name: unknown, schema: Schema, section?: 'headers' | 'trailers', options?: unknown): Result<Value>;
|
|
89
|
+
/** Bounded parsing, serialization, and field-schema construction. */
|
|
73
90
|
export declare const StructuredFields: Readonly<{
|
|
91
|
+
/** Parse and validate the supported wire representation. */
|
|
74
92
|
parse: typeof parse;
|
|
93
|
+
/** Combine field occurrences according to a branded schema and parse them. */
|
|
75
94
|
parseField: typeof parseField;
|
|
95
|
+
/** Serialize validated values to their canonical wire representation. */
|
|
76
96
|
serialize: typeof serialize;
|
|
97
|
+
/** Validate and brand a Structured Fields schema. */
|
|
77
98
|
createSchema: typeof createSchema;
|
|
78
99
|
}>;
|
|
@@ -26,9 +26,11 @@ const allTypes = [...baseTypes, 'date', 'display_string'];
|
|
|
26
26
|
const schemas = new WeakMap();
|
|
27
27
|
const values = new WeakSet();
|
|
28
28
|
const schemaValues = new WeakSet();
|
|
29
|
+
/** Check the private brand of a Structured Fields value. */
|
|
29
30
|
export function isStructuredFieldsValue(value) {
|
|
30
31
|
return typeof value === 'object' && value !== null && values.has(value);
|
|
31
32
|
}
|
|
33
|
+
/** Check the private brand of a Structured Fields schema. */
|
|
32
34
|
export function isStructuredFieldsSchema(value) {
|
|
33
35
|
return typeof value === 'object' && value !== null && schemaValues.has(value);
|
|
34
36
|
}
|
|
@@ -39,6 +41,7 @@ function ensure(value, reason) {
|
|
|
39
41
|
if (!value)
|
|
40
42
|
fail(reason);
|
|
41
43
|
}
|
|
44
|
+
/** Validate and brand a Structured Fields schema. */
|
|
42
45
|
export function createSchema(input) {
|
|
43
46
|
try {
|
|
44
47
|
const s = snapshotOptions(input, [
|
|
@@ -655,4 +658,14 @@ export function parseField(message, name, schema, section = 'headers', options =
|
|
|
655
658
|
return err(error(e, 'invalid_message'));
|
|
656
659
|
}
|
|
657
660
|
}
|
|
658
|
-
|
|
661
|
+
/** Bounded parsing, serialization, and field-schema construction. */
|
|
662
|
+
export const StructuredFields = Object.freeze({
|
|
663
|
+
/** Parse and validate the supported wire representation. */
|
|
664
|
+
parse,
|
|
665
|
+
/** Combine field occurrences according to a branded schema and parse them. */
|
|
666
|
+
parseField,
|
|
667
|
+
/** Serialize validated values to their canonical wire representation. */
|
|
668
|
+
serialize,
|
|
669
|
+
/** Validate and brand a Structured Fields schema. */
|
|
670
|
+
createSchema
|
|
671
|
+
});
|
package/dist/verification.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type Parameters } from "./structured-fields/index.ts";
|
|
2
2
|
import type { DigestFacts } from "./digest.ts";
|
|
3
3
|
import { type Result } from "./result.ts";
|
|
4
|
+
/** The clock, age, skew, and timestamps used in successful freshness validation. */
|
|
4
5
|
export type FreshnessFacts = Readonly<{
|
|
5
6
|
now: number;
|
|
6
7
|
created: number | null;
|
|
@@ -8,6 +9,7 @@ export type FreshnessFacts = Readonly<{
|
|
|
8
9
|
maxAge: number | null;
|
|
9
10
|
skew: number;
|
|
10
11
|
}>;
|
|
12
|
+
/** Branded signature, content, freshness, replay, and principal verification facts. */
|
|
11
13
|
export type Verification = Readonly<{
|
|
12
14
|
label: string;
|
|
13
15
|
signature: Readonly<{
|
|
@@ -19,15 +21,17 @@ export type Verification = Readonly<{
|
|
|
19
21
|
}>;
|
|
20
22
|
profile: Readonly<{
|
|
21
23
|
name: "rfc9421";
|
|
22
|
-
}> | import("./web-bot-auth/verification.ts").WebBotAuthProfile;
|
|
24
|
+
}> | import("./web-bot-auth/verification.ts").WebBotAuthProfile | import("./profile.ts").ProfileStamp;
|
|
23
25
|
content: "not_required" | DigestFacts;
|
|
24
26
|
freshness: "not_evaluated" | FreshnessFacts;
|
|
25
27
|
replay: "not_required" | import("./replay-port.ts").ReplayReceipt;
|
|
26
28
|
principal: "unattributed" | import("./web-bot-auth/verification.ts").AgentPrincipal | import("./web-bot-auth/verification.ts").HeldPrincipal;
|
|
27
29
|
authorization: "not_evaluated";
|
|
28
30
|
}>;
|
|
31
|
+
/** Check the private brand of a single-signature verification result. */
|
|
29
32
|
export declare function isVerification(value: unknown): value is Verification;
|
|
30
33
|
export declare function completed(value: Verification): Result<Verification>;
|
|
31
34
|
/** Ordered language-neutral facts; cryptographic material is never serialized. */
|
|
32
35
|
export declare function verificationFacts(value: unknown): Result<unknown>;
|
|
36
|
+
/** Encode the canonical public verification facts as JSON bytes. */
|
|
33
37
|
export declare function serializeVerification(value: unknown): Result<Uint8Array>;
|
package/dist/verification.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { isWebBotAuthVerification, webBotAuthFacts } from './web-bot-auth/verification.js';
|
|
2
2
|
import { isQuorumVerification, quorumFacts } from './quorum/verification.js';
|
|
3
|
-
import { deepFreeze } from "./brand.js";
|
|
4
|
-
import {
|
|
3
|
+
import { deepFreeze, inspection } from "./brand.js";
|
|
4
|
+
import { encodeUtf8 } from "./runtime.js";
|
|
5
5
|
import { decimalSource, copyDecimalSource } from "./structured-fields/index.js";
|
|
6
6
|
import { isReplayReceipt } from "./replay/receipt.js";
|
|
7
7
|
import { coreError } from "./errors/types.js";
|
|
8
8
|
import { ok, err } from "./result.js";
|
|
9
9
|
const verifications = new WeakSet();
|
|
10
|
+
/** Check the private brand of a single-signature verification result. */
|
|
10
11
|
export function isVerification(value) {
|
|
11
12
|
return (typeof value === "object" && value !== null && verifications.has(value));
|
|
12
13
|
}
|
|
@@ -60,8 +61,7 @@ export function completed(value) {
|
|
|
60
61
|
});
|
|
61
62
|
Object.defineProperties(result, {
|
|
62
63
|
toJSON: { value: inspect },
|
|
63
|
-
|
|
64
|
-
[Symbol.for("Deno.customInspect")]: { value: inspect },
|
|
64
|
+
...inspection(inspect),
|
|
65
65
|
});
|
|
66
66
|
Object.freeze(result);
|
|
67
67
|
verifications.add(result);
|
|
@@ -124,7 +124,9 @@ export function verificationFacts(value) {
|
|
|
124
124
|
keyid: value.signature.keyid,
|
|
125
125
|
crypto: "valid",
|
|
126
126
|
},
|
|
127
|
-
profile: value.profile
|
|
127
|
+
profile: Array.isArray(value.profile.name)
|
|
128
|
+
? Object.fromEntries(Object.entries(value.profile).map(([k, v]) => [k, typeof v === "boolean" ? String(v) : v === null ? "" : v]))
|
|
129
|
+
: value.profile,
|
|
128
130
|
content: value.content === "not_required"
|
|
129
131
|
? "not_required"
|
|
130
132
|
: { ...value.content, bytes: number(value.content.bytes) },
|
|
@@ -134,6 +136,7 @@ export function verificationFacts(value) {
|
|
|
134
136
|
authorization: value.authorization,
|
|
135
137
|
});
|
|
136
138
|
}
|
|
139
|
+
/** Encode the canonical public verification facts as JSON bytes. */
|
|
137
140
|
export function serializeVerification(value) {
|
|
138
141
|
const r = verificationFacts(value);
|
|
139
142
|
if (!r.ok)
|
|
@@ -147,6 +150,6 @@ export function serializeVerification(value) {
|
|
|
147
150
|
: v && typeof v === "object"
|
|
148
151
|
? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, encode(x)]))
|
|
149
152
|
: v;
|
|
150
|
-
return
|
|
153
|
+
return ok(encodeUtf8(JSON.stringify(encode(r.value))));
|
|
151
154
|
}
|
|
152
155
|
import { encodeBase64Url as base64url } from "./bytes.js";
|
package/dist/verify-quorum.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import { type QuorumVerification } from "./quorum/verification.ts";
|
|
2
2
|
import { type Result } from "./result.ts";
|
|
3
|
+
/** Verify bounded HTTP signatures and evaluate the configured quorum policy. */
|
|
3
4
|
export declare function verifyQuorum(message: unknown, quorum: unknown, options: unknown): Promise<Result<QuorumVerification>>;
|