@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/schema-cms.js
CHANGED
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
* CMS handling per RFC 5652 (sec. 3 ContentInfo envelope). `parse` turns a DER or PEM
|
|
13
13
|
* (`CMS`) message into a structured object and is an OID-dispatch envelope --
|
|
14
14
|
* ContentInfo reads its `contentType` and structurally decodes SignedData (sec. 5),
|
|
15
|
-
* EnvelopedData (sec. 6, with all five RecipientInfo kinds
|
|
15
|
+
* EnvelopedData (sec. 6, with all five RecipientInfo kinds: key-transport,
|
|
16
16
|
* key-agreement per RFC 5753, KEK, password, and other, including the RFC 9629
|
|
17
17
|
* KEMRecipientInfo carried under `id-ori-kem` with ML-KEM per RFC 9936),
|
|
18
18
|
* EncryptedData (sec. 8), AuthenticatedData (sec. 9), and AuthEnvelopedData (RFC 5083,
|
|
19
19
|
* with RFC 5084 AES-GCM/CCM parameter validation); the remaining PKCS#7 content
|
|
20
20
|
* types are recognized and rejected with a precise `cms/unsupported-content-type`
|
|
21
|
-
*
|
|
21
|
+
* in place of a generic unknown-format error. A SignedData surfaces its version,
|
|
22
22
|
* digest algorithms, encapsulated content, certificate / CRL sets, and signer
|
|
23
23
|
* infos; an EnvelopedData its recipient infos and encrypted content info; an
|
|
24
24
|
* EncryptedData its encrypted content info; an AuthenticatedData its MAC
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
* content, validated AEAD parameters, and raw `mac`.
|
|
28
28
|
*
|
|
29
29
|
* CMS is a signed container: the bytes an external verifier must hash are
|
|
30
|
-
* surfaced
|
|
30
|
+
* surfaced raw and never re-serialized. `encapContentInfo.eContent` is the raw
|
|
31
31
|
* content (or `null` for a detached signature); each SignerInfo's `signature` is
|
|
32
32
|
* raw, and `signedAttrsBytes` is the on-wire `[0]` SignedAttributes TLV so a
|
|
33
33
|
* verifier can re-tag it to the universal SET the signature is computed over
|
|
34
|
-
* (sec. 5.4)
|
|
34
|
+
* (sec. 5.4); `authAttrsBytes` plays the same role for the sec. 9.2 MAC input and the
|
|
35
35
|
* RFC 5083 sec. 2.2 AAD. Embedded certificates and CRLs are surfaced as raw DER +
|
|
36
36
|
* their outer tag, validated against the closed CertificateChoices /
|
|
37
37
|
* RevocationInfoChoice tag sets, so an obsolete alternative never fails the
|
|
@@ -63,7 +63,7 @@ var ALGORITHM_IDENTIFIER = pkix.algorithmIdentifier(NS);
|
|
|
63
63
|
var ATTRIBUTE = pkix.attribute(NS);
|
|
64
64
|
var NAME = pkix.name(NS);
|
|
65
65
|
|
|
66
|
-
// CMSVersion ::= INTEGER
|
|
66
|
+
// CMSVersion ::= INTEGER, where the SignedData version is {1,3,4,5} and the SignerInfo
|
|
67
67
|
// version is {1,3} (RFC 5652 sec. 5.1, sec. 5.3). Wider accept maps than any other format.
|
|
68
68
|
var SIGNED_DATA_VERSION = pkix.versionReader(NS, { "1": 1, "3": 3, "4": 4, "5": 5 });
|
|
69
69
|
var SIGNER_VERSION = pkix.versionReader(NS, { "1": 1, "3": 3 });
|
|
@@ -188,7 +188,7 @@ function _checkContentBindingAttrs(attrs, mode) {
|
|
|
188
188
|
// content-type attribute (what it signs has no content type).
|
|
189
189
|
if (mode === "countersig") throw NS.E("cms/misplaced-attr", "a countersignature's signedAttrs must not carry a content-type attribute (RFC 5652 sec. 11.4)");
|
|
190
190
|
if (a.values.length !== 1) throw NS.E("cms/bad-content-type-attr", "the content-type attribute must be single-valued");
|
|
191
|
-
// ContentType ::= OBJECT IDENTIFIER (RFC 5652 sec. 11.1)
|
|
191
|
+
// ContentType ::= OBJECT IDENTIFIER (RFC 5652 sec. 11.1): validate the value's
|
|
192
192
|
// full syntax (tag AND minimal base-128 OID content), not just the tag, so a
|
|
193
193
|
// truncated / non-minimal subidentifier is rejected here, not at verify time.
|
|
194
194
|
try { asn1.read.oid(asn1.decode(a.values[0])); }
|
|
@@ -196,7 +196,7 @@ function _checkContentBindingAttrs(attrs, mode) {
|
|
|
196
196
|
} else if (a.type === OID_MESSAGE_DIGEST) {
|
|
197
197
|
md += 1;
|
|
198
198
|
if (a.values.length !== 1) throw NS.E("cms/bad-message-digest-attr", "the message-digest attribute must be single-valued");
|
|
199
|
-
// MessageDigest ::= OCTET STRING (RFC 5652 sec. 11.2)
|
|
199
|
+
// MessageDigest ::= OCTET STRING (RFC 5652 sec. 11.2): validate the full syntax.
|
|
200
200
|
try { asn1.read.octetString(asn1.decode(a.values[0])); }
|
|
201
201
|
catch (e) { throw NS.E("cms/bad-message-digest-attr", "the message-digest attribute value must be an OCTET STRING", e); }
|
|
202
202
|
} else if (a.type === OID_SIGNING_TIME) {
|
|
@@ -214,7 +214,7 @@ function _checkContentBindingAttrs(attrs, mode) {
|
|
|
214
214
|
|
|
215
215
|
// looksLikeSignedAttributes(bytes) -> boolean.
|
|
216
216
|
//
|
|
217
|
-
// Does `bytes` parse as a DER SignedAttributes block
|
|
217
|
+
// Does `bytes` parse as a DER SignedAttributes block, a SET OF Attribute carrying both the
|
|
218
218
|
// content-type and message-digest attributes RFC 5652 sec. 5.3 makes mandatory whenever signed
|
|
219
219
|
// attributes are present?
|
|
220
220
|
//
|
|
@@ -224,20 +224,20 @@ function _checkContentBindingAttrs(attrs, mode) {
|
|
|
224
224
|
// re-presented as one made over content: drop the signedAttrs field and set the encapsulated
|
|
225
225
|
// content to the DER of those same attributes. Sec. 5.4 then says the signature is over the content
|
|
226
226
|
// itself, which is exactly what it covers, and with no attributes there is no message-digest or
|
|
227
|
-
// content-type attribute left to disagree. The proposed standards fixes are protocol changes
|
|
228
|
-
// context string naming which mode was signed
|
|
227
|
+
// content-type attribute left to disagree. The proposed standards fixes are protocol changes (a
|
|
228
|
+
// context string naming which mode was signed) that no verifier can apply on its own.
|
|
229
229
|
//
|
|
230
230
|
// What a verifier CAN do is refuse the shape. Every message produced by the attack has, as its
|
|
231
231
|
// content, the encoded SignedAttributes of a real message, and sec. 5.3 requires those to carry
|
|
232
232
|
// both attributes named above. That makes their presence a NECESSARY condition of the attack rather
|
|
233
233
|
// than a guess, and the shape is one ordinary content does not have: a certificate, a JSON payload,
|
|
234
234
|
// arbitrary bytes, and even a SET OF other attributes all fail it. The cost is a message whose
|
|
235
|
-
// legitimate content really is an encoded SignedAttributes block signed
|
|
235
|
+
// legitimate content really is an encoded SignedAttributes block signed without attributes, which
|
|
236
236
|
// is refused as genuinely ambiguous -- sign it with attributes and it is unambiguous again.
|
|
237
237
|
//
|
|
238
238
|
// One mandatory attribute value, held to all three conditions a real SignedAttributes meets:
|
|
239
239
|
// exactly one value (RFC 5652 sec. 11.1 / sec. 11.2 make both single-valued), the right tag, and a
|
|
240
|
-
// body that actually
|
|
240
|
+
// body that actually reads as that type. All three together, for each attribute; checking the
|
|
241
241
|
// cardinality and the tag while letting an undecodable body through would refuse content the real
|
|
242
242
|
// SignedAttributes parser could never have produced, which is the false positive this whole
|
|
243
243
|
// detector is shaped to avoid.
|
|
@@ -280,10 +280,10 @@ function looksLikeSignedAttributes(bytes) {
|
|
|
280
280
|
if (!schema.isUniversal(node, asn1.TAGS.SET) || !node.constructed) return false;
|
|
281
281
|
var kids = node.children || [];
|
|
282
282
|
if (!kids.length) return false;
|
|
283
|
-
// Every rule _checkContentBindingAttrs applies to a
|
|
283
|
+
// Every rule _checkContentBindingAttrs applies to a real SignedAttributes is applied here, and
|
|
284
284
|
// for one reason: the detector must match only what a conforming block can be. A set the real
|
|
285
285
|
// parser would have rejected cannot be the preimage of any signature, so matching it would refuse
|
|
286
|
-
// content no attack could have produced. Enumerated against that function
|
|
286
|
+
// content no attack could have produced. Enumerated against that function, never discovered
|
|
287
287
|
// one rule at a time -- no duplicate attribute types (sec. 5.3), content-type single-valued and a
|
|
288
288
|
// readable OID (sec. 11.1), message-digest single-valued and a readable OCTET STRING (sec. 11.2),
|
|
289
289
|
// signing-time when present single-valued and a readable Time (sec. 11.3).
|
|
@@ -317,7 +317,7 @@ function looksLikeSignedAttributes(bytes) {
|
|
|
317
317
|
// deliberately not applied: this decoder caps values per attribute as a resource limit of its
|
|
318
318
|
// own, and a limit this implementation chose is not a fact about what a signature can cover.
|
|
319
319
|
// An external signer may sign a conforming set larger than that cap, and the stripped message
|
|
320
|
-
// presents those bytes as opaque content where the cap never applies
|
|
320
|
+
// presents those bytes as opaque content where the cap never applies, so refusing to recognize
|
|
321
321
|
// it because of a local limit would miss exactly the preimage the attack reuses.
|
|
322
322
|
var n = (vs.children || []).length;
|
|
323
323
|
if (n < 1) return false;
|
|
@@ -325,7 +325,7 @@ function looksLikeSignedAttributes(bytes) {
|
|
|
325
325
|
// The two mandatory attributes are checked down to their VALUES, not just their type OIDs.
|
|
326
326
|
// RFC 5652 sec. 11.1 makes content-type a single OBJECT IDENTIFIER and sec. 11.2 makes
|
|
327
327
|
// message-digest a single OCTET STRING, so a set carrying those OIDs over an empty or
|
|
328
|
-
// wrongly-typed value
|
|
328
|
+
// wrongly-typed value cannot be the preimage of a real signature, and refusing it would be a
|
|
329
329
|
// false positive on content that merely resembles the shape. The detector has to stay a
|
|
330
330
|
// necessary condition of the attack; anything broader costs a legitimate caller.
|
|
331
331
|
var vals = vs.children || [];
|
|
@@ -344,17 +344,17 @@ function looksLikeSignedAttributes(bytes) {
|
|
|
344
344
|
return sawContentType && sawMessageDigest;
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
// RFC 5652 sec. 5.3 / sec. 9.3
|
|
347
|
+
// RFC 5652 sec. 5.3 / sec. 9.3: when a content-type attribute is present, it MUST
|
|
348
348
|
// be single-valued (sec. 11.1) and its value MUST equal the eContentType (a
|
|
349
349
|
// cross-field consistency both parsed here). Shared by SignedData signedAttrs,
|
|
350
|
-
// AuthenticatedData authAttrs, and AuthEnvelopedData authAttrs
|
|
350
|
+
// AuthenticatedData authAttrs, and AuthEnvelopedData authAttrs, where the single-value
|
|
351
351
|
// rule holds even where content-type is not REQUIRED (RFC 5083), so an
|
|
352
352
|
// expected-first-value-plus-extra set can never surface as ambiguous.
|
|
353
353
|
function _assertContentTypeMatchesAttrs(attrs, eContentType) {
|
|
354
354
|
for (var i = 0; i < attrs.length; i++) {
|
|
355
355
|
if (attrs[i].type !== OID_CONTENT_TYPE) continue;
|
|
356
356
|
if (attrs[i].values.length !== 1) throw NS.E("cms/bad-content-type-attr", "the content-type attribute must be single-valued (RFC 5652 sec. 11.1)");
|
|
357
|
-
// ContentType ::= OBJECT IDENTIFIER
|
|
357
|
+
// ContentType ::= OBJECT IDENTIFIER: validate the value's full syntax (tag and
|
|
358
358
|
// minimal base-128 OID content) with the CMS typed verdict, so a malformed value
|
|
359
359
|
// on a path that does not run _checkContentBindingAttrs (AuthEnvelopedData,
|
|
360
360
|
// RFC 5083 sec. 2.1) surfaces cms/bad-content-type-attr, not the raw asn1/* error.
|
|
@@ -366,8 +366,8 @@ function _assertContentTypeMatchesAttrs(attrs, eContentType) {
|
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
// RFC 5652 sec. 5.3 -- an attribute set MUST NOT include multiple instances of the
|
|
369
|
-
// same attribute type. AuthEnvelopedData authAttrs (RFC 5083 sec. 2.1) gets
|
|
370
|
-
// duplicate check
|
|
369
|
+
// same attribute type. AuthEnvelopedData authAttrs (RFC 5083 sec. 2.1) gets only this
|
|
370
|
+
// duplicate check, and not the content-type/message-digest presence rules (RFC 5652
|
|
371
371
|
// sec. 11.1/sec. 11.2 bind only signed-data and authenticated-data), so it must not reuse
|
|
372
372
|
// _checkContentBindingAttrs, which would over-enforce.
|
|
373
373
|
function _checkNoDuplicateAttrs(attrs) {
|
|
@@ -418,7 +418,7 @@ function _validateAeadParams(alg, macLen) {
|
|
|
418
418
|
}
|
|
419
419
|
var legal = kind === "gcm" ? AEAD_GCM_ICVLENS : AEAD_CCM_ICVLENS;
|
|
420
420
|
if (!legal.has(icvLen)) throw NS.E("cms/bad-aead-params", "the AES-" + K + " aes-ICVlen " + icvLen + " is not an allowed value (RFC 5084)");
|
|
421
|
-
// X.690 sec. 11.5: a DEFAULT value MUST be omitted in DER
|
|
421
|
+
// X.690 sec. 11.5: a DEFAULT value MUST be omitted in DER, so an encoded ICVlen equal
|
|
422
422
|
// to the default 12 is non-canonical.
|
|
423
423
|
if (icvEncoded && icvLen === 12) throw NS.E("cms/non-canonical-default", "the AEAD aes-ICVlen equal to the DEFAULT 12 MUST be omitted (X.690 sec. 11.5)");
|
|
424
424
|
// RFC 5084 sec. 3.1/sec. 3.2: aes-ICVlen MUST match the AuthEnvelopedData mac length.
|
|
@@ -426,7 +426,7 @@ function _validateAeadParams(alg, macLen) {
|
|
|
426
426
|
return { kind: kind, nonce: nonce, icvLen: icvLen };
|
|
427
427
|
}
|
|
428
428
|
|
|
429
|
-
// RFC 5652 sec. 5.3 / sec. 9.1 + RFC 5083 sec. 2.1
|
|
429
|
+
// RFC 5652 sec. 5.3 / sec. 9.1 + RFC 5083 sec. 2.1: a signed / authenticated attribute SET
|
|
430
430
|
// MUST be DER encoded even when the enclosing structure was decoded as BER (its
|
|
431
431
|
// bytes feed the sec. 5.4 / sec. 9.2 re-tagged hash / MAC input, so a non-DER TLV makes
|
|
432
432
|
// the surfaced raw bytes unusable for verification). Strictly re-decoding the
|
|
@@ -437,8 +437,8 @@ function _assertDerEncodedAttrs(node, code) {
|
|
|
437
437
|
catch (e) { throw NS.E(code, "the attribute set must be DER encoded even inside a BER envelope (RFC 5652 sec. 5.3)", e); }
|
|
438
438
|
}
|
|
439
439
|
|
|
440
|
-
// A CertificateChoices / RevocationInfoChoice element, surfaced
|
|
441
|
-
// outer tag)
|
|
440
|
+
// A CertificateChoices / RevocationInfoChoice element, surfaced raw (its DER +
|
|
441
|
+
// outer tag) and never recursively parsed: the obsolete CHOICE alternatives
|
|
442
442
|
// (extendedCertificate, attribute certs, otherRevocationInfo) never fail the
|
|
443
443
|
// parse, and a caller re-parses a `certificate`/`CertificateList` element itself.
|
|
444
444
|
function rawElement(item) {
|
|
@@ -515,7 +515,7 @@ var ISSUER_AND_SERIAL = schema.seq([
|
|
|
515
515
|
});
|
|
516
516
|
|
|
517
517
|
// SignerIdentifier ::= CHOICE { issuerAndSerialNumber IssuerAndSerialNumber,
|
|
518
|
-
// subjectKeyIdentifier [0] IMPLICIT OCTET STRING } (RFC 5652 sec. 5.3)
|
|
518
|
+
// subjectKeyIdentifier [0] IMPLICIT OCTET STRING } (RFC 5652 sec. 5.3), where the arm is
|
|
519
519
|
// disambiguated by tag (universal SEQUENCE vs context [0]).
|
|
520
520
|
var SIGNER_IDENTIFIER = schema.choice([
|
|
521
521
|
{ when: { tagClass: "universal", tagNumber: asn1.TAGS.SEQUENCE }, schema: ISSUER_AND_SERIAL },
|
|
@@ -526,10 +526,10 @@ var SIGNER_IDENTIFIER = schema.choice([
|
|
|
526
526
|
// signedAttrs [0] IMPLICIT OPTIONAL, signatureAlgorithm, signature OCTET STRING,
|
|
527
527
|
// unsignedAttrs [1] IMPLICIT OPTIONAL } (RFC 5652 sec. 5.3). signedAttrs/unsignedAttrs
|
|
528
528
|
// are positional optionals (a required signatureAlgorithm sits between them, so
|
|
529
|
-
// they cannot be a trailing block).
|
|
529
|
+
// they cannot be a trailing block). One definition drives both consumers: the
|
|
530
530
|
// signerInfos of a SignedData (mode "content") and a countersignature attribute
|
|
531
531
|
// value (mode "countersig", RFC 5652 sec. 11.4: syntactically a SignerInfo whose
|
|
532
|
-
// signedAttrs MUST carry message-digest and MUST NOT carry content-type)
|
|
532
|
+
// signedAttrs MUST carry message-digest and MUST NOT carry content-type), so
|
|
533
533
|
// the two can never diverge structurally.
|
|
534
534
|
function makeSignerInfo(mode) {
|
|
535
535
|
return schema.seq([
|
|
@@ -574,7 +574,7 @@ function makeSignerInfo(mode) {
|
|
|
574
574
|
// sec. 11.4 -- every countersignature value IS a SignerInfo (validated by
|
|
575
575
|
// content, never accepted on the attribute type alone). Multiple
|
|
576
576
|
// countersignature instances are explicitly permitted here, and a
|
|
577
|
-
// countersignature's own unsignedAttrs may nest further ones
|
|
577
|
+
// countersignature's own unsignedAttrs may nest further ones, so the
|
|
578
578
|
// recursion is bounded by the decoder's depth cap.
|
|
579
579
|
for (var u = 0; u < unsignedAttrs.length; u++) {
|
|
580
580
|
if (unsignedAttrs[u].type !== OID_COUNTERSIGNATURE) continue;
|
|
@@ -605,7 +605,7 @@ var COUNTERSIGNATURE_SIGNER_INFO = makeSignerInfo("countersig");
|
|
|
605
605
|
// SignedData ::= SEQUENCE { version CMSVersion, digestAlgorithms SET OF,
|
|
606
606
|
// encapContentInfo, certificates [0] IMPLICIT OPTIONAL, crls [1] IMPLICIT
|
|
607
607
|
// OPTIONAL, signerInfos SET OF } (RFC 5652 sec. 5.1). digestAlgorithms and
|
|
608
|
-
// signerInfos are min:0
|
|
608
|
+
// signerInfos are min:0, since a degenerate certs-only SignedData carries neither.
|
|
609
609
|
var SIGNED_DATA = schema.seq([
|
|
610
610
|
schema.field("version", SIGNED_DATA_VERSION),
|
|
611
611
|
schema.field("digestAlgorithms", schema.setOf(ALGORITHM_IDENTIFIER, { min: 0, code: "cms/bad-digest-algorithms", what: "digestAlgorithms" })),
|
|
@@ -658,10 +658,10 @@ var T = asn1.TAGS;
|
|
|
658
658
|
|
|
659
659
|
// EncryptedContentInfo ::= SEQUENCE { contentType OID, contentEncryptionAlgorithm
|
|
660
660
|
// AlgorithmIdentifier, encryptedContent [0] IMPLICIT OCTET STRING OPTIONAL } (RFC
|
|
661
|
-
// 5652 sec. 6.1). encryptedContent is [0] IMPLICIT (context
|
|
661
|
+
// 5652 sec. 6.1). encryptedContent is [0] IMPLICIT (context primitive), so its content
|
|
662
662
|
// octets ARE the ciphertext directly, so it reads through implicitOctetString(0),
|
|
663
|
-
//
|
|
664
|
-
// length header). The ciphertext + algorithm parameters are surfaced
|
|
663
|
+
// and not the [0] EXPLICIT shape ENCAP_CONTENT_INFO uses (which would double-strip a
|
|
664
|
+
// length header). The ciphertext + algorithm parameters are surfaced raw.
|
|
665
665
|
var ENCRYPTED_CONTENT_INFO = schema.seq([
|
|
666
666
|
schema.field("contentType", schema.oidLeaf()),
|
|
667
667
|
schema.field("contentEncryptionAlgorithm", ALGORITHM_IDENTIFIER),
|
|
@@ -678,7 +678,7 @@ var ENCRYPTED_CONTENT_INFO = schema.seq([
|
|
|
678
678
|
});
|
|
679
679
|
|
|
680
680
|
// RecipientIdentifier ::= CHOICE { issuerAndSerialNumber, subjectKeyIdentifier [0]
|
|
681
|
-
// IMPLICIT OCTET STRING } (RFC 5652 sec. 6.2.1)
|
|
681
|
+
// IMPLICIT OCTET STRING } (RFC 5652 sec. 6.2.1), structurally identical to
|
|
682
682
|
// SignerIdentifier; reuse ISSUER_AND_SERIAL + the implicitOctetString(0) leaf.
|
|
683
683
|
var RECIPIENT_IDENTIFIER = schema.choice([
|
|
684
684
|
{ when: { tagClass: "universal", tagNumber: T.SEQUENCE }, schema: ISSUER_AND_SERIAL },
|
|
@@ -712,7 +712,7 @@ var KEY_TRANS_RECIPIENT_INFO = schema.seq([
|
|
|
712
712
|
});
|
|
713
713
|
|
|
714
714
|
// OriginatorPublicKey ::= SEQUENCE { algorithm, publicKey BIT STRING } (RFC 5753
|
|
715
|
-
// sec. 3.1.1), reached as originatorKey [1] IMPLICIT
|
|
715
|
+
// sec. 3.1.1), reached as originatorKey [1] IMPLICIT: SPKI-shaped but cannot reuse
|
|
716
716
|
// pkix.spki (which asserts a universal SEQUENCE), so assert:"constructed".
|
|
717
717
|
var ORIGINATOR_PUBLIC_KEY = schema.seq([
|
|
718
718
|
schema.field("algorithm", ALGORITHM_IDENTIFIER),
|
|
@@ -732,7 +732,7 @@ var ORIGINATOR_IDENTIFIER_OR_KEY = schema.choice([
|
|
|
732
732
|
|
|
733
733
|
// RecipientKeyIdentifier (RFC 5652 sec. 6.2.2) and KEKIdentifier (sec. 6.2.3) are one
|
|
734
734
|
// shape -- { <keyId> OCTET STRING, date GeneralizedTime OPTIONAL, other
|
|
735
|
-
// OtherKeyAttribute OPTIONAL }
|
|
735
|
+
// OtherKeyAttribute OPTIONAL }, differing only in the key-id field's name and
|
|
736
736
|
// the enclosing tag form. One factory defines both so the OPTIONAL handling
|
|
737
737
|
// (date and the raw-surfaced OtherKeyAttribute) cannot diverge between them.
|
|
738
738
|
function keyIdentifierSchema(keyIdName, assert, code, what) {
|
|
@@ -752,7 +752,7 @@ function keyIdentifierSchema(keyIdName, assert, code, what) {
|
|
|
752
752
|
});
|
|
753
753
|
}
|
|
754
754
|
|
|
755
|
-
// Reached as rKeyId [0] IMPLICIT (a SEQUENCE
|
|
755
|
+
// Reached as rKeyId [0] IMPLICIT (a SEQUENCE, constructed, unlike ktri's [0] leaf).
|
|
756
756
|
var RECIPIENT_KEY_IDENTIFIER = keyIdentifierSchema("subjectKeyIdentifier",
|
|
757
757
|
"constructed", "cms/bad-recipient-key-identifier", "RecipientKeyIdentifier");
|
|
758
758
|
|
|
@@ -945,7 +945,7 @@ var RECIPIENT_INFO = schema.choice([
|
|
|
945
945
|
], { code: "cms/bad-recipient-info", what: "RecipientInfo" });
|
|
946
946
|
|
|
947
947
|
// OriginatorInfo ::= [0] IMPLICIT SEQUENCE { certs [0] IMPLICIT OPTIONAL, crls [1]
|
|
948
|
-
// IMPLICIT OPTIONAL } (RFC 5652 sec. 6.1). Members surfaced
|
|
948
|
+
// IMPLICIT OPTIONAL } (RFC 5652 sec. 6.1). Members surfaced raw (their outer tag feeds
|
|
949
949
|
// the version rule).
|
|
950
950
|
var ORIGINATOR_INFO = schema.seq([
|
|
951
951
|
schema.optional("certs", schema.implicitSetOf(0, schema.any(), { min: 1, code: "cms/bad-originator-certs", what: "certs" }), { tag: 0 }),
|
|
@@ -977,8 +977,8 @@ function _expectedEnvelopedDataVersion(originatorInfo, recipientInfos, hasUnprot
|
|
|
977
977
|
}
|
|
978
978
|
|
|
979
979
|
// RFC 5652 sec. 9.1 -- the exact AuthenticatedData CMSVersion, from originatorInfo's raw
|
|
980
|
-
// cert/crl outer tags
|
|
981
|
-
//
|
|
980
|
+
// cert/crl outer tags only. Unlike EnvelopedData sec. 6.1, the recipient-info kinds do
|
|
981
|
+
// not influence the version. If originatorInfo is present and (other-cert [3] or
|
|
982
982
|
// other-crl [1]) -> 3; ELSE IF originatorInfo present AND v2AttrCert [2] -> 1; ELSE 0.
|
|
983
983
|
function _expectedAuthDataVersion(originatorInfo) {
|
|
984
984
|
if (!originatorInfo) return 0;
|
|
@@ -991,7 +991,7 @@ function _expectedAuthDataVersion(originatorInfo) {
|
|
|
991
991
|
// EnvelopedData ::= SEQUENCE { version, originatorInfo [0] IMPLICIT OPTIONAL,
|
|
992
992
|
// recipientInfos RecipientInfos (SET SIZE 1..MAX), encryptedContentInfo,
|
|
993
993
|
// unprotectedAttrs [1] IMPLICIT OPTIONAL } (RFC 5652 sec. 6.1). recipientInfos is
|
|
994
|
-
// min:1 (an empty SET is non-conformant
|
|
994
|
+
// min:1 (an empty SET is non-conformant, the inverse of SignedData's degenerate
|
|
995
995
|
// signerInfos).
|
|
996
996
|
var ENVELOPED_DATA = schema.seq([
|
|
997
997
|
schema.field("version", ENVELOPED_DATA_VERSION),
|
|
@@ -1021,7 +1021,7 @@ var ENVELOPED_DATA = schema.seq([
|
|
|
1021
1021
|
});
|
|
1022
1022
|
|
|
1023
1023
|
// EncryptedData ::= SEQUENCE { version, encryptedContentInfo, unprotectedAttrs [1]
|
|
1024
|
-
// IMPLICIT OPTIONAL } (RFC 5652 sec. 8)
|
|
1024
|
+
// IMPLICIT OPTIONAL } (RFC 5652 sec. 8): no recipients, no originatorInfo; the CEK is
|
|
1025
1025
|
// distributed out of band. version is 0, or 2 iff unprotectedAttrs are present.
|
|
1026
1026
|
var ENCRYPTED_DATA = schema.seq([
|
|
1027
1027
|
schema.field("version", ENCRYPTED_DATA_VERSION),
|
|
@@ -1150,7 +1150,7 @@ var AUTH_ENVELOPED_DATA = schema.seq([
|
|
|
1150
1150
|
// sec. 2.2 AAD is the re-tagged DER SET). No content-type/message-digest
|
|
1151
1151
|
// presence rules bind here (sec. 11.1/sec. 11.2 name signed-data and
|
|
1152
1152
|
// authenticated-data; sec. 2.1 says message-digest SHOULD NOT appear), so only
|
|
1153
|
-
// the duplicate rule, the sec. 11 placement rows, and
|
|
1153
|
+
// the duplicate rule, the sec. 11 placement rows, and, when a content-type
|
|
1154
1154
|
// attribute IS present -- the value == contentType coherence apply.
|
|
1155
1155
|
_assertDerEncodedAttrs(m.fields.authAttrs.node, "cms/bad-auth-attrs");
|
|
1156
1156
|
authAttrs = m.fields.authAttrs.value.items.map(function (it) { return it.value.result; });
|
|
@@ -1265,8 +1265,8 @@ var CONTENT_INFO = schema.seq([
|
|
|
1265
1265
|
* `{ type: "ori", oriType, oriValue, kemri }` with the parsed KEMRecipientInfo in
|
|
1266
1266
|
* `kemri`. Every result additionally carries `contentType` (the dotted OID) and
|
|
1267
1267
|
* `contentTypeName` (its registry name) naming which of the five shapes was
|
|
1268
|
-
* dispatched.
|
|
1269
|
-
* `signature`, `signedAttrsBytes`, `authAttrsBytes`, `mac`
|
|
1268
|
+
* dispatched. The raw byte ranges an external verifier hashes (`eContent`,
|
|
1269
|
+
* `signature`, `signedAttrsBytes`, `authAttrsBytes`, `mac`) are surfaced
|
|
1270
1270
|
* exactly as on the wire. The remaining PKCS#7 types throw `cms/unsupported-content-type`; an
|
|
1271
1271
|
* unrecognized OID throws `cms/unknown-content-type`; a malformed structure
|
|
1272
1272
|
* throws a typed `CmsError` (`cms/*`) and a leaf-level codec fault surfaces as
|
package/lib/schema-crl.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* rules, and the raw `tbsCertList` bytes are returned for signature checking.
|
|
20
20
|
*
|
|
21
21
|
* @card
|
|
22
|
-
* Parse DER / PEM X.509 CRLs into structured, validated fields
|
|
22
|
+
* Parse DER / PEM X.509 CRLs into structured, validated fields: revoked
|
|
23
23
|
* serials with real-`Date` revocation times, named extensions, fail-closed.
|
|
24
24
|
*/
|
|
25
25
|
|
|
@@ -34,14 +34,14 @@ var PemError = frameworkError.PemError;
|
|
|
34
34
|
var TAGS = asn1.TAGS;
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
// CRLReason ::= ENUMERATED (RFC 5280 sec. 5.3.1)
|
|
37
|
+
// CRLReason ::= ENUMERATED (RFC 5280 sec. 5.3.1). Value 7 is unused/reserved.
|
|
38
38
|
// The legal set lives once in pkix.CRL_REASON_NAMES (shared with the OCSP
|
|
39
39
|
// RevokedInfo decoder, which surfaces the name; this decoder the numeric code).
|
|
40
40
|
var CRL_REASONS = pkix.CRL_REASON_NAMES;
|
|
41
41
|
|
|
42
|
-
// Extension-value decoding is keyed off the
|
|
43
|
-
// load from the canonical name)
|
|
44
|
-
// pki.oid.register() display override
|
|
42
|
+
// Extension-value decoding is keyed off the stable dotted OID (resolved once at
|
|
43
|
+
// load from the canonical name) and not the mutable display name, so a caller's
|
|
44
|
+
// pki.oid.register() display override cannot change parse behaviour.
|
|
45
45
|
var OID_CRL_NUMBER = oid.byName("cRLNumber");
|
|
46
46
|
var OID_REASON_CODE = oid.byName("reasonCode");
|
|
47
47
|
var OID_INVALIDITY_DATE = oid.byName("invalidityDate");
|
|
@@ -58,9 +58,9 @@ var EXTENSIONS = pkix.extensions(NS);
|
|
|
58
58
|
// UTCTime). invalidityDate stays GeneralizedTime-only (sec. 5.3.2, decodeExt).
|
|
59
59
|
var TIME = pkix.time(NS);
|
|
60
60
|
|
|
61
|
-
// CRL Version ::= INTEGER { v1(0), v2(1) }
|
|
61
|
+
// CRL Version ::= INTEGER { v1(0), v2(1) }, a bare INTEGER (not [0] EXPLICIT).
|
|
62
62
|
// A CRL is at most v2; reject an explicit v1 (DER forbids the default) and any
|
|
63
|
-
// value >= 2. Do
|
|
63
|
+
// value >= 2. Do not reuse the certificate readVersion (it maps 2 -> v3).
|
|
64
64
|
var CRL_VERSION = pkix.versionReader(NS, { "1": 2 });
|
|
65
65
|
|
|
66
66
|
// The three cheap, high-value CRL extension values are decoded from their raw
|
package/lib/schema-crmf.js
CHANGED
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
* when supplied, MUST be 2 (RFC 4211 sec. 5); certReqId is an unbounded signed
|
|
25
25
|
* INTEGER (the RFC 9483 `-1` sentinel is legal). The `CertRequest` byte range
|
|
26
26
|
* the proof-of-possession signature covers, and each `poposkInput`, are surfaced
|
|
27
|
-
*
|
|
28
|
-
* keyEncipherment / keyAgreement POP arms are surfaced
|
|
27
|
+
* raw for a downstream verifier; registration controls / info values and the
|
|
28
|
+
* keyEncipherment / keyAgreement POP arms are surfaced raw and never recursed.
|
|
29
29
|
* DER-only, fail-closed.
|
|
30
30
|
*
|
|
31
31
|
* @card
|
|
32
32
|
* Parse DER / PEM RFC 4211 CertReqMessages into requested-certificate templates,
|
|
33
|
-
* proof-of-possession, and registration controls
|
|
33
|
+
* proof-of-possession, and registration controls: dual-accepted names, raw
|
|
34
34
|
* verifier inputs, fail-closed.
|
|
35
35
|
*/
|
|
36
36
|
|
|
@@ -102,7 +102,7 @@ var OPTIONAL_VALIDITY = schema.seq([
|
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
// ---- ProofOfPossession (sec. 4) ------------------------------------------
|
|
105
|
-
// raVerified [0] NULL
|
|
105
|
+
// raVerified [0] NULL: the RA has already verified POP out of band.
|
|
106
106
|
var POPO_RAVERIFIED = schema.decode(function (n, ctx) {
|
|
107
107
|
try { asn1.read.nullImplicit(n, 0); }
|
|
108
108
|
catch (e) { throw ctx.E("crmf/bad-popo", "raVerified [0] must be an IMPLICIT NULL", e); }
|
|
@@ -110,9 +110,9 @@ var POPO_RAVERIFIED = schema.decode(function (n, ctx) {
|
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
// When poposkInput is present the POP signature is over the DER of the
|
|
113
|
-
// POPOSigningKeyInput
|
|
113
|
+
// POPOSigningKeyInput value (RFC 4211 sec. 4.1), which is a SEQUENCE. But on the wire
|
|
114
114
|
// poposkInput is an IMPLICIT [0] field, so its bytes lead with the context tag
|
|
115
|
-
// (0xA0)
|
|
115
|
+
// (0xA0) and not the SEQUENCE tag a verifier hashes. `asn1.sequenceTlv` recovers
|
|
116
116
|
// the SEQUENCE-tagged signed region from the field's content; `signedBytes` is that
|
|
117
117
|
// region and `bytes` keeps the raw wire [0] TLV, so a verifier can use either
|
|
118
118
|
// encoding its peer produced.
|
|
@@ -120,7 +120,7 @@ var POPO_RAVERIFIED = schema.decode(function (n, ctx) {
|
|
|
120
120
|
// POPOSigningKeyInput ::= SEQUENCE { authInfo CHOICE { sender [0] GeneralName,
|
|
121
121
|
// publicKeyMAC PKMACValue }, publicKey SubjectPublicKeyInfo } (sec. 4.1). Structurally
|
|
122
122
|
// validated (not deferred raw), so an empty or malformed poposkInput fails closed
|
|
123
|
-
//
|
|
123
|
+
// and never surfaces a signed region that was not a well-formed POP input.
|
|
124
124
|
// PKMACValue ::= SEQUENCE { algId AlgorithmIdentifier, value BIT STRING }. The
|
|
125
125
|
// MAC value is octet-string algorithm output handed to an external verifier;
|
|
126
126
|
// with no in-tree POP verify layer, octet alignment is enforced at parse.
|
|
@@ -130,7 +130,7 @@ var PKMAC_VALUE = schema.seq([
|
|
|
130
130
|
], {
|
|
131
131
|
assert: "sequence", arity: { exact: 2 }, code: "crmf/bad-popo", what: "PKMACValue",
|
|
132
132
|
build: function (m, ctx) {
|
|
133
|
-
// The parsed BIT STRING structure { unusedBits, bytes }
|
|
133
|
+
// The parsed BIT STRING structure { unusedBits, bytes }, a structural
|
|
134
134
|
// octet-alignment check on the unused-bit COUNT, not a comparison of the
|
|
135
135
|
// MAC secret (there is no in-tree POP verify), so no timing oracle exists.
|
|
136
136
|
var macBitString = m.fields.value.value;
|
|
@@ -142,7 +142,7 @@ var PKMAC_VALUE = schema.seq([
|
|
|
142
142
|
});
|
|
143
143
|
|
|
144
144
|
// authInfo CHOICE: sender [0] GeneralName is EXPLICIT (GeneralName is itself a
|
|
145
|
-
// CHOICE); publicKeyMAC is a bare PKMACValue SEQUENCE
|
|
145
|
+
// CHOICE); publicKeyMAC is a bare PKMACValue SEQUENCE, disjoint by tag class.
|
|
146
146
|
var POPOSK_AUTH_INFO = schema.choice([
|
|
147
147
|
{ when: { tagClass: "context", tagNumber: 0 }, schema: schema.explicit(0, pkix.generalName(NS, { code: "crmf/bad-popo" }), { code: "crmf/bad-popo" }) },
|
|
148
148
|
{ when: { tagClass: "universal", tagNumber: TAGS.SEQUENCE }, schema: PKMAC_VALUE },
|
|
@@ -154,7 +154,7 @@ var POPOSK_INPUT = schema.seq([
|
|
|
154
154
|
], { assert: "implicit", implicitTag: 0, arity: { exact: 2 }, code: "crmf/bad-popo", what: "POPOSigningKeyInput" });
|
|
155
155
|
|
|
156
156
|
// POPOSigningKey ::= SEQUENCE { poposkInput [0] POPOSigningKeyInput OPTIONAL,
|
|
157
|
-
// algorithmIdentifier AlgorithmIdentifier, signature BIT STRING }
|
|
157
|
+
// algorithmIdentifier AlgorithmIdentifier, signature BIT STRING }, IMPLICIT [1].
|
|
158
158
|
var POPO_SIGNING_KEY = schema.seq([
|
|
159
159
|
schema.optional("poposkInput", schema.decode(function (n, ctx) {
|
|
160
160
|
// Validate the full POPOSigningKeyInput structure fail-closed, then surface the
|
|
@@ -187,9 +187,9 @@ var POPO_SIGNING_KEY = schema.seq([
|
|
|
187
187
|
// keyEncipherment [2] / keyAgreement [3] POPOPrivKey (sec. 4). POPOPrivKey is a
|
|
188
188
|
// 5-arm CHOICE { thisMessage [0], subsequentMessage [1], dhMAC [2], agreeMAC [3],
|
|
189
189
|
// encryptedKey [4] }, so X.680 sec. 31.2.7 forces the outer [2]/[3] tag to be EXPLICIT
|
|
190
|
-
// (a CHOICE has no single tag to replace). Validate that shell
|
|
191
|
-
// wrapper around exactly one inner context [0]..[4] alternative
|
|
192
|
-
// the arm
|
|
190
|
+
// (a CHOICE has no single tag to replace). Validate that shell, a constructed
|
|
191
|
+
// wrapper around exactly one inner context [0]..[4] alternative, before surfacing
|
|
192
|
+
// the arm raw, so a primitive, empty or mis-tagged node is rejected and never
|
|
193
193
|
// reported to a verifier as a well-formed POP. The inner alternative's own value
|
|
194
194
|
// (incl. the encryptedKey [4] EnvelopedData) stays deferred to the verify layer.
|
|
195
195
|
// POPOPrivKey ::= CHOICE { thisMessage [0] BIT STRING (deprecated), subsequentMessage
|
|
@@ -240,7 +240,7 @@ function popoPrivKey(type) {
|
|
|
240
240
|
try { schema.embeddedDer(PKMAC_VALUE, asn1.sequenceTlv(inner), NS, { code: "crmf/bad-popo", what: "agreeMAC [3] PKMACValue" }); }
|
|
241
241
|
catch (e) { throw ctx.E("crmf/bad-popo", "agreeMAC [3] must be a PKMACValue SEQUENCE { algId, BIT STRING } (RFC 4211 sec. 4.2)", e); }
|
|
242
242
|
}
|
|
243
|
-
// encryptedKey [4] EnvelopedData
|
|
243
|
+
// encryptedKey [4] EnvelopedData: validate the CMS structure fail-closed by
|
|
244
244
|
// composing the shared EnvelopedData decoder (retag the IMPLICIT [4] to the
|
|
245
245
|
// universal SEQUENCE it decodes as). The EncKeyWithID identity check lives inside
|
|
246
246
|
// the ENCRYPTED content, so it stays a verify-layer concern.
|
|
@@ -248,12 +248,12 @@ function popoPrivKey(type) {
|
|
|
248
248
|
var env;
|
|
249
249
|
try { env = cms.walkEnvelopedData(asn1.decode(asn1.sequenceTlv(inner))); }
|
|
250
250
|
catch (e) { throw ctx.E("crmf/bad-popo", "encryptedKey [4] must be a well-formed EnvelopedData (RFC 4211 sec. 4.2, RFC 5652 sec. 6.1)", e); }
|
|
251
|
-
// RFC 4211 sec. 4.2
|
|
251
|
+
// RFC 4211 sec. 4.2: the enveloped content type MUST be id-ct-encKeyWithID (the
|
|
252
252
|
// ContentType OID is in the clear even though the content itself is encrypted).
|
|
253
253
|
if (env.encryptedContentInfo.contentType !== oid.byName("encKeyWithID")) {
|
|
254
254
|
throw ctx.E("crmf/bad-popo", "encryptedKey [4] EnvelopedData content type MUST be id-ct-encKeyWithID (RFC 4211 sec. 4.2)");
|
|
255
255
|
}
|
|
256
|
-
// The encrypted key material MUST be present
|
|
256
|
+
// The encrypted key material MUST be present. CMS allows a detached
|
|
257
257
|
// EnvelopedData (encryptedContent OPTIONAL), but a POP with no key to verify
|
|
258
258
|
// or archive is meaningless. The shared CMS assert rejects a null OR
|
|
259
259
|
// zero-length attached ciphertext (the rule the EST / PKCS#12 siblings hold).
|
|
@@ -275,7 +275,7 @@ var PROOF_OF_POSSESSION = schema.choice([
|
|
|
275
275
|
|
|
276
276
|
// ---- Controls / regInfo (sec. 5, sec. 6, sec. 7) ---------------------------------
|
|
277
277
|
// AttributeTypeAndValue ::= SEQUENCE { type OBJECT IDENTIFIER, value ANY DEFINED BY
|
|
278
|
-
// type }. The list is decoded; each value stays
|
|
278
|
+
// type }. The list is decoded; each value stays raw (the per-OID value semantics
|
|
279
279
|
// are deferred). Shared shape for both controls and regInfo.
|
|
280
280
|
var CONTROL = schema.seq([
|
|
281
281
|
schema.field("type", schema.oidLeaf()),
|
|
@@ -293,10 +293,10 @@ var CONTROLS = schema.seqOf(CONTROL, { assert: "sequence", min: 1, code: "crmf/b
|
|
|
293
293
|
// regInfo ::= SEQUENCE SIZE(1..MAX) OF AttributeTypeAndValue (sec. 7).
|
|
294
294
|
var REG_INFO = schema.seqOf(CONTROL, { assert: "sequence", min: 1, code: "crmf/bad-reg-info", what: "regInfo", build: mapControls });
|
|
295
295
|
|
|
296
|
-
// ---- CertTemplate (sec. 5)
|
|
296
|
+
// ---- CertTemplate (sec. 5), the IMPLICIT-TAGS core ------------------------
|
|
297
297
|
// CertTemplate ::= SEQUENCE { version [0], serialNumber [1], signingAlg [2],
|
|
298
298
|
// issuer [3] Name, validity [4] OptionalValidity, subject [5] Name, publicKey [6]
|
|
299
|
-
// SubjectPublicKeyInfo, issuerUID [7], subjectUID [8], extensions [9] }
|
|
299
|
+
// SubjectPublicKeyInfo, issuerUID [7], subjectUID [8], extensions [9] }, where every
|
|
300
300
|
// field IMPLICIT and OPTIONAL, so the whole body is one ascending [0..9] trailing
|
|
301
301
|
// run. issuer/subject Name and the [4] validity times are the CHOICE exceptions.
|
|
302
302
|
var CERT_TEMPLATE = schema.seq([
|
|
@@ -323,9 +323,9 @@ var CERT_TEMPLATE = schema.seq([
|
|
|
323
323
|
// existing certificate -- serialNumber and issuer present -- inside a CMP
|
|
324
324
|
// RevDetails (RFC 9810 sec. 5.3.9). Every field is surfaced so both the request
|
|
325
325
|
// omission check and the revocation consumer read them off one result.
|
|
326
|
-
// The
|
|
326
|
+
// The single profile override: RFC 9810 Appendix D.6 profiles the CMP
|
|
327
327
|
// cross-certification (ccr) template as "version v1 or v3", so the caller
|
|
328
|
-
// opts in to v1 (0) via ctx.allowV1Version
|
|
328
|
+
// opts in to v1 (0) via ctx.allowV1Version, the same base-section-vs-
|
|
329
329
|
// profile-appendix override mechanism as the signingAlg relaxation below.
|
|
330
330
|
if (f.version.present && f.version.value !== 2n &&
|
|
331
331
|
!(ctx.allowV1Version && f.version.value === 0n)) {
|
|
@@ -365,7 +365,7 @@ var CERT_REQUEST = schema.seq([
|
|
|
365
365
|
// hazard), so a request template that sets any of them is rejected
|
|
366
366
|
// fail-closed. The check lives HERE, not in CERT_TEMPLATE, because the same
|
|
367
367
|
// structure legitimately carries serialNumber/issuer in a CMP RevDetails.
|
|
368
|
-
// A cross-certification request (ccr) is the
|
|
368
|
+
// A cross-certification request (ccr) is the one exception: RFC 9810 Appendix
|
|
369
369
|
// D.6 profiles its template with signingAlg PRESENT (the requesting CA states
|
|
370
370
|
// the algorithm it wants the cross-certificate signed with), so the caller
|
|
371
371
|
// relaxes that single field via ctx.allowSigningAlg; serialNumber and the UID
|
|
@@ -406,7 +406,7 @@ var CERT_REQ_MSG = schema.seq([
|
|
|
406
406
|
// to sign / MAC over, and a verifier would be bound to the wrong identity/key.
|
|
407
407
|
var complete = certReq.certTemplate.subject !== null && certReq.certTemplate.publicKey !== null;
|
|
408
408
|
// RFC 4211 sec. 4.1 -- for a signature POP, poposkInput's presence is fixed by the
|
|
409
|
-
// CertTemplate: it MUST be omitted when the template carries
|
|
409
|
+
// CertTemplate: it MUST be omitted when the template carries both subject and
|
|
410
410
|
// publicKey (the signature is then over the DER of the CertRequest), and MUST be
|
|
411
411
|
// present otherwise.
|
|
412
412
|
if (popo && popo.type === "signature") {
|
|
@@ -438,7 +438,7 @@ var CERT_REQ_MSG = schema.seq([
|
|
|
438
438
|
},
|
|
439
439
|
});
|
|
440
440
|
|
|
441
|
-
// CertReqMessages ::= SEQUENCE SIZE(1..MAX) OF CertReqMsg (sec. 3)
|
|
441
|
+
// CertReqMessages ::= SEQUENCE SIZE(1..MAX) OF CertReqMsg (sec. 3), where an empty sequence
|
|
442
442
|
// is malformed. The top-level reject code predates the
|
|
443
443
|
// `<prefix>/not-a-<structure>` convention and stays frozen on the public
|
|
444
444
|
// error-code surface.
|
|
@@ -461,16 +461,16 @@ var CERT_REQ_MESSAGES = schema.seqOf(CERT_REQ_MSG, {
|
|
|
461
461
|
* `{ messages: [ { certReq, popo, regInfo } ] }`. Each `certReq` is
|
|
462
462
|
* `{ certReqId, certReqIdHex, certTemplate, controls, certReqBytes }`, and
|
|
463
463
|
* `certTemplate` carries the requestable certificate fields (`version`, `issuer`,
|
|
464
|
-
* `validity`, `subject`, `publicKey`, `extensions
|
|
464
|
+
* `validity`, `subject`, `publicKey`, `extensions`, each `null` when absent). The
|
|
465
465
|
* CA-assigned / deprecated fields RFC 4211 sec. 5 requires a request to omit
|
|
466
466
|
* (`serialNumber`, `signingAlg`, `issuerUID`, `subjectUID`) are rejected, not
|
|
467
467
|
* surfaced. `popo` is
|
|
468
468
|
* `null`, `{ type: "raVerified" }`, `{ type: "signature", poposkInput,
|
|
469
469
|
* algorithmIdentifier, signature }`, or `{ type: "keyEncipherment" |
|
|
470
470
|
* "keyAgreement", method, bytes }` (where `method` is the POPOPrivKey alternative --
|
|
471
|
-
* `thisMessage` / `subsequentMessage` / `dhMAC` / `agreeMAC` / `encryptedKey
|
|
471
|
+
* `thisMessage` / `subsequentMessage` / `dhMAC` / `agreeMAC` / `encryptedKey`, each
|
|
472
472
|
* structurally validated, the `encryptedKey` EnvelopedData included). When present, `poposkInput` is
|
|
473
|
-
* `{ bytes, signedBytes, publicKey }
|
|
473
|
+
* `{ bytes, signedBytes, publicKey }`, where `bytes` is the raw wire `[0]` TLV,
|
|
474
474
|
* `signedBytes` is the `POPOSigningKeyInput` re-tagged to the `SEQUENCE` DER the
|
|
475
475
|
* signature actually covers, and `publicKey` is the canonical
|
|
476
476
|
* `SubjectPublicKeyInfo` DER the RFC 4211 sec. 4.1 template-match check compares
|
|
@@ -503,7 +503,7 @@ var parse = pkix.makeParser({
|
|
|
503
503
|
* @related pki.schema.crmf.parse
|
|
504
504
|
*
|
|
505
505
|
* Extract the DER bytes from a PEM block (RFC 4211 registers no RFC 7468 label, so
|
|
506
|
-
* the first block is taken unless `label` is given
|
|
506
|
+
* the first block is taken unless `label` is given; CRMF rides inside CMP / EST as
|
|
507
507
|
* DER in practice). Throws `PemError` on a missing envelope or a non-base64 body.
|
|
508
508
|
*
|
|
509
509
|
* @example
|
package/lib/schema-csr.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* certificate/CRL guards.
|
|
26
26
|
*
|
|
27
27
|
* @card
|
|
28
|
-
* Parse DER / PEM PKCS#10 CSRs into structured, validated fields
|
|
28
|
+
* Parse DER / PEM PKCS#10 CSRs into structured, validated fields: subject DN,
|
|
29
29
|
* public key, requested attributes, signature, fail-closed.
|
|
30
30
|
*/
|
|
31
31
|
|
|
@@ -45,14 +45,14 @@ var NS = pkix.makeNS("csr", CsrError, oid);
|
|
|
45
45
|
var NAME = pkix.name(NS);
|
|
46
46
|
var SPKI = pkix.spki(NS);
|
|
47
47
|
|
|
48
|
-
// CertificationRequestInfo version ::= INTEGER { v1(0) } (RFC 2986 sec. 4.1). A
|
|
49
|
-
// mandatory INTEGER whose
|
|
48
|
+
// CertificationRequestInfo version ::= INTEGER { v1(0) } (RFC 2986 sec. 4.1). A bare
|
|
49
|
+
// mandatory INTEGER whose one legal value is 0, the inverse of the cert/CRL
|
|
50
50
|
// readers, which reject 0 as the DER-forbidden default. Surface v1 as 1. A
|
|
51
51
|
// cert-shaped [0] EXPLICIT wrapper at this position makes asn1.read.integer throw
|
|
52
52
|
// asn1/* (fail-closed).
|
|
53
53
|
var CSR_VERSION = pkix.versionReader(NS, { "0": 1 });
|
|
54
54
|
|
|
55
|
-
// Attribute ::= SEQUENCE { type OID, values SET OF ANY }
|
|
55
|
+
// Attribute ::= SEQUENCE { type OID, values SET OF ANY }, the shared pkix factory
|
|
56
56
|
// under the csr NS (values raw DER, SET SIZE(1..MAX), no uniqueness). The same
|
|
57
57
|
// factory serves the PKCS#8 private-key attributes.
|
|
58
58
|
var ATTRIBUTE = pkix.attribute(NS);
|
|
@@ -76,8 +76,8 @@ var DIRECTORY_STRING_TAGS = [
|
|
|
76
76
|
var RECOGNIZED_ATTRIBUTE_VALUE = {};
|
|
77
77
|
RECOGNIZED_ATTRIBUTE_VALUE[oid.byName("extensionRequest")] = function (node, ctx) {
|
|
78
78
|
// Surface the requested extensions decoded (same {oid,name,critical,value}
|
|
79
|
-
// shape as a certificate's extensions) so a CA pipeline
|
|
80
|
-
// re-enroll guard
|
|
79
|
+
// shape as a certificate's extensions) so a CA pipeline, and the EST
|
|
80
|
+
// re-enroll guard, read them off the attribute instead of re-walking the
|
|
81
81
|
// raw Extensions bytes. Returned as an enrichment merged onto the attribute.
|
|
82
82
|
try { return { extensions: schema.walk(EXTENSIONS, node, ctx).result }; }
|
|
83
83
|
catch (e) {
|
|
@@ -101,7 +101,7 @@ RECOGNIZED_ATTRIBUTE_VALUE[oid.byName("challengePassword")] = function (node, ct
|
|
|
101
101
|
// subjectPKInfo SubjectPublicKeyInfo, attributes [0] IMPLICIT SET OF Attribute }.
|
|
102
102
|
// attributes is a REQUIRED field (a CRI omitting [0] is a missing-required-field
|
|
103
103
|
// fault, not silently accepted) modeled as an IMPLICIT [0] SET OF with min:0 (an
|
|
104
|
-
// empty attributes SET is legal). subject MAY be empty
|
|
104
|
+
// empty attributes SET is legal). subject MAY be empty, so there is no non-empty-DN guard.
|
|
105
105
|
var CERTIFICATION_REQUEST_INFO = schema.seq([
|
|
106
106
|
schema.field("version", CSR_VERSION),
|
|
107
107
|
schema.field("subject", NAME),
|
|
@@ -134,11 +134,11 @@ var CERTIFICATION_REQUEST_INFO = schema.seq([
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
// CertificationRequest ::= SEQUENCE { certificationRequestInfo, signatureAlgorithm,
|
|
137
|
-
// signature BIT STRING }
|
|
138
|
-
// cert/CRL builders
|
|
139
|
-
// (the CRI has no inner signature AlgorithmIdentifier) and the
|
|
140
|
-
// guard (a CSR subject MAY be empty). The omission is
|
|
141
|
-
// simply never references the agreement bytes.
|
|
137
|
+
// signature BIT STRING }, the shared SIGNED envelope. It diverges from the
|
|
138
|
+
// cert/CRL builders by omitting the outer-vs-inner signatureAlgorithm agreement
|
|
139
|
+
// check (the CRI has no inner signature AlgorithmIdentifier) and the
|
|
140
|
+
// non-empty-subject guard (a CSR subject MAY be empty). The omission is
|
|
141
|
+
// structural: this build simply never references the agreement bytes.
|
|
142
142
|
var CERTIFICATION_REQUEST = pkix.signedEnvelope(NS, CERTIFICATION_REQUEST_INFO, {
|
|
143
143
|
code: "csr/not-a-certification-request", what: "CertificationRequest",
|
|
144
144
|
build: function (e) {
|