@blamejs/pki 0.5.6 → 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 +419 -378
- package/MIGRATING.md +65 -0
- package/README.md +12 -12
- package/lib/acme.js +31 -31
- package/lib/asn1-der.js +10 -10
- package/lib/attrcert-sign.js +24 -20
- 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 +104 -104
- package/lib/cmc-verify.js +108 -32
- package/lib/cmp-build.js +31 -26
- package/lib/cmp-session.js +74 -72
- package/lib/cmp-verify.js +72 -58
- package/lib/cms-compress.js +8 -9
- package/lib/cms-decrypt.js +92 -76
- package/lib/cms-encrypt.js +33 -34
- package/lib/cms-sign.js +100 -54
- package/lib/cms-verify.js +141 -82
- package/lib/composite-sig.js +13 -13
- package/lib/constants.js +4 -4
- package/lib/crl-sign.js +31 -25
- package/lib/crl-verify.js +7 -6
- package/lib/crmf-sign.js +19 -15
- package/lib/csr-sign.js +13 -9
- package/lib/ct.js +37 -37
- package/lib/edwards-point.js +7 -7
- package/lib/est.js +103 -59
- 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 +378 -15
- 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 +144 -75
- 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 +32 -20
- package/lib/oid.js +29 -29
- package/lib/path-validate.js +114 -113
- package/lib/pbes2.js +16 -16
- package/lib/pkcs12-build.js +71 -56
- package/lib/pki-build.js +23 -22
- 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 +169 -36
- 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 +19 -16
- 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 +22 -18
- 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 +21 -20
- package/lib/webauthn-mds.js +67 -67
- package/lib/webauthn.js +34 -34
- package/lib/webcrypto.js +15 -15
- package/lib/x509-sign.js +24 -15
- package/package.json +3 -2
- package/sbom.cdx.json +6 -6
package/lib/schema-cmc.js
CHANGED
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
* the CMS layer, dispatches on that content type, and decodes the message.
|
|
17
17
|
*
|
|
18
18
|
* Every body part carries a `BodyPartID`, and the identifier MUST be unique
|
|
19
|
-
* across the
|
|
19
|
+
* across the whole message, not merely within one sequence; 0 is reserved as
|
|
20
20
|
* the reference to the enclosing PKIData and is never an element's own
|
|
21
|
-
* identity. Controls are surfaced as an ordered list with their values
|
|
22
|
-
* unrecognized control is
|
|
21
|
+
* identity. Controls are surfaced as an ordered list with their values raw: an
|
|
22
|
+
* unrecognized control is data, not a fault, because the rule that a server
|
|
23
23
|
* fails an unrecognized control binds the server, not the client reading a
|
|
24
24
|
* response. The status controls (`CMCStatusInfo` v1 and `CMCStatusInfoV2`) are
|
|
25
25
|
* collected in wire order -- a response may carry several verdicts, and the
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
*
|
|
32
32
|
* @spec RFC 5272, RFC 5273, RFC 5274, RFC 6402
|
|
33
33
|
* @card
|
|
34
|
-
* Decode RFC 5272 CMC Full PKI Requests and Responses inside CMS
|
|
34
|
+
* Decode RFC 5272 CMC Full PKI Requests and Responses inside CMS: controls
|
|
35
35
|
* with raw values, tagged requests (PKCS#10 / CRMF / other), ordered status
|
|
36
36
|
* verdicts with the RFC 6402 two-module OtherStatusInfo ambiguity resolved,
|
|
37
37
|
* whole-message body-part identity, raw reqSequence bytes, fail-closed.
|
|
@@ -61,20 +61,20 @@ var OID_STATUS_INFO_V2 = O("id-cmc-statusInfoV2");
|
|
|
61
61
|
var OID_RESPONSE_BODY = O("id-cmc-responseBody");
|
|
62
62
|
var OID_RA_IDENTITY_WITNESS = O("id-cmc-raIdentityWitness");
|
|
63
63
|
|
|
64
|
-
// BodyPartID ::= INTEGER (0..4294967295)
|
|
64
|
+
// BodyPartID ::= INTEGER (0..4294967295), RFC 5272 sec. 3.2.2.
|
|
65
65
|
var BODY_PART_MAX = 4294967295n;
|
|
66
66
|
|
|
67
67
|
// CMCStatus ::= INTEGER { success(0), failed(2), pending(3), noSupport(4),
|
|
68
|
-
// confirmRequired(5), popRequired(6), partial(7) }
|
|
69
|
-
// 1 is
|
|
70
|
-
//
|
|
68
|
+
// confirmRequired(5), popRequired(6), partial(7) }, RFC 5272 sec. 6.1.3.
|
|
69
|
+
// 1 is reserved: it is neither a success nor a failure, so it is refused and
|
|
70
|
+
// never folded into either.
|
|
71
71
|
var STATUS_BY_VALUE = {
|
|
72
72
|
0: "success", 2: "failed", 3: "pending", 4: "noSupport",
|
|
73
73
|
5: "confirmRequired", 6: "popRequired", 7: "partial",
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
-
// CMCFailInfo ::= INTEGER { ... }
|
|
77
|
-
// are surfaced numerically
|
|
76
|
+
// CMCFailInfo ::= INTEGER { ... }, RFC 5272 sec. 6.1.4. Values outside this set
|
|
77
|
+
// are surfaced numerically and never refused: sec. 6.1.4 reserves 1000..1999
|
|
78
78
|
// for private extended reasons, so an unknown number is legal data.
|
|
79
79
|
var FAIL_INFO_BY_VALUE = {
|
|
80
80
|
0: "badAlg", 1: "badMessageCheck", 2: "badRequest", 3: "badTime", 4: "badCertId",
|
|
@@ -83,10 +83,10 @@ var FAIL_INFO_BY_VALUE = {
|
|
|
83
83
|
13: "authDataFail",
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
-
// The CMS content types a Full PKI Request / Response may ride in
|
|
86
|
+
// The CMS content types a Full PKI Request / Response may ride in, per RFC 5272
|
|
87
87
|
// sec. 3.2 ("encapsulated in either a SignedData or an AuthenticatedData") and
|
|
88
88
|
// sec. 4.2, which says the same of the response.
|
|
89
|
-
// The names are the OID
|
|
89
|
+
// The names are the OID registry's, read off `oid.name(...)` and never taken from the
|
|
90
90
|
// spec's prose: RFC 5652 calls the type AuthenticatedData, the registry row is
|
|
91
91
|
// `authData`, and hard-coding the prose spelling would reject every conforming
|
|
92
92
|
// AuthenticatedData response while looking correct.
|
|
@@ -124,12 +124,12 @@ function readBodyPartIdentity(node) {
|
|
|
124
124
|
// ---- controls --------------------------------------------------------
|
|
125
125
|
|
|
126
126
|
// TaggedAttribute ::= SEQUENCE { bodyPartID BodyPartID, attrType OBJECT
|
|
127
|
-
// IDENTIFIER, attrValues SET OF AttributeValue }
|
|
128
|
-
// attrValues are surfaced
|
|
129
|
-
// PD7's "fail on an unrecognized control" binds the
|
|
130
|
-
// a response, so an unknown value is data to hand on
|
|
127
|
+
// IDENTIFIER, attrValues SET OF AttributeValue }, RFC 5272 sec. 3.2.1.1.
|
|
128
|
+
// attrValues are surfaced raw: this decoder does not know every control, and
|
|
129
|
+
// PD7's "fail on an unrecognized control" binds the server, not a client reading
|
|
130
|
+
// a response, so an unknown value is data to hand on, never a fault.
|
|
131
131
|
//
|
|
132
|
-
// The placement rule is
|
|
132
|
+
// The placement rule is not checked here: it depends on which message the control
|
|
133
133
|
// sits in, which the enclosing walk knows and this shape does not.
|
|
134
134
|
var TAGGED_ATTRIBUTE = schema.seq([
|
|
135
135
|
schema.field("bodyPartID", schema.decode(readBodyPartIdentity)),
|
|
@@ -163,8 +163,8 @@ function assertControlPlacement(control, where) {
|
|
|
163
163
|
// ---- status controls -------------------------------------------------
|
|
164
164
|
|
|
165
165
|
// BodyPartReference ::= CHOICE { bodyPartID BodyPartID, bodyPartPath BodyPartPath }
|
|
166
|
-
//
|
|
167
|
-
// SEQUENCE SIZE(1..MAX), so an empty one is malformed
|
|
166
|
+
// (an INTEGER and a SEQUENCE, so the arms are tag-disjoint). BodyPartPath is
|
|
167
|
+
// SEQUENCE SIZE(1..MAX), so an empty one is malformed, never "no path".
|
|
168
168
|
function parseBodyPartReference(node) {
|
|
169
169
|
if (node.tagClass === "universal" && node.tagNumber === TAGS.INTEGER) {
|
|
170
170
|
return { bodyPartID: readBodyPartId(node), bodyPartPath: null };
|
|
@@ -189,7 +189,7 @@ function parseBodyPartReference(node) {
|
|
|
189
189
|
*
|
|
190
190
|
* universal INTEGER -> failInfo
|
|
191
191
|
* context [1] -> extendedFailInfo (2008 module)
|
|
192
|
-
* universal SEQUENCE -> disambiguate on the
|
|
192
|
+
* universal SEQUENCE -> disambiguate on the first child's tag:
|
|
193
193
|
* OCTET STRING -> pendInfo (PendInfo ::= SEQUENCE
|
|
194
194
|
* { pendToken OCTET STRING, pendTime GeneralizedTime })
|
|
195
195
|
* OBJECT IDENTIFIER -> extendedFailInfo
|
|
@@ -201,8 +201,8 @@ function parseBodyPartReference(node) {
|
|
|
201
201
|
*/
|
|
202
202
|
function parseOtherStatusInfo(node, version) {
|
|
203
203
|
// The extendedFailInfo arm belongs to CMCStatusInfoV2 alone. RFC 5272 sec. 6.1.1
|
|
204
|
-
// gives the v1 otherInfo exactly two arms
|
|
205
|
-
// pendInfo PendInfo }
|
|
204
|
+
// gives the v1 otherInfo exactly two arms, `CHOICE { failInfo CMCFailInfo,
|
|
205
|
+
// pendInfo PendInfo }`, so accepting the third here would read a failure
|
|
206
206
|
// verdict out of a v1 control that cannot legally express one, and the
|
|
207
207
|
// disambiguation below would be answering a question v1 never asks.
|
|
208
208
|
var extendedAllowed = version !== 1;
|
|
@@ -221,7 +221,7 @@ function parseOtherStatusInfo(node, version) {
|
|
|
221
221
|
// unwrap. Both RFC 6402 modules are IMPLICIT: A.1 (rfc6402.txt:805) and A.2
|
|
222
222
|
// (rfc6402.txt:1314) each open `DEFINITIONS IMPLICIT TAGS ::=`, and the
|
|
223
223
|
// EXPLICIT-override rule does not apply here because the tagged type is a
|
|
224
|
-
// plain SEQUENCE
|
|
224
|
+
// plain SEQUENCE, not a CHOICE or an open type. Reading this arm as
|
|
225
225
|
// EXPLICIT would reject every conforming 2008-module response.
|
|
226
226
|
return { failInfo: null, failInfoName: null, pendInfo: null, extendedFailInfo: readExtendedFailInfo(node.children, "[1] extendedFailInfo") };
|
|
227
227
|
}
|
|
@@ -230,8 +230,8 @@ function parseOtherStatusInfo(node, version) {
|
|
|
230
230
|
if (!first) throw E("cmc/ambiguous-status-info", "an empty OtherStatusInfo SEQUENCE matches neither pendInfo nor extendedFailInfo");
|
|
231
231
|
if (first.tagClass === "universal" && first.tagNumber === TAGS.OCTET_STRING) {
|
|
232
232
|
if (node.children.length !== 2) throw E("cmc/bad-status-info", "PendInfo must be { pendToken, pendTime }");
|
|
233
|
-
// pendTime is a GeneralizedTime specifically, so assert the tag
|
|
234
|
-
// accept either time form
|
|
233
|
+
// pendTime is a GeneralizedTime specifically, so assert the tag and do not
|
|
234
|
+
// accept either time form: a UTCTime here is a different encoding, and
|
|
235
235
|
// reading it anyway would let a two-digit year through the retry clock.
|
|
236
236
|
var timeNode = node.children[1];
|
|
237
237
|
if (timeNode.tagClass !== "universal" || timeNode.tagNumber !== TAGS.GENERALIZED_TIME) {
|
|
@@ -256,7 +256,7 @@ function parseOtherStatusInfo(node, version) {
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
// ExtendedFailInfo ::= SEQUENCE { failInfoOID OBJECT IDENTIFIER, failInfoValue ANY }
|
|
259
|
-
// The OID is surfaced raw and
|
|
259
|
+
// The OID is surfaced raw and never mapped onto a CMCFailInfo: RFC 5272 sec.
|
|
260
260
|
// 6.1.1 makes internalCAError only a MAY for an unrecognized extended error, and
|
|
261
261
|
// synthesizing it would report a reason the responder did not send.
|
|
262
262
|
function readExtendedFailInfo(children, what) {
|
|
@@ -285,7 +285,7 @@ function parseStatusInfo(valueDer, version, bodyPartID) {
|
|
|
285
285
|
var statusName = STATUS_BY_VALUE[statusNum];
|
|
286
286
|
if (!statusName) {
|
|
287
287
|
// 1 is reserved and everything above 7 is unassigned. An unknown status is
|
|
288
|
-
// not a verdict in either direction, so it is refused
|
|
288
|
+
// not a verdict in either direction, so it is refused and never mapped.
|
|
289
289
|
throw E("cmc/bad-status", "unknown CMCStatus " + statusNum + " (1 is reserved; RFC 5272 sec. 6.1.3)");
|
|
290
290
|
}
|
|
291
291
|
|
|
@@ -337,7 +337,7 @@ function parseStatusInfo(valueDer, version, bodyPartID) {
|
|
|
337
337
|
// The cross-field coupling (RFC 5272 sec. 6.1.1 / 6.1.2): failInfo and
|
|
338
338
|
// extendedFailInfo are "present only if cMCStatus contains the value failed",
|
|
339
339
|
// and pendInfo "MUST be populated for a cMCStatus value of pending or partial".
|
|
340
|
-
// A message whose status and detail disagree is refused
|
|
340
|
+
// A message whose status and detail disagree is refused, never reconciled:
|
|
341
341
|
// the two halves would otherwise let a caller read a verdict neither field states.
|
|
342
342
|
function _assertStatusCoherent(s) {
|
|
343
343
|
var isFailed = s.status === "failed";
|
|
@@ -363,8 +363,8 @@ function _assertStatusCoherent(s) {
|
|
|
363
363
|
// orm [2] SEQUENCE { bodyPartID, requestMessageType,
|
|
364
364
|
// requestMessageValue } }
|
|
365
365
|
// The CMC module is IMPLICIT TAGS, so each context tag REPLACES the arm's own
|
|
366
|
-
// universal tag
|
|
367
|
-
// fields,
|
|
366
|
+
// universal tag: the [0] node's direct children are the TaggedCertificationRequest
|
|
367
|
+
// fields, not a nested SEQUENCE. An EXPLICIT encoding is therefore a different
|
|
368
368
|
// (and invalid) message, which is what C5 pins.
|
|
369
369
|
// An IMPLICIT context-tagged constructed node re-encoded under the universal
|
|
370
370
|
// SEQUENCE tag it stands in for. IMPLICIT tagging REPLACES the tag and leaves the
|
|
@@ -402,8 +402,8 @@ function parseTaggedRequest(node) {
|
|
|
402
402
|
// bodyPartID field of the other objects", and sec. 6.5.2 repeats it
|
|
403
403
|
// ("... either the bodyPartID of a TaggedCertificationRequest (PKCS #10) or
|
|
404
404
|
// the certReqId of the CertRequest within a CertReqMsg (CRMF)"). So the
|
|
405
|
-
// certReqId is read here and carried into the
|
|
406
|
-
// check every other body part is bound by
|
|
405
|
+
// certReqId is read here and carried into the same whole-message uniqueness
|
|
406
|
+
// check every other body part is bound by; returning null would silently
|
|
407
407
|
// exempt this arm from it.
|
|
408
408
|
//
|
|
409
409
|
// CertReqMsg ::= SEQUENCE { certReq CertRequest, popo OPTIONAL, regInfo OPTIONAL }
|
|
@@ -445,7 +445,7 @@ function parseTaggedRequest(node) {
|
|
|
445
445
|
// ---- body-part identity ----------------------------------------------
|
|
446
446
|
|
|
447
447
|
// RFC 5272 sec. 3.2.2: "The body part identifier MUST be unique within a single
|
|
448
|
-
// PKIData or PKIResponse." The scope is the
|
|
448
|
+
// PKIData or PKIResponse." The scope is the whole message: a checker that ran
|
|
449
449
|
// per-sequence would accept a control and a request that collide, which is
|
|
450
450
|
// exactly the shape B2 pins.
|
|
451
451
|
function assertUniqueBodyPartIds(ids) {
|
|
@@ -463,8 +463,8 @@ function assertUniqueBodyPartIds(ids) {
|
|
|
463
463
|
// ---- the two message bodies ------------------------------------------
|
|
464
464
|
|
|
465
465
|
// The two message bodies as engine schemas. Declaring them means the engine owns
|
|
466
|
-
// the positional reads, the field arity and the SET-OF ordering
|
|
467
|
-
// a hand-rolled cursor keeps reintroducing
|
|
466
|
+
// the positional reads, the field arity and the SET-OF ordering (the bug class
|
|
467
|
+
// a hand-rolled cursor keeps reintroducing), while the rules that genuinely span
|
|
468
468
|
// fields (whole-message body-part uniqueness, control placement, the status
|
|
469
469
|
// cross-checks) run in the build / the caller, which is where they can see
|
|
470
470
|
// everything they need.
|
|
@@ -486,9 +486,9 @@ var REQ_SEQUENCE = schema.seqOf(schema.decode(parseTaggedRequest),
|
|
|
486
486
|
|
|
487
487
|
// TaggedContentInfo ::= SEQUENCE { bodyPartID, contentInfo } (RFC 5272 sec.
|
|
488
488
|
// 3.2.1.3) and OtherMsg ::= SEQUENCE { bodyPartID, otherMsgType, otherMsgValue }
|
|
489
|
-
// (sec. 3.2.1.4). Neither payload is interpreted here
|
|
490
|
-
// CMS object the caller decodes, the OtherMsg value is by definition arbitrary
|
|
491
|
-
// but
|
|
489
|
+
// (sec. 3.2.1.4). Neither payload is interpreted here (the ContentInfo is a
|
|
490
|
+
// CMS object the caller decodes, the OtherMsg value is by definition arbitrary),
|
|
491
|
+
// but both carry a body part identity, and sec. 3.2.2's uniqueness rule covers
|
|
492
492
|
// "each element of a PKIData or PKIResponse". Decoding just the identifier is
|
|
493
493
|
// what lets that rule cover them without this layer claiming to understand the
|
|
494
494
|
// payload.
|
|
@@ -498,8 +498,8 @@ var TAGGED_CONTENT_INFO = schema.seq([
|
|
|
498
498
|
], {
|
|
499
499
|
assert: "sequence", arity: { exact: 2 }, code: "cmc/bad-cms-sequence", what: "a TaggedContentInfo",
|
|
500
500
|
build: function (m) {
|
|
501
|
-
// The payload stays
|
|
502
|
-
// not know
|
|
501
|
+
// The payload stays raw, since this layer does not decode a content type it may
|
|
502
|
+
// not know, but it must still be a ContentInfo: SEQUENCE { contentType
|
|
503
503
|
// OBJECT IDENTIFIER, content [0] EXPLICIT ANY OPTIONAL } (RFC 5652 sec. 3).
|
|
504
504
|
// Surfacing an INTEGER, or anything else that cannot be one, as a content
|
|
505
505
|
// info would hand the caller a "CMS message" no CMS reader will accept, which
|
|
@@ -511,9 +511,9 @@ var TAGGED_CONTENT_INFO = schema.seq([
|
|
|
511
511
|
throw E("cmc/bad-cms-sequence",
|
|
512
512
|
"a TaggedContentInfo carries a CMS ContentInfo, which leads with its contentType OBJECT IDENTIFIER (RFC 5652 sec. 3)");
|
|
513
513
|
}
|
|
514
|
-
//
|
|
515
|
-
// TLV can carry content no OID reader will accept
|
|
516
|
-
// padded to a non-minimal encoding
|
|
514
|
+
// Read the contentType; do not merely inspect its tag. An OBJECT IDENTIFIER
|
|
515
|
+
// TLV can carry content no OID reader will accept (empty, or a subidentifier
|
|
516
|
+
// padded to a non-minimal encoding) and a tag check waves both through. The
|
|
517
517
|
// toolkit's own CMS parser refuses them, so accepting them here would let the
|
|
518
518
|
// builder sign a "CMS message" pki.schema.cms.parse will not read back. The
|
|
519
519
|
// VALUE is still not interpreted: this layer only establishes that there is a
|
|
@@ -523,10 +523,10 @@ var TAGGED_CONTENT_INFO = schema.seq([
|
|
|
523
523
|
throw E("cmc/bad-cms-sequence",
|
|
524
524
|
"a TaggedContentInfo carries a CMS ContentInfo, whose contentType must be a readable OBJECT IDENTIFIER (RFC 5652 sec. 3)", e);
|
|
525
525
|
}
|
|
526
|
-
// The
|
|
526
|
+
// The whole envelope, not just its first field: content is `[0] EXPLICIT ANY
|
|
527
527
|
// DEFINED BY contentType OPTIONAL`, so at most one more element and it is that
|
|
528
528
|
// tag. An INTEGER sitting where the content belongs, or a third field, is not
|
|
529
|
-
// a ContentInfo any CMS reader will take
|
|
529
|
+
// a ContentInfo any CMS reader will take, and stopping at the leading OID
|
|
530
530
|
// would surface it as one.
|
|
531
531
|
if (kids.length > 2) {
|
|
532
532
|
throw E("cmc/bad-cms-sequence",
|
|
@@ -539,9 +539,9 @@ var TAGGED_CONTENT_INFO = schema.seq([
|
|
|
539
539
|
throw E("cmc/bad-cms-sequence",
|
|
540
540
|
"a CMS ContentInfo's second field is the [0] EXPLICIT content (RFC 5652 sec. 3)");
|
|
541
541
|
}
|
|
542
|
-
// EXPLICIT wraps
|
|
543
|
-
// would let an
|
|
544
|
-
// info
|
|
542
|
+
// EXPLICIT wraps exactly one value (X.690 sec. 8.14). Checking the tag alone
|
|
543
|
+
// would let an empty wrapper, or one holding several values, read as a content
|
|
544
|
+
// info: shapes pki.schema.cms.parse refuses as cms/not-a-content-info, so the
|
|
545
545
|
// builder's readback would pass a message its own CMS reader will not accept.
|
|
546
546
|
if (!content.children || content.children.length !== 1) {
|
|
547
547
|
throw E("cmc/bad-cms-sequence",
|
|
@@ -569,7 +569,7 @@ var OTHER_MSG = schema.seq([
|
|
|
569
569
|
});
|
|
570
570
|
|
|
571
571
|
// PKIData ::= SEQUENCE { controlSequence, reqSequence, cmsSequence,
|
|
572
|
-
// otherMsgSequence }
|
|
572
|
+
// otherMsgSequence }, RFC 5272 sec. 3.2.1. All four are mandatory though each
|
|
573
573
|
// may be empty, so `arity: { exact: 4 }` is the rule, not a convenience.
|
|
574
574
|
var PKI_DATA = schema.seq([
|
|
575
575
|
schema.field("controlSequence", CONTROL_SEQUENCE),
|
|
@@ -591,7 +591,7 @@ var PKI_DATA = schema.seq([
|
|
|
591
591
|
// The raw reqSequence TLV, tag and length included: the Identity Proof V2
|
|
592
592
|
// witness is computed over exactly these bytes "encoded exactly as it
|
|
593
593
|
// appears in the Full PKI Request" (RFC 5272 sec. 6.2.1), so it is taken
|
|
594
|
-
// off the matched
|
|
594
|
+
// off the matched node, never re-encoded from the decoded requests.
|
|
595
595
|
reqSequenceBytes: m.fields.reqSequence.node.bytes,
|
|
596
596
|
};
|
|
597
597
|
},
|
|
@@ -621,7 +621,7 @@ var PKI_RESPONSE = schema.seq([
|
|
|
621
621
|
});
|
|
622
622
|
|
|
623
623
|
// Collect the status controls out of an already-parsed control list, in wire
|
|
624
|
-
// order. Absence is
|
|
624
|
+
// order. Absence is not an error: RFC 5272 sec. 6.1.2 says "If no status exists
|
|
625
625
|
// for a Simple or Full PKI Request, then the value of success is assumed", so a
|
|
626
626
|
// response with no status control is a successful one.
|
|
627
627
|
function _collectStatuses(controls) {
|
|
@@ -648,9 +648,9 @@ function _finishBody(body, where) {
|
|
|
648
648
|
return c.attrType !== OID_STATUS_INFO && c.attrType !== OID_STATUS_INFO_V2 && c.attrName === null;
|
|
649
649
|
});
|
|
650
650
|
// RFC 5272 sec. 3.2.2 scopes uniqueness to "each element of a PKIData or
|
|
651
|
-
// PKIResponse", and
|
|
651
|
+
// PKIResponse", and four element kinds carry an identity. Enumerated here in
|
|
652
652
|
// one place so the rule cannot hold for some of them and quietly lapse for the
|
|
653
|
-
// rest: controls (sec. 3.2.1.1), requests (sec. 3.2.1.2
|
|
653
|
+
// rest: controls (sec. 3.2.1.1), requests (sec. 3.2.1.2, the tcr/orm
|
|
654
654
|
// bodyPartID and the crm arm's certReqId), content infos (sec. 3.2.1.3) and
|
|
655
655
|
// other messages (sec. 3.2.1.4).
|
|
656
656
|
var ids = [];
|
|
@@ -699,10 +699,10 @@ function parsePkiData(input) {
|
|
|
699
699
|
* @spec RFC 5272 sec. 4.2.1
|
|
700
700
|
* @related pki.schema.cmc.parse, pki.schema.cmc.parsePkiData
|
|
701
701
|
*
|
|
702
|
-
* Decode a bare `PKIResponse` body. A PKIResponse has
|
|
703
|
-
* no `reqSequence`
|
|
702
|
+
* Decode a bare `PKIResponse` body. A PKIResponse has three sequences (there is
|
|
703
|
+
* no `reqSequence`) and all three are mandatory though each may be empty. The
|
|
704
704
|
* status controls are collected in wire order; a response may legitimately carry
|
|
705
|
-
* several, and carrying
|
|
705
|
+
* several, and carrying none means success (RFC 5272 sec. 6.1.2).
|
|
706
706
|
*
|
|
707
707
|
* @example
|
|
708
708
|
* var b = pki.asn1.build;
|
|
@@ -730,9 +730,9 @@ function parsePkiResponse(input) {
|
|
|
730
730
|
* is DER, a PEM `CMS` block, or an already-parsed `pki.schema.cms` object. The CMS
|
|
731
731
|
* layer is peeled, the encapsulated content type selects the body
|
|
732
732
|
* (`id-cct-PKIData` -> a request, `id-cct-PKIResponse` -> a response), and any
|
|
733
|
-
* other content type is refused as not-CMC
|
|
733
|
+
* other content type is refused as not-CMC, never guessed at.
|
|
734
734
|
*
|
|
735
|
-
* The parsed CMS is returned on `cms` so a caller can verify the signature
|
|
735
|
+
* The parsed CMS is returned on `cms` so a caller can verify the signature; this
|
|
736
736
|
* decoder never does: reading a message and trusting it are separate steps, and
|
|
737
737
|
* RFC 5272 sec. 3.2.1.3.4 makes the signature check the caller's obligation.
|
|
738
738
|
*
|
package/lib/schema-cmp.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
*
|
|
11
11
|
* @intro
|
|
12
12
|
* Certificate Management Protocol handling per RFC 9810 (which obsoletes
|
|
13
|
-
* RFC 4210 and RFC 9480). `parse` decodes a `PKIMessage
|
|
13
|
+
* RFC 4210 and RFC 9480). `parse` decodes a `PKIMessage`, the protected
|
|
14
14
|
* transport envelope CMP enrollment, revocation, confirmation, and support
|
|
15
|
-
* exchanges ride
|
|
15
|
+
* exchanges ride, into its header (version, sender / recipient
|
|
16
16
|
* GeneralNames including the NULL-DN anonymous form, nonces, transaction
|
|
17
17
|
* id, free text, general info), its body, its protection bits, and its
|
|
18
18
|
* extra certificates.
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
* or signature itself. DER-only, fail-closed.
|
|
30
30
|
*
|
|
31
31
|
* @card
|
|
32
|
-
* Parse DER / PEM RFC 9810 CMP PKIMessages
|
|
32
|
+
* Parse DER / PEM RFC 9810 CMP PKIMessages: header, 27-arm body (requests
|
|
33
33
|
* via CRMF, encrypted certs via CMS, the rest structural or raw),
|
|
34
34
|
* protection inputs surfaced byte-exact for external verification,
|
|
35
35
|
* fail-closed.
|
|
@@ -67,7 +67,7 @@ var PKI_FREE_TEXT = schema.seqOf(UTF8_TEXT, {
|
|
|
67
67
|
build: function (m) { return m.items.map(function (it) { return it.value; }); },
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
// PKIStatus ::= INTEGER {0..6} (sec. 5.2.3)
|
|
70
|
+
// PKIStatus ::= INTEGER {0..6} (sec. 5.2.3): a value whitelist, surfaced named.
|
|
71
71
|
var PKI_STATUS_NAMES = ["accepted", "grantedWithMods", "rejection", "waiting",
|
|
72
72
|
"revocationWarning", "revocationNotification", "keyUpdateWarning"];
|
|
73
73
|
var PKI_STATUS = schema.decode(function (n, ctx) {
|
|
@@ -97,17 +97,17 @@ var PKI_FAILURE_INFO = schema.decode(function (n, ctx) {
|
|
|
97
97
|
return { bits: bits, raw: bs };
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
-
// PollRep checkAfter is a delay in seconds
|
|
100
|
+
// PollRep checkAfter is a delay in seconds; a negative one poisons scheduling, and
|
|
101
101
|
// the value surfaces as an exact number or not at all.
|
|
102
102
|
var CHECK_AFTER = schema.decode(function (n, ctx) {
|
|
103
103
|
return guard.range.uint31(asn1.read.integer(n), ctx.E, "cmp/bad-poll-rep", "checkAfter delay in seconds (RFC 9810 sec. 5.3.22)");
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
// A raw certificate / CRL / publication-info element
|
|
106
|
+
// A raw certificate / CRL / publication-info element, surfaced byte-exact for
|
|
107
107
|
// downstream parsing, but structurally a universal SEQUENCE: a CMPCertificate
|
|
108
108
|
// is a Certificate, and CertificateList / PKIPublicationInfo are SEQUENCEs, so
|
|
109
109
|
// a primitive (an INTEGER) or wrong-tag element is not a valid structure and
|
|
110
|
-
// rejects
|
|
110
|
+
// rejects, never handing arbitrary bytes to certificate processing. The
|
|
111
111
|
// `code` names the containing structure (a shared leaf cannot know its context).
|
|
112
112
|
function rawSequence(code) {
|
|
113
113
|
return pkix.rawNonEmptySequence(NS, { code: code, message: "expected a non-empty universal SEQUENCE (Certificate / CertificateList / PKIPublicationInfo)" });
|
|
@@ -140,10 +140,10 @@ var INFO_TYPE_AND_VALUE = schema.seq([
|
|
|
140
140
|
throw ctx.E("cmp/bad-info-value", "a recognized fixed-syntax id-it (implicitConfirm / confirmWaitTime / certProfile) must carry its infoValue (RFC 9810 sec. 5.1.1)");
|
|
141
141
|
}
|
|
142
142
|
if (valueNode !== null) {
|
|
143
|
-
// A recognized id-it value is validated by
|
|
143
|
+
// A recognized id-it value is validated by content, not just tag: run the
|
|
144
144
|
// strict typed reader so a well-tagged but malformed payload (a non-empty
|
|
145
145
|
// NULL, a GeneralizedTime carrying garbage, an invalid-UTF-8 string)
|
|
146
|
-
// rejects
|
|
146
|
+
// rejects and is never surfaced as valid CMP.
|
|
147
147
|
if (t === OID_IMPLICIT_CONFIRM) {
|
|
148
148
|
if (!(valueNode.tagClass === "universal" && valueNode.tagNumber === TAGS.NULL)) {
|
|
149
149
|
throw ctx.E("cmp/bad-info-value", "an implicitConfirm value must be NULL (RFC 9810 sec. 5.1.1.1)");
|
|
@@ -276,14 +276,14 @@ var CERT_STATUS = schema.seq([
|
|
|
276
276
|
},
|
|
277
277
|
});
|
|
278
278
|
|
|
279
|
-
// CertConfirmContent ::= SEQUENCE OF CertStatus
|
|
279
|
+
// CertConfirmContent ::= SEQUENCE OF CertStatus, where an empty sequence is legal
|
|
280
280
|
// (reject-all, sec. 5.3.18), so no SIZE floor.
|
|
281
281
|
var CERT_CONFIRM_CONTENT = schema.seqOf(CERT_STATUS, {
|
|
282
282
|
code: "cmp/bad-cert-status", what: "CertConfirmContent",
|
|
283
283
|
build: function (m) { return m.items.map(function (it) { return it.value.result; }); },
|
|
284
284
|
});
|
|
285
285
|
|
|
286
|
-
// GenMsgContent / GenRepContent ::= SEQUENCE OF InfoTypeAndValue
|
|
286
|
+
// GenMsgContent / GenRepContent ::= SEQUENCE OF InfoTypeAndValue, with no SIZE
|
|
287
287
|
// bound, empty legal (sec. 5.3.19/.20).
|
|
288
288
|
var GEN_MSG_CONTENT = schema.seqOf(INFO_TYPE_AND_VALUE, {
|
|
289
289
|
code: "cmp/bad-info-type-and-value", what: "GenMsgContent",
|
|
@@ -291,7 +291,7 @@ var GEN_MSG_CONTENT = schema.seqOf(INFO_TYPE_AND_VALUE, {
|
|
|
291
291
|
});
|
|
292
292
|
|
|
293
293
|
// RevDetails ::= SEQUENCE { certDetails CertTemplate, crlEntryDetails
|
|
294
|
-
// Extensions OPTIONAL } (sec. 5.3.9)
|
|
294
|
+
// Extensions OPTIONAL } (sec. 5.3.9). The CertTemplate interior (an IMPLICIT
|
|
295
295
|
// TAGS module) is owned by the CRMF parser and walked NS-bound there.
|
|
296
296
|
var CERT_TEMPLATE_LEAF = schema.decode(function (n) { return crmf.walkCertTemplate(n); });
|
|
297
297
|
var REV_DETAILS = schema.seq([
|
|
@@ -353,20 +353,20 @@ var REV_REP_CONTENT = schema.seq([
|
|
|
353
353
|
// EncryptedKey ::= CHOICE { encryptedValue EncryptedValue (deprecated),
|
|
354
354
|
// envelopedData [0] EnvelopedData } (sec. 5.2.2). EncryptedKey is imported from
|
|
355
355
|
// the RFC 4211 CRMF module, which is IMPLICIT TAGS, so `envelopedData [0]` is
|
|
356
|
-
// an IMPLICIT context tag
|
|
357
|
-
// node's children
|
|
356
|
+
// an IMPLICIT context tag replacing the EnvelopedData SEQUENCE tag: the [0]
|
|
357
|
+
// node's children are the EnvelopedData fields, with no inner wrapper. Retag it to
|
|
358
358
|
// the universal SEQUENCE the CMS walker expects, exactly as the sibling CRMF
|
|
359
359
|
// `encryptedKey [4] EnvelopedData` POP arm does (schema-crmf.js). The
|
|
360
|
-
// deprecated encryptedValue arm surfaces
|
|
360
|
+
// deprecated encryptedValue arm surfaces raw and is never field-walked, so nothing
|
|
361
361
|
// dereferences its optional interior (absent algorithm parameters crash
|
|
362
362
|
// consumers that walk it blindly).
|
|
363
363
|
var ENCRYPTED_KEY = schema.choice([
|
|
364
364
|
{ when: { tagClass: "universal", tagNumber: TAGS.SEQUENCE },
|
|
365
365
|
schema: schema.decode(function (n, ctx) {
|
|
366
366
|
// An RFC 4211 sec. 2 EncryptedValue REQUIRES encValue (its final BIT STRING
|
|
367
|
-
// field), so an
|
|
368
|
-
// EncryptedValue
|
|
369
|
-
//
|
|
367
|
+
// field), so an empty SEQUENCE has the right tag but is not any valid
|
|
368
|
+
// EncryptedValue. Require at least one child, so no degenerate structure
|
|
369
|
+
// surfaces as encrypted certificate/key bytes (the same
|
|
370
370
|
// non-empty rule every raw-SEQUENCE surface in this module applies).
|
|
371
371
|
if (!(n.children && n.children.length >= 1)) {
|
|
372
372
|
throw ctx.E("cmp/bad-cert-response", "an EncryptedValue must be a non-empty SEQUENCE (RFC 4211 sec. 2)");
|
|
@@ -391,9 +391,9 @@ var ENCRYPTED_KEY = schema.choice([
|
|
|
391
391
|
if (env.recipientInfos.length !== 1) {
|
|
392
392
|
throw ctx.E("cmp/bad-cert-response", "an EncryptedKey EnvelopedData must contain exactly one RecipientInfo (RFC 9810 sec. 5.2.2)");
|
|
393
393
|
}
|
|
394
|
-
// The ciphertext
|
|
395
|
-
// EnvelopedData with no ciphertext to decrypt
|
|
396
|
-
// permits it)
|
|
394
|
+
// The ciphertext is the encrypted certificate / private key here, so an
|
|
395
|
+
// EnvelopedData with no ciphertext to decrypt, whether absent (detached, CMS
|
|
396
|
+
// permits it) or present-but-empty, must reject (RFC 9810 sec. 5.2.2/sec. 5.3.4;
|
|
397
397
|
// the same rule the CRMF encryptedKey POP arm enforces).
|
|
398
398
|
var ct = env.encryptedContentInfo.encryptedContent;
|
|
399
399
|
if (ct === null || ct.length === 0) {
|
|
@@ -456,9 +456,9 @@ var CERT_RESPONSE = schema.seq([
|
|
|
456
456
|
throw ctx.E("cmp/bad-cert-response",
|
|
457
457
|
"a CertResponse must not carry both failInfo and certifiedKeyPair (RFC 9810 sec. 5.3.4)");
|
|
458
458
|
}
|
|
459
|
-
// A certifiedKeyPair is present
|
|
460
|
-
// accepted (0) or grantedWithMods (1). Any other status
|
|
461
|
-
// the revocation / keyUpdate warnings
|
|
459
|
+
// A certifiedKeyPair is present only when the status grants the certificate:
|
|
460
|
+
// accepted (0) or grantedWithMods (1). Any other status (rejection, waiting,
|
|
461
|
+
// the revocation / keyUpdate warnings) denies or defers the request, so a
|
|
462
462
|
// certificate under it is a malformed response even when no explicit failInfo
|
|
463
463
|
// bit is set (a rejection is commonly signalled by status alone). Keying the
|
|
464
464
|
// rule off failInfo presence alone would let a bare-rejection status ship a
|
|
@@ -477,8 +477,8 @@ var CERT_RESPONSE = schema.seq([
|
|
|
477
477
|
});
|
|
478
478
|
|
|
479
479
|
// CertRepMessage ::= SEQUENCE { caPubs [1] OPTIONAL, response SEQUENCE OF
|
|
480
|
-
// CertResponse } (sec. 5.3.4)
|
|
481
|
-
// x509-version shape). caPubs surface raw: the parser confers
|
|
480
|
+
// CertResponse } (sec. 5.3.4), where the optional precedes the required field (the
|
|
481
|
+
// x509-version shape). caPubs surface raw: the parser confers no trust.
|
|
482
482
|
var CERT_REP_MESSAGE = schema.seq([
|
|
483
483
|
schema.optional("caPubs", schema.seqOf(rawSequence("cmp/bad-cert-rep"), {
|
|
484
484
|
min: 1, code: "cmp/bad-cert-rep", what: "caPubs",
|
|
@@ -502,7 +502,7 @@ var CERT_REP_MESSAGE = schema.seq([
|
|
|
502
502
|
// Certificate OPTIONAL, caCerts [1] SEQUENCE OF Certificate OPTIONAL,
|
|
503
503
|
// keyPairHist [2] SEQUENCE OF CertifiedKeyPair OPTIONAL } (sec. 5.3.8). The
|
|
504
504
|
// keyPairHist CertifiedKeyPairs can carry the same EnvelopedData form as the
|
|
505
|
-
// enrollment responses, so krp is decoded (not surfaced raw)
|
|
505
|
+
// enrollment responses, so krp is decoded (not surfaced raw) and the operator sees
|
|
506
506
|
// the status / key history and the cross-field EnvelopedData version check runs.
|
|
507
507
|
var KEY_REC_REP_CONTENT = schema.seq([
|
|
508
508
|
schema.field("status", PKI_STATUS_INFO),
|
|
@@ -579,8 +579,8 @@ var DECODED_ARMS = {
|
|
|
579
579
|
};
|
|
580
580
|
|
|
581
581
|
// The open-CHOICE dispatch: tag -> arm name; walk where a schema exists;
|
|
582
|
-
// surface every other defined arm raw as { arm, tag, bytes }
|
|
583
|
-
//
|
|
582
|
+
// surface every other defined arm raw as { arm, tag, bytes }, where `decoded` is
|
|
583
|
+
// absent (not null) on raw arms, so its presence discriminates decoded-empty
|
|
584
584
|
// (pkiconf decodes to null) from recognized-undecoded. `nested` [20] is never
|
|
585
585
|
// auto-recursed: a self-nesting tower amplifies per-level walk products, so
|
|
586
586
|
// the operator re-feeds its bytes to parse explicitly.
|
|
@@ -625,7 +625,7 @@ var BODY = schema.decode(function (n, ctx) {
|
|
|
625
625
|
|
|
626
626
|
// ---- the message envelope -----------------------------------------------------
|
|
627
627
|
|
|
628
|
-
// PKIProtection ::= BIT STRING
|
|
628
|
+
// PKIProtection ::= BIT STRING: raw bits for the external verifier. Every
|
|
629
629
|
// RFC 9481 MSG_SIG_ALG / MSG_MAC_ALG output is an octet string, so the BIT
|
|
630
630
|
// STRING must be octet-aligned; with no in-tree CMP verify layer this is
|
|
631
631
|
// enforced at parse (the posture the OCSP signature shares), fail-closed.
|
|
@@ -644,7 +644,7 @@ var PROTECTION = schema.decode(function (n, ctx) {
|
|
|
644
644
|
// span structures: protection presence <=> protectionAlg presence (sec. 5.1.1,
|
|
645
645
|
// both directions), and a certConf hashAlg requires pvno cmp2021(3)
|
|
646
646
|
// (sec. 5.3.18). headerBytes / bodyBytes are the exact wire slices protection is
|
|
647
|
-
// computed over (as DER-SEQUENCE(headerBytes || bodyBytes))
|
|
647
|
+
// computed over (as DER-SEQUENCE(headerBytes || bodyBytes)), surfaced raw and
|
|
648
648
|
// never re-encoded.
|
|
649
649
|
var PKI_MESSAGE = schema.seq([
|
|
650
650
|
schema.field("header", PKI_HEADER),
|
|
@@ -674,7 +674,7 @@ var PKI_MESSAGE = schema.seq([
|
|
|
674
674
|
}
|
|
675
675
|
}
|
|
676
676
|
}
|
|
677
|
-
// RFC 9810 sec. 5.2.2 / sec. 7
|
|
677
|
+
// RFC 9810 sec. 5.2.2 / sec. 7: EnvelopedData is cmp2021(3) syntax, so a response
|
|
678
678
|
// carrying it (an encryptedCert or a privateKey in the envelopedData form)
|
|
679
679
|
// under pvno < 3 is a version mismatch, the same version gate the certConf
|
|
680
680
|
// hashAlg rule applies. The deprecated EncryptedValue form is the pre-2021
|
|
@@ -701,10 +701,10 @@ var PKI_MESSAGE = schema.seq([
|
|
|
701
701
|
// CertRepMessage syntax "with the restriction that no encrypted private key
|
|
702
702
|
// can be sent": cross-certification certifies an existing CA's public key,
|
|
703
703
|
// so there is no key generation and CertifiedKeyPair.privateKey has no
|
|
704
|
-
// meaning. Reject a ccp that carries one
|
|
705
|
-
// material
|
|
704
|
+
// meaning. Reject a ccp that carries one; never surface the key
|
|
705
|
+
// material. The field stays legal in the enrollment responses (ip/cp/kup).
|
|
706
706
|
if (body.arm === "ccp" && body.decoded && body.decoded.response) {
|
|
707
|
-
// RFC 9810 Appendix D.6
|
|
707
|
+
// RFC 9810 Appendix D.6: a one-way cross-certification response carries
|
|
708
708
|
// exactly one CertResponse (multiple cross-certificates go in separate
|
|
709
709
|
// PKIMessages), the mirror of the ccr one-CertReqMsg rule.
|
|
710
710
|
if (body.decoded.response.length !== 1) {
|
|
@@ -717,10 +717,10 @@ var PKI_MESSAGE = schema.seq([
|
|
|
717
717
|
}
|
|
718
718
|
}
|
|
719
719
|
}
|
|
720
|
-
// RFC 9810 sec. 5.3.11
|
|
720
|
+
// RFC 9810 sec. 5.3.11: a cross-certification request (ccr) MUST NOT send the
|
|
721
721
|
// private key to the responding CA (the requesting CA generates and holds
|
|
722
|
-
// it). The private-key-carrying POP choices
|
|
723
|
-
// deprecated thisMessage (cmp2000)
|
|
722
|
+
// it). The private-key-carrying POP choices, encryptedKey (cmp2021) and the
|
|
723
|
+
// deprecated thisMessage (cmp2000), are therefore forbidden in a ccr; the
|
|
724
724
|
// MAC (dhMAC / agreeMAC) and indirect (subsequentMessage) methods carry no
|
|
725
725
|
// key. Checked before the version gate so a ccr always gets the ccr verdict.
|
|
726
726
|
if (body.arm === "ccr" && body.decoded && body.decoded.messages) {
|
|
@@ -863,22 +863,22 @@ var PKI_MESSAGE = schema.seq([
|
|
|
863
863
|
* structurally; `pkiconf` decodes to `null`; every other defined arm --
|
|
864
864
|
* `p10cr` (feed `body.bytes` to `pki.schema.csr.parse`), the
|
|
865
865
|
* challenge-response and announcement arms, and `nested` (never
|
|
866
|
-
* auto-recursed)
|
|
866
|
+
* auto-recursed) surfaces raw with `decoded` absent. `certReqId` values
|
|
867
867
|
* are BigInt and accept the protocol's -1 sentinel.
|
|
868
868
|
*
|
|
869
869
|
* Protection is surfaced, not verified: `protection` carries the raw,
|
|
870
|
-
* octet-aligned BIT STRING (unused bits are rejected at parse
|
|
870
|
+
* octet-aligned BIT STRING (unused bits are rejected at parse, since every
|
|
871
871
|
* RFC 9481 protection algorithm emits whole octets), and the MAC or
|
|
872
872
|
* signature is computed over the DER of the virtual
|
|
873
|
-
* `ProtectedPart ::= SEQUENCE { header, body }
|
|
873
|
+
* `ProtectedPart ::= SEQUENCE { header, body }`, which you reconstruct as a DER
|
|
874
874
|
* SEQUENCE wrapping exactly `headerBytes || bodyBytes`. `extraCerts` are
|
|
875
|
-
* raw DER certificates and are
|
|
875
|
+
* raw DER certificates and are not covered by protection.
|
|
876
876
|
*
|
|
877
877
|
* Throws `CmpError` when the bytes are not a well-formed PKIMessage, and
|
|
878
878
|
* `Asn1Error` when the underlying DER is malformed. The request-arm interiors
|
|
879
879
|
* (`ir` / `cr` / `kur` / `krr` / `ccr`, and an `rr` entry's `certDetails`) are
|
|
880
880
|
* walked by the CRMF parser, so a malformed interior throws `CrmfError` with a
|
|
881
|
-
* `crmf/*` code
|
|
881
|
+
* `crmf/*` code; all are `PkiError` subclasses.
|
|
882
882
|
*
|
|
883
883
|
* @example
|
|
884
884
|
* var pair = await pki.key.generate("Ed25519");
|