@blamejs/pki 0.5.7 → 0.5.8
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 +405 -386
- package/MIGRATING.md +43 -0
- package/README.md +12 -12
- package/lib/acme.js +31 -31
- package/lib/asn1-der.js +10 -10
- package/lib/attrcert-sign.js +19 -19
- package/lib/byte-reader.js +6 -6
- package/lib/byte-writer.js +5 -5
- package/lib/cbor-det.js +27 -24
- package/lib/cmc-build.js +97 -92
- package/lib/cmc-verify.js +106 -30
- package/lib/cmp-build.js +25 -25
- package/lib/cmp-session.js +70 -70
- package/lib/cmp-verify.js +71 -57
- package/lib/cms-compress.js +7 -7
- package/lib/cms-decrypt.js +90 -72
- package/lib/cms-encrypt.js +32 -32
- package/lib/cms-sign.js +74 -55
- package/lib/cms-verify.js +97 -75
- package/lib/composite-sig.js +13 -13
- package/lib/constants.js +4 -4
- package/lib/crl-sign.js +22 -22
- package/lib/crl-verify.js +7 -6
- package/lib/crmf-sign.js +14 -14
- package/lib/csr-sign.js +8 -8
- package/lib/ct.js +37 -37
- package/lib/edwards-point.js +7 -7
- package/lib/est.js +98 -55
- package/lib/framework-error.js +5 -5
- package/lib/guard-all.js +3 -3
- package/lib/guard-async.js +4 -4
- package/lib/guard-bytes.js +79 -79
- package/lib/guard-compress.js +17 -17
- package/lib/guard-crypto.js +1 -1
- package/lib/guard-encoding.js +15 -15
- package/lib/guard-header.js +3 -3
- package/lib/guard-identifier.js +16 -16
- package/lib/guard-json.js +15 -15
- package/lib/guard-limits.js +7 -7
- package/lib/guard-name.js +81 -16
- package/lib/guard-parsed.js +80 -80
- package/lib/guard-range.js +19 -19
- package/lib/guard-secret.js +11 -10
- package/lib/guard-text.js +6 -6
- package/lib/guard-time.js +10 -10
- package/lib/hpke.js +18 -17
- package/lib/http-digest.js +35 -35
- package/lib/http-retry-after.js +13 -13
- package/lib/http-transport.js +20 -19
- package/lib/inspect.js +53 -53
- package/lib/ip-utils.js +2 -2
- package/lib/jose.js +13 -13
- package/lib/key.js +16 -16
- package/lib/lint.js +51 -51
- package/lib/merkle.js +51 -36
- package/lib/mime.js +18 -18
- package/lib/ocsp-verify.js +10 -10
- package/lib/ocsp.js +13 -13
- package/lib/oid.js +29 -29
- package/lib/path-validate.js +114 -113
- package/lib/pbes2.js +16 -16
- package/lib/pkcs12-build.js +53 -53
- package/lib/pki-build.js +21 -19
- package/lib/rc2.js +1 -1
- package/lib/rfc3339.js +5 -5
- package/lib/schema-all.js +31 -31
- package/lib/schema-attrcert.js +12 -12
- package/lib/schema-c509.js +144 -142
- package/lib/schema-cmc.js +58 -58
- package/lib/schema-cmp.js +43 -43
- package/lib/schema-cms.js +45 -45
- package/lib/schema-crl.js +7 -7
- package/lib/schema-crmf.js +28 -28
- package/lib/schema-csr.js +12 -12
- package/lib/schema-csrattrs.js +16 -16
- package/lib/schema-engine.js +18 -18
- package/lib/schema-ocsp.js +15 -15
- package/lib/schema-pkcs12.js +20 -20
- package/lib/schema-pkcs8.js +2 -2
- package/lib/schema-pkix.js +131 -126
- package/lib/schema-smime.js +19 -19
- package/lib/schema-tsp.js +12 -12
- package/lib/schema-x509.js +3 -3
- package/lib/shbs.js +18 -18
- package/lib/sign-scheme.js +13 -13
- package/lib/sigstore.js +10 -11
- package/lib/sleep.js +1 -1
- package/lib/smime.js +308 -96
- package/lib/tls-cert-compress.js +18 -18
- package/lib/trust.js +27 -27
- package/lib/tsp-sign.js +17 -17
- package/lib/validator-all.js +1 -1
- package/lib/validator-attcert.js +1 -1
- package/lib/validator-cose.js +43 -44
- package/lib/validator-keydesc.js +3 -3
- package/lib/validator-sig.js +13 -13
- package/lib/validator-tls.js +11 -11
- package/lib/validator-tpm.js +20 -19
- package/lib/webauthn-mds.js +66 -66
- package/lib/webauthn.js +33 -33
- package/lib/webcrypto.js +15 -15
- package/lib/x509-sign.js +13 -13
- package/package.json +3 -2
- package/sbom.cdx.json +6 -6
package/lib/validator-sig.js
CHANGED
|
@@ -7,21 +7,21 @@
|
|
|
7
7
|
//
|
|
8
8
|
// validator-sig -- the SINGLE home for "is this a conformant DER ECDSA-Sig-Value", and
|
|
9
9
|
// its conversion to the raw r||s (IEEE P1363) form a WebCrypto verify expects. Sibling to
|
|
10
|
-
// the guard family: a validator owns a decoded
|
|
11
|
-
// so a format module composes it
|
|
10
|
+
// the guard family: a validator owns a decoded type's complete conformance rule set once,
|
|
11
|
+
// so a format module composes it instead of hand-decoding a two-INTEGER SEQUENCE into r/s
|
|
12
12
|
// and forgetting a strict-DER check (the drift a validator exists to prevent).
|
|
13
13
|
//
|
|
14
14
|
// Interface mirrors the guard family: (subject, ..., E, code) where E is the caller's
|
|
15
15
|
// typed error CONSTRUCTOR and code its domain code.
|
|
16
16
|
//
|
|
17
17
|
// Rule set (gap-checked verbatim against RFC 3279 sec. 2.2.3 + X.690 sec. 8.3 + SEC1):
|
|
18
|
-
// - ECDSA-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER }
|
|
19
|
-
//
|
|
20
|
-
// - r and s are each a
|
|
21
|
-
// leading 0x00/0xFF),
|
|
22
|
-
// enforces the primitive
|
|
18
|
+
// - ECDSA-Sig-Value ::= SEQUENCE { r INTEGER, s INTEGER }, a universal SEQUENCE with
|
|
19
|
+
// exactly two children.
|
|
20
|
+
// - r and s are each a primitive, minimally-encoded (X.690 sec. 8.3.2, no redundant
|
|
21
|
+
// leading 0x00/0xFF), positive integer. Reading through the strict DER integer reader
|
|
22
|
+
// enforces the primitive and minimal-encoding rules; the value is then range-checked
|
|
23
23
|
// positive (r,s >= 1) and bounded by the curve field size. A non-minimal, zero,
|
|
24
|
-
// negative, or over-size coordinate fails closed
|
|
24
|
+
// negative, or over-size coordinate fails closed, never normalized-and-accepted.
|
|
25
25
|
|
|
26
26
|
var asn1 = require("./asn1-der");
|
|
27
27
|
|
|
@@ -52,8 +52,8 @@ var CURVE_ORDER = {
|
|
|
52
52
|
|
|
53
53
|
// ecdsaDerToP1363(der, curve, E, code) -> the DER ECDSA-Sig-Value converted to raw r||s (P1363),
|
|
54
54
|
// each coordinate left-padded to the curve field width, rejecting r or s outside [1, n-1] against
|
|
55
|
-
// the curve
|
|
56
|
-
// bound). `curve` is a WebCrypto namedCurve (P-256/384/521). This is the
|
|
55
|
+
// the curve order (CVE-2022-21449 "Psychic Signatures": the r/s = 0 case and the >= n upper
|
|
56
|
+
// bound). `curve` is a WebCrypto namedCurve (P-256/384/521). This is the single ECDSA-Sig-Value
|
|
57
57
|
// conformance gate every curve-aware verifier routes through: it enforces the complete strict-DER
|
|
58
58
|
// rule set (2-INTEGER SEQUENCE, minimal encoding) AND the order bound, so a verifier never
|
|
59
59
|
// hand-decodes the SEQUENCE (skipping minimality) nor bounds r/s only by the field size (missing
|
|
@@ -72,9 +72,9 @@ function ecdsaDerToP1363(der, curve, E, code) {
|
|
|
72
72
|
if (n.tagClass !== "universal" || n.tagNumber !== asn1.TAGS.SEQUENCE || !n.children || n.children.length !== 2) {
|
|
73
73
|
throw new E(code, "ECDSA signature must be a SEQUENCE of exactly two INTEGERs");
|
|
74
74
|
}
|
|
75
|
-
// Wrap the strict DER integer reads (they enforce
|
|
76
|
-
// constructed
|
|
77
|
-
// conformance rule set, gated together with the order bound below.
|
|
75
|
+
// Wrap the strict DER integer reads (they enforce primitive and minimal encoding) so a non-minimal,
|
|
76
|
+
// constructed or empty INTEGER surfaces the caller's typed code and not a raw asn1/*. That is the
|
|
77
|
+
// complete DER conformance rule set, gated together with the order bound below.
|
|
78
78
|
var r, s;
|
|
79
79
|
try { r = asn1.read.integer(n.children[0]); } catch (e) { throw new E(code, "ECDSA signature r is not a minimally-encoded DER INTEGER", e); }
|
|
80
80
|
try { s = asn1.read.integer(n.children[1]); } catch (e) { throw new E(code, "ECDSA signature s is not a minimally-encoded DER INTEGER", e); }
|
package/lib/validator-tls.js
CHANGED
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
// Every field is unsigned big-endian, packed with no padding, in the TLS presentation
|
|
15
15
|
// language: `uintN` is a fixed-width integer and `opaque<min..max>` is a length prefix wide
|
|
16
16
|
// enough for `max` followed by exactly that many bytes. The caller supplies its typed error
|
|
17
|
-
//
|
|
17
|
+
// constructor E (a validator takes a class, `new E(code, msg)`, where the guard family
|
|
18
18
|
// takes a factory) plus the `codes` its domain uses for each fault.
|
|
19
19
|
//
|
|
20
20
|
// Rule set (verbatim against RFC 8879 sec. 4 and RFC 8446 sec. 4.4.2):
|
|
21
21
|
// - compressedCertificate: uint16 algorithm, uint24 uncompressed_length, then
|
|
22
|
-
// opaque compressed_certificate_message<1..2^24-1
|
|
22
|
+
// opaque compressed_certificate_message<1..2^24-1>, where the minimum of 1 makes an empty
|
|
23
23
|
// compressed body a framing violation, which matters because the three decompressors do
|
|
24
24
|
// not agree on what empty input means. Bytes after the vector are rejected: one chain
|
|
25
25
|
// must have exactly one encoding.
|
|
@@ -34,19 +34,19 @@
|
|
|
34
34
|
// codec's policy and stay with the codec.
|
|
35
35
|
//
|
|
36
36
|
// The RFC 8446 sec. 4.4.2 / sec. 4.2 rules that bind a Certificate-message DECODER, and where
|
|
37
|
-
// each one lands
|
|
38
|
-
//
|
|
37
|
+
// each one lands, enumerated so the ones deliberately left unenforced are a recorded decision
|
|
38
|
+
// and not an oversight:
|
|
39
39
|
// ENFORCED here:
|
|
40
40
|
// - RawPublicKey carries at most one CertificateEntry (sec. 4.4.2).
|
|
41
41
|
// - No extension type appears twice in one extension block (sec. 4.2).
|
|
42
42
|
// - Every vector's framing, its declared minimum, and no trailing bytes at either level.
|
|
43
|
-
//
|
|
43
|
+
// Not enforced, deliberately:
|
|
44
44
|
// - "The sender's certificate MUST come in the first CertificateEntry" is about which
|
|
45
45
|
// certificate a consumer treats as the leaf, not about framing. Entries are surfaced in
|
|
46
|
-
// wire order and never reordered, so the caller sees exactly what arrived
|
|
46
|
+
// wire order and never reordered, so the caller sees exactly what arrived, and the same
|
|
47
47
|
// section tells implementations to tolerate extraneous certificates and arbitrary
|
|
48
48
|
// orderings beyond the first, so refusing any order here would be wrong.
|
|
49
|
-
// - "The server's certificate_list MUST always be non-empty" is conditioned on the
|
|
49
|
+
// - "The server's certificate_list MUST always be non-empty" is conditioned on the sender's
|
|
50
50
|
// role: a client legitimately sends an empty list when it has no certificate to offer.
|
|
51
51
|
// This decoder has no role, so an empty list decodes to zero entries and the caller, which
|
|
52
52
|
// does know the role, decides.
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
// extension's own contents (a status_request body being a CertificateStatus) need the
|
|
55
55
|
// negotiation state and the extension's semantics. Neither exists here: extension values
|
|
56
56
|
// are surfaced raw.
|
|
57
|
-
// - The OpenPGP certificate type is unreachable
|
|
57
|
+
// - The OpenPGP certificate type is unreachable, not checked: the only accepted
|
|
58
58
|
// types are X509 and RawPublicKey, and anything else is refused at the entry point.
|
|
59
59
|
|
|
60
60
|
var C = require("./constants");
|
|
@@ -78,8 +78,8 @@ function compressedCertificate(view, E, codes) {
|
|
|
78
78
|
var r = new ByteReader(view, 0, view.length, E, codes.truncated);
|
|
79
79
|
var algorithm = r.u16(codes.truncated);
|
|
80
80
|
var uncompressedLength = r.u24(codes.truncated);
|
|
81
|
-
// opaque compressed_certificate_message<1..2^24-1
|
|
82
|
-
// framing violation
|
|
81
|
+
// opaque compressed_certificate_message<1..2^24-1>, minimum 1, so an empty body is a
|
|
82
|
+
// framing violation and not something a decompressor gets to interpret.
|
|
83
83
|
var body = r.vector(3, 1, MAX_VECTOR_24, codes.framing);
|
|
84
84
|
if (!r.atEnd()) {
|
|
85
85
|
throw new E(codes.trailing, "the CompressedCertificate carries " + r.remaining() +
|
|
@@ -92,7 +92,7 @@ function compressedCertificate(view, E, codes) {
|
|
|
92
92
|
// Extension a uint16 type plus a uint16-prefixed value, so the vector is a whole number of them and
|
|
93
93
|
// the smallest is 4 bytes. Walking it is what keeps a malformed Certificate message from passing as
|
|
94
94
|
// structurally valid: a one-byte vector cannot be an Extension, and accepting the bytes opaquely
|
|
95
|
-
// would report it as well-formed. Each record's value is surfaced raw
|
|
95
|
+
// would report it as well-formed. Each record's value is surfaced raw: this decodes the framing,
|
|
96
96
|
// never the extension's own contents.
|
|
97
97
|
function _extensionRecords(view, E, codes) {
|
|
98
98
|
var r = new ByteReader(view, 0, view.length, E, codes.truncated);
|
package/lib/validator-tpm.js
CHANGED
|
@@ -112,7 +112,7 @@ function parsePubArea(buf, E, code) {
|
|
|
112
112
|
throw new E(code, "unsupported TPMT_PUBLIC type 0x" + type.toString(16));
|
|
113
113
|
}
|
|
114
114
|
// TPMT_PUBLIC ends with `unique`; trailing bytes mean a malformed pubArea (and would
|
|
115
|
-
// perturb the TPM Name hash), so
|
|
115
|
+
// perturb the TPM Name hash), so the decode fails closed on them.
|
|
116
116
|
if (!r.atEnd()) throw new E(code, "pubArea has trailing bytes after the unique field (WebAuthn 8.3)");
|
|
117
117
|
return pub;
|
|
118
118
|
}
|
|
@@ -148,7 +148,7 @@ function pubKeyEqualsCose(pub, cose, E, mismatchCode, code) {
|
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
150
|
if (pub.type === TPM_ALG.RSA) {
|
|
151
|
-
// Compare the exponent as an unsigned integer over its
|
|
151
|
+
// Compare the exponent as an unsigned integer over its full width (a UINT32 up to
|
|
152
152
|
// 0xFFFFFFFF); a fixed 3-byte re-encode would silently truncate an exponent > 0xFFFFFF
|
|
153
153
|
// and let a mismatched key pass (WebAuthn 8.3 item 22).
|
|
154
154
|
var e = _uintBytes(pub.exponent >>> 0);
|
|
@@ -161,7 +161,7 @@ function pubKeyEqualsCose(pub, cose, E, mismatchCode, code) {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
// The one defined preset: the six TPMA_OBJECT bits every genuine attestation observed sets the same
|
|
164
|
-
// way. `decrypt`, `noDA` and `userWithAuth` are deliberately absent
|
|
164
|
+
// way. `decrypt`, `noDA` and `userWithAuth` are deliberately absent: they differ across real
|
|
165
165
|
// Windows Hello statements, so requiring any of them rejects working hardware. A second preset
|
|
166
166
|
// would be policy invention; a caller who wants different bits spells them out.
|
|
167
167
|
var TPM_POLICY_PROFILES = Object.assign(Object.create(null), {
|
|
@@ -171,7 +171,7 @@ var _TPM_POLICY_KEYS = Object.assign(Object.create(null), { profile: 1, objectAt
|
|
|
171
171
|
var _AUTH_POLICY_KEYS = Object.assign(Object.create(null), { present: 1, allow: 1 });
|
|
172
172
|
|
|
173
173
|
// Config-time validation of opts.tpmPolicy: a typo must never silently disable a check, so an
|
|
174
|
-
// unknown key or attribute name throws at the boundary
|
|
174
|
+
// unknown key or attribute name throws at the boundary.
|
|
175
175
|
// @enforced-by behavioral -- an opts-shape validator has no rename-proof code shape; the RED
|
|
176
176
|
// vectors (unknown key, unknown profile, unknown attribute, non-boolean value, and the
|
|
177
177
|
// sensitiveDataOrigin-without-fixedTPM rule) are the guard.
|
|
@@ -196,8 +196,8 @@ function normalizeObjectAttributePolicy(policy, E, code) {
|
|
|
196
196
|
});
|
|
197
197
|
}
|
|
198
198
|
// sec. 8.3.3.5 NOTE 1: sensitiveDataOrigin only asserts the TPM generated the key when fixedTPM
|
|
199
|
-
// is also SET
|
|
200
|
-
// nothing,
|
|
199
|
+
// is also SET; otherwise the object could have been imported. Demanding it alone asserts
|
|
200
|
+
// nothing, and a caller who thinks otherwise should hear it at config time.
|
|
201
201
|
if (want.sensitiveDataOrigin === true && want.fixedTPM !== true) {
|
|
202
202
|
throw new E(code, "opts.tpmPolicy requires sensitiveDataOrigin without fixedTPM; on its own it does not establish that the TPM generated the key (TPM 2.0 Part 2 sec. 8.3.3.5)");
|
|
203
203
|
}
|
|
@@ -215,15 +215,16 @@ function normalizeObjectAttributePolicy(policy, E, code) {
|
|
|
215
215
|
if (ap.present !== undefined && typeof ap.present !== "boolean") throw new E(code, "opts.tpmPolicy.authPolicy.present must be a boolean");
|
|
216
216
|
if (ap.allow !== undefined) {
|
|
217
217
|
if (!Array.isArray(ap.allow)) throw new E(code, "opts.tpmPolicy.authPolicy.allow must be an array");
|
|
218
|
-
//
|
|
219
|
-
// decoder is permissive: it stops at the first character that is not a hex
|
|
220
|
-
// "<digest>zz" decodes back to the digest and a non-string decodes to an empty
|
|
221
|
-
//
|
|
218
|
+
// Every entry is decoded here, at config time, so the comparison only ever sees bytes.
|
|
219
|
+
// Node's hex decoder is permissive: it stops at the first character that is not a hex
|
|
220
|
+
// digit, so "<digest>zz" decodes back to the digest and a non-string decodes to an empty
|
|
221
|
+
// buffer. Either could match a key this policy was written to exclude, including the
|
|
222
222
|
// Empty Policy. An entry that is not a Buffer or a canonical even-length hex string is a
|
|
223
|
-
// caller error
|
|
224
|
-
//
|
|
225
|
-
// canonical round-trip
|
|
226
|
-
//
|
|
223
|
+
// caller error; it fails here, before it can become a digest nobody intended.
|
|
224
|
+
// The decoding runs through guard.encoding.hex, which owns the alphabet, the even-length
|
|
225
|
+
// rule and the canonical round-trip. Those three checks were once written out here by hand
|
|
226
|
+
// and now live in one place. It also decodes, so the hex path cannot validate one string
|
|
227
|
+
// and decode another.
|
|
227
228
|
allow = ap.allow.map(function (entry, i) {
|
|
228
229
|
if (Buffer.isBuffer(entry)) return guard.bytes.snapshot(entry, E, code, "opts.tpmPolicy.authPolicy.allow[" + i + "]");
|
|
229
230
|
var label = "opts.tpmPolicy.authPolicy.allow[" + i + "]";
|
|
@@ -240,8 +241,8 @@ function normalizeObjectAttributePolicy(policy, E, code) {
|
|
|
240
241
|
authPolicy: ap ? { present: ap.present === true, allow: allow } : null };
|
|
241
242
|
}
|
|
242
243
|
|
|
243
|
-
// Apply a normalized policy to a parsed pubArea. Nothing here is a WebAuthn sec. 8.3 requirement
|
|
244
|
-
// that section constrains only pubArea's `parameters` and `unique` fields
|
|
244
|
+
// Apply a normalized policy to a parsed pubArea. Nothing here is a WebAuthn sec. 8.3 requirement,
|
|
245
|
+
// since that section constrains only pubArea's `parameters` and `unique` fields. Every rule runs
|
|
245
246
|
// only because a caller asked for it by name.
|
|
246
247
|
// @enforced-by behavioral -- an opt-gated policy gate has no rename-proof code shape, and there is
|
|
247
248
|
// no general vector shape to detect: the RED vectors (each required bit in both directions, the
|
|
@@ -251,7 +252,7 @@ function assertObjectAttributePolicy(pub, policy, E, policyCode, structuralCode)
|
|
|
251
252
|
if (!policy) return;
|
|
252
253
|
var oa = pub.objectAttributes >>> 0;
|
|
253
254
|
// Bit 31 lies inside the reserved mask, so the AND must be coerced back to unsigned: a bare
|
|
254
|
-
// `oa & 0xfff0f009` is a signed int32 and would read
|
|
255
|
+
// `oa & 0xfff0f009` is a signed int32, and a negative result would not read as "a bit is set".
|
|
255
256
|
if (policy.reservedBitsClear && ((oa & TPMA_OBJECT_RESERVED) >>> 0) !== 0) {
|
|
256
257
|
throw new E(structuralCode, "the TPMT_PUBLIC objectAttributes sets a reserved bit (TPM 2.0 Part 2 sec. 8.3.2 Table 33: shall be zero)");
|
|
257
258
|
}
|
|
@@ -271,8 +272,8 @@ function assertObjectAttributePolicy(pub, policy, E, policyCode, structuralCode)
|
|
|
271
272
|
if (pub.authPolicy.length > 64) {
|
|
272
273
|
throw new E(structuralCode, "the TPMT_PUBLIC authPolicy is " + pub.authPolicy.length + " octets, above the 64-octet TPM2B_DIGEST maximum");
|
|
273
274
|
}
|
|
274
|
-
// The
|
|
275
|
-
// encryptedDuplication are each constrained against the value the object's
|
|
275
|
+
// The remaining sec. 8.3.3 "shall" statements are all parent-relative. fixedTPM, stClear and
|
|
276
|
+
// encryptedDuplication are each constrained against the value the object's parent carries. An
|
|
276
277
|
// attestation presents one public area and no parent, so a verifier cannot evaluate them at
|
|
277
278
|
// all; they are not omitted by oversight, they are unverifiable from what is on the wire.
|
|
278
279
|
}
|