tunnelfetch 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +617 -0
- package/README.zh-CN.md +470 -0
- package/package.json +74 -0
- package/src/client/cookies.js +429 -0
- package/src/client/decode.js +346 -0
- package/src/client/redirect.js +249 -0
- package/src/client.js +704 -0
- package/src/errors.js +181 -0
- package/src/http1/chunked.js +289 -0
- package/src/http1/index.js +10 -0
- package/src/http1/request.js +143 -0
- package/src/http1/response.js +493 -0
- package/src/http2/connection.js +1170 -0
- package/src/http2/constants.js +129 -0
- package/src/http2/frames.js +291 -0
- package/src/http2/hpack.js +420 -0
- package/src/http2/huffman.js +203 -0
- package/src/http2/index.js +21 -0
- package/src/index.js +46 -0
- package/src/pool.js +256 -0
- package/src/proxy/direct.js +62 -0
- package/src/proxy/http-connect.js +206 -0
- package/src/proxy/index.js +197 -0
- package/src/proxy/socks5.js +344 -0
- package/src/tls/aead.js +263 -0
- package/src/tls/connect.js +407 -0
- package/src/tls/constants.js +334 -0
- package/src/tls/extensions.js +376 -0
- package/src/tls/handshake-messages.js +901 -0
- package/src/tls/handshake.js +568 -0
- package/src/tls/handshake12.js +507 -0
- package/src/tls/index.js +44 -0
- package/src/tls/keyschedule.js +473 -0
- package/src/tls/record.js +872 -0
- package/src/tls/tickets.js +145 -0
- package/src/tls/transcript.js +101 -0
- package/src/tls/wire.js +224 -0
- package/src/transport.js +296 -0
- package/src/trust/der.js +551 -0
- package/src/trust/index.js +375 -0
- package/src/trust/name.js +235 -0
- package/src/trust/ocsp.js +759 -0
- package/src/trust/path.js +595 -0
- package/src/trust/roots.js +454 -0
- package/src/trust/x509.js +902 -0
- package/src/util/bytes.js +470 -0
- package/src/util/deadline.js +266 -0
- package/src/warmup-fixture.js +85 -0
- package/src/warmup.js +243 -0
- package/types/client/cookies.d.ts +159 -0
- package/types/client/decode.d.ts +54 -0
- package/types/client/redirect.d.ts +96 -0
- package/types/client.d.ts +323 -0
- package/types/errors.d.ts +141 -0
- package/types/http1/chunked.d.ts +48 -0
- package/types/http1/index.d.ts +3 -0
- package/types/http1/request.d.ts +44 -0
- package/types/http1/response.d.ts +183 -0
- package/types/http2/connection.d.ts +282 -0
- package/types/http2/constants.d.ts +95 -0
- package/types/http2/frames.d.ts +116 -0
- package/types/http2/hpack.d.ts +99 -0
- package/types/http2/huffman.d.ts +21 -0
- package/types/http2/index.d.ts +5 -0
- package/types/index.d.ts +17 -0
- package/types/pool.d.ts +135 -0
- package/types/proxy/direct.d.ts +26 -0
- package/types/proxy/http-connect.d.ts +37 -0
- package/types/proxy/index.d.ts +62 -0
- package/types/proxy/socks5.d.ts +47 -0
- package/types/tls/aead.d.ts +67 -0
- package/types/tls/connect.d.ts +280 -0
- package/types/tls/constants.d.ts +275 -0
- package/types/tls/extensions.d.ts +195 -0
- package/types/tls/handshake-messages.d.ts +430 -0
- package/types/tls/handshake.d.ts +90 -0
- package/types/tls/handshake12.d.ts +35 -0
- package/types/tls/index.d.ts +9 -0
- package/types/tls/keyschedule.d.ts +272 -0
- package/types/tls/record.d.ts +361 -0
- package/types/tls/tickets.d.ts +66 -0
- package/types/tls/transcript.d.ts +52 -0
- package/types/tls/wire.d.ts +106 -0
- package/types/transport.d.ts +222 -0
- package/types/trust/der.d.ts +239 -0
- package/types/trust/index.d.ts +194 -0
- package/types/trust/name.d.ts +33 -0
- package/types/trust/ocsp.d.ts +138 -0
- package/types/trust/path.d.ts +139 -0
- package/types/trust/roots.d.ts +36 -0
- package/types/trust/x509.d.ts +401 -0
- package/types/util/bytes.d.ts +183 -0
- package/types/util/deadline.d.ts +133 -0
- package/types/warmup-fixture.d.ts +11 -0
- package/types/warmup.d.ts +45 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A trust anchor as supplied by a caller: PEM text or raw DER.
|
|
3
|
+
* @typedef {string | Uint8Array} AnchorInput
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Revocation policy, for the modes that validate chains. Checking is via stapled OCSP only, so
|
|
7
|
+
* the knob decides what a MISSING staple means: `'staple'` (the default) tolerates absence but
|
|
8
|
+
* fully verifies any staple that is present; `'require-staple'` makes absence OCSP_REQUIRED.
|
|
9
|
+
* A verified `revoked` or `unknown` verdict is fatal under both — no value ignores it.
|
|
10
|
+
* @typedef {'staple' | 'require-staple'} RevocationPolicy
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Verify against the bundled CCADB root store. The default.
|
|
14
|
+
* @typedef {{ mode?: 'system', revocation?: RevocationPolicy }} SystemTrust
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Verify against exactly these anchors and nothing else. The bundled store is not consulted.
|
|
18
|
+
* @typedef {{ mode: 'anchors', anchors: AnchorInput[],
|
|
19
|
+
* revocation?: RevocationPolicy }} AnchorsTrust
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Full path validation, plus a requirement that some certificate in the accepted path (or its
|
|
23
|
+
* anchor) match one of `pins`. Pins are `sha256/` followed by the base64 SHA-256 of a
|
|
24
|
+
* SubjectPublicKeyInfo, the same spelling HPKP used.
|
|
25
|
+
* @typedef {{ mode: 'pinned', pins: string[], anchors?: AnchorInput[],
|
|
26
|
+
* revocation?: RevocationPolicy }} PinnedTrust
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* No path validation at all. `insecureAcceptAnyCertificate` is mandatory and must be `true`, so
|
|
30
|
+
* that this mode can never be reached by a typo in `mode`. Supplying `pins` turns it into
|
|
31
|
+
* pin-only trust: no chain is built, but a pin must still match. `revocation` is refused here:
|
|
32
|
+
* without a validated issuer there is no trusted key to verify a staple against, so the check
|
|
33
|
+
* cannot be performed honestly and pretending otherwise would be worse.
|
|
34
|
+
* @typedef {{ mode: 'none', insecureAcceptAnyCertificate: true, pins?: string[] }} NoTrust
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Caller-supplied policy. Returning normally accepts the chain; throwing rejects it. The third
|
|
38
|
+
* argument carries the peer's stapled OCSP response (DER, or null) so a custom policy can judge
|
|
39
|
+
* revocation itself — `verifyOcspStaple` is exported for exactly that.
|
|
40
|
+
* @typedef {{ mode: 'custom',
|
|
41
|
+
* verify: (chain: ParsedCertificate[], hostname: string,
|
|
42
|
+
* details?: { ocspResponse: Uint8Array | null })
|
|
43
|
+
* => void | Promise<void> }} CustomTrust
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* The `verify=` knob, in httpx's spirit. Written as a discriminated union so that a TypeScript
|
|
47
|
+
* caller cannot ask for pinning without pins, or reach `mode: 'none'` without spelling out the
|
|
48
|
+
* flag that says they meant it — both of which are otherwise runtime errors discovered in
|
|
49
|
+
* production rather than compile errors discovered while typing.
|
|
50
|
+
* @typedef {SystemTrust | AnchorsTrust | PinnedTrust | NoTrust | CustomTrust} TrustConfig
|
|
51
|
+
*/
|
|
52
|
+
/**
|
|
53
|
+
* A parsed certificate, as returned by `parseCertificate`. Only the members other layers rely on
|
|
54
|
+
* are named here; the object carries the full parse.
|
|
55
|
+
* @typedef {object} ParsedCertificate
|
|
56
|
+
* @property {Uint8Array} der the original bytes, never re-encoded
|
|
57
|
+
* @property {Uint8Array} tbsBytes exact TBSCertificate slice the signature covers
|
|
58
|
+
* @property {{ spkiDer: Uint8Array, algorithmOid: string, keyBytes: Uint8Array }} spki
|
|
59
|
+
* @property {{ text: string }} subject
|
|
60
|
+
* @property {{ text: string }} issuer
|
|
61
|
+
* @property {number} notBefore epoch ms
|
|
62
|
+
* @property {number} notAfter epoch ms
|
|
63
|
+
* @property {{ dns: string[], ip: Uint8Array[], uri: string[], email: string[] }} subjectAltNames
|
|
64
|
+
*/
|
|
65
|
+
/**
|
|
66
|
+
* Verify a TLS-delivered certificate chain for `hostname`.
|
|
67
|
+
*
|
|
68
|
+
* @param {object} opts
|
|
69
|
+
* @param {Uint8Array[]} opts.chain DER certificates, leaf first, as the peer sent them
|
|
70
|
+
* @param {string} opts.hostname identity from the request URL (DNS name or IP literal)
|
|
71
|
+
* @param {TrustConfig} [opts.trust] the verification policy; defaults to the bundled roots
|
|
72
|
+
* @param {number} [opts.now] epoch ms, for tests and for callers with a better clock
|
|
73
|
+
* @param {Uint8Array | null} [opts.ocspResponse] the peer's stapled DER OCSPResponse, when the
|
|
74
|
+
* handshake carried one; judged under `trust.revocation` (see the policy comment above)
|
|
75
|
+
* @returns {Promise<ParsedCertificate>} the parsed leaf. Every other outcome throws.
|
|
76
|
+
*/
|
|
77
|
+
export function verifyChain({ chain, hostname, trust, now, ocspResponse, }: {
|
|
78
|
+
chain: Uint8Array[];
|
|
79
|
+
hostname: string;
|
|
80
|
+
trust?: TrustConfig | undefined;
|
|
81
|
+
now?: number | undefined;
|
|
82
|
+
ocspResponse?: Uint8Array<ArrayBufferLike> | null | undefined;
|
|
83
|
+
}): Promise<ParsedCertificate>;
|
|
84
|
+
export { matchesIdentity } from "./name.js";
|
|
85
|
+
/**
|
|
86
|
+
* A trust anchor as supplied by a caller: PEM text or raw DER.
|
|
87
|
+
*/
|
|
88
|
+
export type AnchorInput = string | Uint8Array;
|
|
89
|
+
/**
|
|
90
|
+
* Revocation policy, for the modes that validate chains. Checking is via stapled OCSP only, so
|
|
91
|
+
* the knob decides what a MISSING staple means: `'staple'` (the default) tolerates absence but
|
|
92
|
+
* fully verifies any staple that is present; `'require-staple'` makes absence OCSP_REQUIRED.
|
|
93
|
+
* A verified `revoked` or `unknown` verdict is fatal under both — no value ignores it.
|
|
94
|
+
*/
|
|
95
|
+
export type RevocationPolicy = "staple" | "require-staple";
|
|
96
|
+
/**
|
|
97
|
+
* Verify against the bundled CCADB root store. The default.
|
|
98
|
+
*/
|
|
99
|
+
export type SystemTrust = {
|
|
100
|
+
mode?: "system";
|
|
101
|
+
revocation?: RevocationPolicy;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Verify against exactly these anchors and nothing else. The bundled store is not consulted.
|
|
105
|
+
*/
|
|
106
|
+
export type AnchorsTrust = {
|
|
107
|
+
mode: "anchors";
|
|
108
|
+
anchors: AnchorInput[];
|
|
109
|
+
revocation?: RevocationPolicy;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* Full path validation, plus a requirement that some certificate in the accepted path (or its
|
|
113
|
+
* anchor) match one of `pins`. Pins are `sha256/` followed by the base64 SHA-256 of a
|
|
114
|
+
* SubjectPublicKeyInfo, the same spelling HPKP used.
|
|
115
|
+
*/
|
|
116
|
+
export type PinnedTrust = {
|
|
117
|
+
mode: "pinned";
|
|
118
|
+
pins: string[];
|
|
119
|
+
anchors?: AnchorInput[];
|
|
120
|
+
revocation?: RevocationPolicy;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* No path validation at all. `insecureAcceptAnyCertificate` is mandatory and must be `true`, so
|
|
124
|
+
* that this mode can never be reached by a typo in `mode`. Supplying `pins` turns it into
|
|
125
|
+
* pin-only trust: no chain is built, but a pin must still match. `revocation` is refused here:
|
|
126
|
+
* without a validated issuer there is no trusted key to verify a staple against, so the check
|
|
127
|
+
* cannot be performed honestly and pretending otherwise would be worse.
|
|
128
|
+
*/
|
|
129
|
+
export type NoTrust = {
|
|
130
|
+
mode: "none";
|
|
131
|
+
insecureAcceptAnyCertificate: true;
|
|
132
|
+
pins?: string[];
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Caller-supplied policy. Returning normally accepts the chain; throwing rejects it. The third
|
|
136
|
+
* argument carries the peer's stapled OCSP response (DER, or null) so a custom policy can judge
|
|
137
|
+
* revocation itself — `verifyOcspStaple` is exported for exactly that.
|
|
138
|
+
*/
|
|
139
|
+
export type CustomTrust = {
|
|
140
|
+
mode: "custom";
|
|
141
|
+
verify: (chain: ParsedCertificate[], hostname: string, details?: {
|
|
142
|
+
ocspResponse: Uint8Array | null;
|
|
143
|
+
}) => void | Promise<void>;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* The `verify=` knob, in httpx's spirit. Written as a discriminated union so that a TypeScript
|
|
147
|
+
* caller cannot ask for pinning without pins, or reach `mode: 'none'` without spelling out the
|
|
148
|
+
* flag that says they meant it — both of which are otherwise runtime errors discovered in
|
|
149
|
+
* production rather than compile errors discovered while typing.
|
|
150
|
+
*/
|
|
151
|
+
export type TrustConfig = SystemTrust | AnchorsTrust | PinnedTrust | NoTrust | CustomTrust;
|
|
152
|
+
/**
|
|
153
|
+
* A parsed certificate, as returned by `parseCertificate`. Only the members other layers rely on
|
|
154
|
+
* are named here; the object carries the full parse.
|
|
155
|
+
*/
|
|
156
|
+
export type ParsedCertificate = {
|
|
157
|
+
/**
|
|
158
|
+
* the original bytes, never re-encoded
|
|
159
|
+
*/
|
|
160
|
+
der: Uint8Array;
|
|
161
|
+
/**
|
|
162
|
+
* exact TBSCertificate slice the signature covers
|
|
163
|
+
*/
|
|
164
|
+
tbsBytes: Uint8Array;
|
|
165
|
+
spki: {
|
|
166
|
+
spkiDer: Uint8Array;
|
|
167
|
+
algorithmOid: string;
|
|
168
|
+
keyBytes: Uint8Array;
|
|
169
|
+
};
|
|
170
|
+
subject: {
|
|
171
|
+
text: string;
|
|
172
|
+
};
|
|
173
|
+
issuer: {
|
|
174
|
+
text: string;
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* epoch ms
|
|
178
|
+
*/
|
|
179
|
+
notBefore: number;
|
|
180
|
+
/**
|
|
181
|
+
* epoch ms
|
|
182
|
+
*/
|
|
183
|
+
notAfter: number;
|
|
184
|
+
subjectAltNames: {
|
|
185
|
+
dns: string[];
|
|
186
|
+
ip: Uint8Array[];
|
|
187
|
+
uri: string[];
|
|
188
|
+
email: string[];
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
export { parseCertificate, decodePem } from "./x509.js";
|
|
192
|
+
export { validatePath, anchorFromCertificate } from "./path.js";
|
|
193
|
+
export { verifyOcspStaple, parseOcspResponse } from "./ocsp.js";
|
|
194
|
+
export { systemAnchors, provenance as rootStoreProvenance } from "./roots.js";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse an IP literal (v4 dotted-quad or v6, optionally [bracketed]) to raw bytes, else null.
|
|
3
|
+
* @param {string} text
|
|
4
|
+
* @returns {Uint8Array | null}
|
|
5
|
+
*/
|
|
6
|
+
export function parseIp(text: string): Uint8Array | null;
|
|
7
|
+
/**
|
|
8
|
+
* Verify that `cert` is a certificate for `hostname`. Returns nothing; every non-match throws
|
|
9
|
+
* CERT_NAME_MISMATCH with the entries that were considered, so one log line shows exactly how
|
|
10
|
+
* close the certificate was.
|
|
11
|
+
*
|
|
12
|
+
* @param {import('./x509.js').Certificate} cert
|
|
13
|
+
* @param {string} hostname DNS name (A-label form) or IP literal, per the request URL
|
|
14
|
+
* @returns {void}
|
|
15
|
+
*/
|
|
16
|
+
export function matchesIdentity(cert: import("./x509.js").Certificate, hostname: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* RFC 5280 s4.2.1.10 dNSName subtree: a constraint "example.com" covers the host itself and any
|
|
19
|
+
* subdomain, at a label boundary. The seen-in-the-wild ".example.com" form covers subdomains
|
|
20
|
+
* only. An empty constraint covers every DNS name (used by permittedSubtrees to say "any DNS").
|
|
21
|
+
* @param {string} name
|
|
22
|
+
* @param {string} base
|
|
23
|
+
* @returns {boolean}
|
|
24
|
+
*/
|
|
25
|
+
export function dnsWithinSubtree(name: string, base: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* iPAddress subtree: same family, and (address & mask) equal on every byte.
|
|
28
|
+
* @param {Uint8Array} ip
|
|
29
|
+
* @param {Uint8Array} addr
|
|
30
|
+
* @param {Uint8Array} mask
|
|
31
|
+
* @returns {boolean}
|
|
32
|
+
*/
|
|
33
|
+
export function ipWithinSubtree(ip: Uint8Array, addr: Uint8Array, mask: Uint8Array): boolean;
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parse a DER OCSPResponse (RFC 6960 s4.2.1) into a fully-walked structure.
|
|
3
|
+
*
|
|
4
|
+
* A parser, not a judge, exactly like parseCertificate: it throws OCSP_PARSE on malformed or
|
|
5
|
+
* self-contradictory bytes and on constructs this package refuses to interpret (unknown
|
|
6
|
+
* responseType, unrecognized critical extensions); whether the parsed response is TRUE is
|
|
7
|
+
* verifyOcspStaple's problem.
|
|
8
|
+
*
|
|
9
|
+
* @param {Uint8Array} der
|
|
10
|
+
* @returns {OcspResponse}
|
|
11
|
+
*/
|
|
12
|
+
export function parseOcspResponse(der: Uint8Array): OcspResponse;
|
|
13
|
+
/**
|
|
14
|
+
* Verify a stapled OCSP response against the certificate it must vouch for.
|
|
15
|
+
*
|
|
16
|
+
* Every check the module comment promises happens here, in this order: parse, match the CertID
|
|
17
|
+
* to `leaf`/`issuer`, establish the signer (the CA itself or an RFC 6960 s4.2.2.2 delegated
|
|
18
|
+
* responder), verify the signature over the original tbsResponseData bytes, and only then read
|
|
19
|
+
* the verdict and its freshness window. `revoked` and `unknown` always throw; `good` throws
|
|
20
|
+
* unless the window covers `now`.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} args
|
|
23
|
+
* @param {Uint8Array} args.staple DER OCSPResponse, exactly as the peer stapled it
|
|
24
|
+
* @param {import('./x509.js').Certificate} args.leaf the validated leaf the staple must cover
|
|
25
|
+
* @param {OcspIssuer} args.issuer the leaf's issuer, from the validated path
|
|
26
|
+
* @param {number} args.now epoch ms, injected — never a wall clock read here
|
|
27
|
+
* @returns {Promise<OcspVerdict>} every failure throws a typed CertificateError (OCSP_* codes)
|
|
28
|
+
*/
|
|
29
|
+
export function verifyOcspStaple({ staple, leaf, issuer, now }: {
|
|
30
|
+
staple: Uint8Array;
|
|
31
|
+
leaf: import("./x509.js").Certificate;
|
|
32
|
+
issuer: OcspIssuer;
|
|
33
|
+
now: number;
|
|
34
|
+
}): Promise<OcspVerdict>;
|
|
35
|
+
/**
|
|
36
|
+
* CertID (RFC 6960 s4.1.1): which certificate a SingleResponse is talking about.
|
|
37
|
+
*/
|
|
38
|
+
export type OcspCertId = {
|
|
39
|
+
hashOid: string;
|
|
40
|
+
issuerNameHash: Uint8Array;
|
|
41
|
+
issuerKeyHash: Uint8Array;
|
|
42
|
+
/**
|
|
43
|
+
* INTEGER content bytes, minimal DER
|
|
44
|
+
*/
|
|
45
|
+
serialBytes: Uint8Array;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* One SingleResponse, parsed. Times are epoch ms.
|
|
49
|
+
*/
|
|
50
|
+
export type OcspSingleResponse = {
|
|
51
|
+
certId: OcspCertId;
|
|
52
|
+
status: {
|
|
53
|
+
kind: "good";
|
|
54
|
+
} | {
|
|
55
|
+
kind: "unknown";
|
|
56
|
+
} | {
|
|
57
|
+
kind: "revoked";
|
|
58
|
+
revocationTime: number;
|
|
59
|
+
reason: number | null;
|
|
60
|
+
};
|
|
61
|
+
thisUpdate: number;
|
|
62
|
+
nextUpdate: number | null;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* A parsed BasicOCSPResponse. `tbsBytes` is the exact original ResponseData element — the bytes
|
|
66
|
+
* the signature covers, never re-encoded.
|
|
67
|
+
*/
|
|
68
|
+
export type OcspBasicResponse = {
|
|
69
|
+
tbsBytes: Uint8Array;
|
|
70
|
+
signatureAlgorithm: import("./x509.js").AlgorithmId;
|
|
71
|
+
signature: Uint8Array;
|
|
72
|
+
responderId: {
|
|
73
|
+
kind: "name";
|
|
74
|
+
nameBytes: Uint8Array;
|
|
75
|
+
} | {
|
|
76
|
+
kind: "key";
|
|
77
|
+
keyHash: Uint8Array;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* epoch ms
|
|
81
|
+
*/
|
|
82
|
+
producedAt: number;
|
|
83
|
+
singles: OcspSingleResponse[];
|
|
84
|
+
/**
|
|
85
|
+
* attached responder certificates, parsed
|
|
86
|
+
*/
|
|
87
|
+
certs: import("./x509.js").Certificate[];
|
|
88
|
+
};
|
|
89
|
+
export type OcspResponse = {
|
|
90
|
+
responseStatus: number;
|
|
91
|
+
responseStatusName: string;
|
|
92
|
+
/**
|
|
93
|
+
* null exactly when responseStatus != successful
|
|
94
|
+
*/
|
|
95
|
+
basic: OcspBasicResponse | null;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* The issuer of the certificate under check, reduced to what OCSP verification needs. Built by
|
|
99
|
+
* the caller from the VALIDATED path — the certificate that actually signed the leaf, or the
|
|
100
|
+
* trust anchor when the leaf sits directly under one — never from the unverified wire chain.
|
|
101
|
+
*/
|
|
102
|
+
export type OcspIssuer = {
|
|
103
|
+
/**
|
|
104
|
+
* exact subject Name DER
|
|
105
|
+
*/
|
|
106
|
+
subjectBytes: Uint8Array;
|
|
107
|
+
/**
|
|
108
|
+
* SubjectPublicKeyInfo DER
|
|
109
|
+
*/
|
|
110
|
+
spkiDer: Uint8Array;
|
|
111
|
+
/**
|
|
112
|
+
* for error messages
|
|
113
|
+
*/
|
|
114
|
+
subjectText: string;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* What a verified `good` staple reports. Every other outcome throws; there is no boolean.
|
|
118
|
+
*/
|
|
119
|
+
export type OcspVerdict = {
|
|
120
|
+
status: "good";
|
|
121
|
+
/**
|
|
122
|
+
* epoch ms
|
|
123
|
+
*/
|
|
124
|
+
producedAt: number;
|
|
125
|
+
/**
|
|
126
|
+
* epoch ms
|
|
127
|
+
*/
|
|
128
|
+
thisUpdate: number;
|
|
129
|
+
/**
|
|
130
|
+
* epoch ms
|
|
131
|
+
*/
|
|
132
|
+
nextUpdate: number | null;
|
|
133
|
+
/**
|
|
134
|
+
* whether a delegated responder certificate signed, rather than
|
|
135
|
+
* the CA key itself
|
|
136
|
+
*/
|
|
137
|
+
delegated: boolean;
|
|
138
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The RFC 5280 s6.1.1 trust-anchor triple, normalised. Validity bounds are recorded but never
|
|
3
|
+
* enforced — see the module comment for why expiring a root is store policy, not path policy.
|
|
4
|
+
* @typedef {object} TrustAnchor
|
|
5
|
+
* @property {Uint8Array} subjectBytes exact subject DN DER
|
|
6
|
+
* @property {string} subjectText
|
|
7
|
+
* @property {Uint8Array} spkiDer
|
|
8
|
+
* @property {Uint8Array | null} subjectKeyIdentifier
|
|
9
|
+
* @property {import('./x509.js').NameConstraints | null} nameConstraints
|
|
10
|
+
* @property {number | null} notBefore
|
|
11
|
+
* @property {number | null} notAfter
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Anything normalizeAnchor accepts: DER, a parsed certificate, or an anchor-shaped record.
|
|
15
|
+
* The record form also admits `nameConstraintsBytes` (raw extnValue), which is how the bundled
|
|
16
|
+
* store defers parsing to the one anchor a handshake actually lands on.
|
|
17
|
+
* @typedef {object} AnchorRecord
|
|
18
|
+
* @property {Uint8Array} subjectBytes
|
|
19
|
+
* @property {Uint8Array} spkiDer
|
|
20
|
+
* @property {string} [subjectText]
|
|
21
|
+
* @property {Uint8Array | null} [subjectKeyIdentifier]
|
|
22
|
+
* @property {import('./x509.js').NameConstraints | null} [nameConstraints]
|
|
23
|
+
* @property {Uint8Array | null} [nameConstraintsBytes]
|
|
24
|
+
* @property {number | null} [notBefore]
|
|
25
|
+
* @property {number | null} [notAfter]
|
|
26
|
+
*/
|
|
27
|
+
/** @typedef {Uint8Array | import('./x509.js').Certificate | AnchorRecord} AnchorLike */
|
|
28
|
+
/**
|
|
29
|
+
* An indexed anchor lookup: everything path building needs from a root store. The bundled
|
|
30
|
+
* store implements it with a subject-hash index so a handshake touches one anchor, not all.
|
|
31
|
+
* @typedef {{ forIssuer: (subjectDn: Uint8Array) => AnchorLike[] | Promise<AnchorLike[]> }}
|
|
32
|
+
* AnchorSource
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* Strip a parsed certificate down to the RFC 5280 s6.1.1 trust-anchor triple. Used for the
|
|
36
|
+
* `mode:'anchors'` knob and by the root-store generator, so both feed path validation through
|
|
37
|
+
* the identical shape.
|
|
38
|
+
* @param {import('./x509.js').Certificate} cert
|
|
39
|
+
* @returns {TrustAnchor}
|
|
40
|
+
*/
|
|
41
|
+
export function anchorFromCertificate(cert: import("./x509.js").Certificate): TrustAnchor;
|
|
42
|
+
/**
|
|
43
|
+
* Verify `cert`'s signature over its original to-be-signed bytes with the signer's public key.
|
|
44
|
+
*
|
|
45
|
+
* The scheme comes from resolveSignatureScheme (which is where MD5/SHA-1 die, before any
|
|
46
|
+
* cryptography runs). For ECDSA the curve belongs to the issuer's key, not the OID, so the
|
|
47
|
+
* WebCrypto import parameters are chosen from the issuer's SPKI and only the hash from the OID —
|
|
48
|
+
* both looked up in SIG_SCHEME_PARAMS rather than re-declared here.
|
|
49
|
+
*
|
|
50
|
+
* Exported (as verifySignedObject) for the OCSP checker: a BasicOCSPResponse is signed exactly
|
|
51
|
+
* like a certificate — an AlgorithmIdentifier, a BIT STRING over the original DER of a TBS
|
|
52
|
+
* element — and two implementations of "check an X.509-style signature" is two chances for one
|
|
53
|
+
* of them to be subtly the weaker. `cert` is therefore the structural subset both callers can
|
|
54
|
+
* supply: `{ tbsBytes, signature, signatureAlgorithm, subject: { text } }`, which a parsed
|
|
55
|
+
* Certificate satisfies as-is and the OCSP checker fakes up from response fields.
|
|
56
|
+
*
|
|
57
|
+
* @param {{ tbsBytes: Uint8Array, signature: Uint8Array,
|
|
58
|
+
* signatureAlgorithm: import('./x509.js').AlgorithmId,
|
|
59
|
+
* subject: { text: string } }} cert what was signed, certificate-shaped
|
|
60
|
+
* @param {Uint8Array} issuerSpkiDer the signer's SubjectPublicKeyInfo, DER
|
|
61
|
+
* @param {string} issuerText the signer's name, for error messages
|
|
62
|
+
* @returns {Promise<void>} every failure throws a typed CertificateError
|
|
63
|
+
*/
|
|
64
|
+
export function verifySignedObject(cert: {
|
|
65
|
+
tbsBytes: Uint8Array;
|
|
66
|
+
signature: Uint8Array;
|
|
67
|
+
signatureAlgorithm: import("./x509.js").AlgorithmId;
|
|
68
|
+
subject: {
|
|
69
|
+
text: string;
|
|
70
|
+
};
|
|
71
|
+
}, issuerSpkiDer: Uint8Array, issuerText: string): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Build and validate a certification path. Every failure throws a typed CertificateError;
|
|
74
|
+
* there is no boolean to forget to check.
|
|
75
|
+
*
|
|
76
|
+
* @param {object} opts
|
|
77
|
+
* @param {Array<Uint8Array | import('./x509.js').Certificate>} opts.chain DER (or
|
|
78
|
+
* already-parsed) certificates, leaf first, in whatever order and with whatever extras the
|
|
79
|
+
* server chose to send
|
|
80
|
+
* @param {AnchorLike[] | AnchorSource} opts.anchors trust anchors, or an indexed anchor source
|
|
81
|
+
* @param {string | null} [opts.hostname] identity to require of the leaf; omit to skip
|
|
82
|
+
* (index.js never omits)
|
|
83
|
+
* @param {number} [opts.now] epoch ms
|
|
84
|
+
* @param {number} [opts.maxPathLength] hard cap on path certificates — this runs on a metered
|
|
85
|
+
* runtime and a pathological chain must cost O(cap), not O(chain²)
|
|
86
|
+
* @returns {Promise<{ leaf: import('./x509.js').Certificate,
|
|
87
|
+
* path: import('./x509.js').Certificate[], anchor: TrustAnchor }>} parsed leaf, the
|
|
88
|
+
* validated path (leaf first), and the anchor that terminated it
|
|
89
|
+
*/
|
|
90
|
+
export function validatePath({ chain, anchors, hostname, now, maxPathLength }: {
|
|
91
|
+
chain: Array<Uint8Array | import("./x509.js").Certificate>;
|
|
92
|
+
anchors: AnchorLike[] | AnchorSource;
|
|
93
|
+
hostname?: string | null | undefined;
|
|
94
|
+
now?: number | undefined;
|
|
95
|
+
maxPathLength?: number | undefined;
|
|
96
|
+
}): Promise<{
|
|
97
|
+
leaf: import("./x509.js").Certificate;
|
|
98
|
+
path: import("./x509.js").Certificate[];
|
|
99
|
+
anchor: TrustAnchor;
|
|
100
|
+
}>;
|
|
101
|
+
/**
|
|
102
|
+
* The RFC 5280 s6.1.1 trust-anchor triple, normalised. Validity bounds are recorded but never
|
|
103
|
+
* enforced — see the module comment for why expiring a root is store policy, not path policy.
|
|
104
|
+
*/
|
|
105
|
+
export type TrustAnchor = {
|
|
106
|
+
/**
|
|
107
|
+
* exact subject DN DER
|
|
108
|
+
*/
|
|
109
|
+
subjectBytes: Uint8Array;
|
|
110
|
+
subjectText: string;
|
|
111
|
+
spkiDer: Uint8Array;
|
|
112
|
+
subjectKeyIdentifier: Uint8Array | null;
|
|
113
|
+
nameConstraints: import("./x509.js").NameConstraints | null;
|
|
114
|
+
notBefore: number | null;
|
|
115
|
+
notAfter: number | null;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Anything normalizeAnchor accepts: DER, a parsed certificate, or an anchor-shaped record.
|
|
119
|
+
* The record form also admits `nameConstraintsBytes` (raw extnValue), which is how the bundled
|
|
120
|
+
* store defers parsing to the one anchor a handshake actually lands on.
|
|
121
|
+
*/
|
|
122
|
+
export type AnchorRecord = {
|
|
123
|
+
subjectBytes: Uint8Array;
|
|
124
|
+
spkiDer: Uint8Array;
|
|
125
|
+
subjectText?: string | undefined;
|
|
126
|
+
subjectKeyIdentifier?: Uint8Array<ArrayBufferLike> | null | undefined;
|
|
127
|
+
nameConstraints?: import("./x509.js").NameConstraints | null | undefined;
|
|
128
|
+
nameConstraintsBytes?: Uint8Array<ArrayBufferLike> | null | undefined;
|
|
129
|
+
notBefore?: number | null | undefined;
|
|
130
|
+
notAfter?: number | null | undefined;
|
|
131
|
+
};
|
|
132
|
+
export type AnchorLike = Uint8Array | import("./x509.js").Certificate | AnchorRecord;
|
|
133
|
+
/**
|
|
134
|
+
* An indexed anchor lookup: everything path building needs from a root store. The bundled
|
|
135
|
+
* store implements it with a subject-hash index so a handshake touches one anchor, not all.
|
|
136
|
+
*/
|
|
137
|
+
export type AnchorSource = {
|
|
138
|
+
forIssuer: (subjectDn: Uint8Array) => AnchorLike[] | Promise<AnchorLike[]>;
|
|
139
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Where these anchors came from. */
|
|
2
|
+
export const provenance: Readonly<{
|
|
3
|
+
source: "ccadb:IncludedRootsPEMTxt?TrustBitsInclude=Websites";
|
|
4
|
+
retrievedAt: "2026-07-30";
|
|
5
|
+
upstreamSha256: "1813222850e0d3efb875b2978e61acbcb5fcdce93d5c4358443050e9659394bd";
|
|
6
|
+
anchorCount: 121;
|
|
7
|
+
}>;
|
|
8
|
+
export namespace systemAnchors {
|
|
9
|
+
/**
|
|
10
|
+
* @param {Uint8Array} dnBytes exact subject DN DER of the issuer being resolved
|
|
11
|
+
* @returns {Promise<StoredAnchor[]>}
|
|
12
|
+
*/
|
|
13
|
+
function forIssuer(dnBytes: Uint8Array): Promise<StoredAnchor[]>;
|
|
14
|
+
}
|
|
15
|
+
export type PackedAnchor = {
|
|
16
|
+
name: string;
|
|
17
|
+
s: string;
|
|
18
|
+
spki: string;
|
|
19
|
+
ski: string | null;
|
|
20
|
+
nc: string | null;
|
|
21
|
+
nb: number;
|
|
22
|
+
na: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* An unpacked anchor record, shaped for path validation's normalizeAnchor: name constraints
|
|
26
|
+
* stay raw (`nameConstraintsBytes`) so only the anchor a handshake lands on pays for parsing.
|
|
27
|
+
*/
|
|
28
|
+
export type StoredAnchor = {
|
|
29
|
+
subjectText: string;
|
|
30
|
+
subjectBytes: Uint8Array;
|
|
31
|
+
spkiDer: Uint8Array;
|
|
32
|
+
subjectKeyIdentifier: Uint8Array | null;
|
|
33
|
+
nameConstraintsBytes: Uint8Array | null;
|
|
34
|
+
notBefore: number;
|
|
35
|
+
notAfter: number;
|
|
36
|
+
};
|