@blamejs/pki 0.6.13 → 0.6.15
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 +22 -1
- package/README.md +1 -1
- package/index.js +1 -1
- package/lib/acme.js +59 -815
- package/lib/asn1-der.js +7 -245
- package/lib/attrcert-sign.js +12 -260
- package/lib/attrcert-verify.js +1 -7
- package/lib/byte-reader.js +0 -8
- package/lib/byte-writer.js +0 -11
- package/lib/cbor-det.js +0 -54
- package/lib/cmc-build.js +15 -357
- package/lib/cmc-verify.js +6 -239
- package/lib/cmp-build.js +41 -317
- package/lib/cmp-session.js +73 -760
- package/lib/cmp-verify.js +32 -488
- package/lib/cms-compress.js +3 -32
- package/lib/cms-decrypt.js +29 -294
- package/lib/cms-encrypt.js +14 -234
- package/lib/cms-sign.js +29 -324
- package/lib/cms-verify.js +9 -500
- package/lib/composite-kem.js +2 -102
- package/lib/composite-sig.js +1 -77
- package/lib/constants.js +0 -249
- package/lib/crl-sign.js +9 -237
- package/lib/crl-verify.js +1 -10
- package/lib/crmf-sign.js +19 -198
- package/lib/crmf-verify.js +1 -12
- package/lib/csr-sign.js +3 -34
- package/lib/csr-verify.js +1 -12
- package/lib/ct.js +28 -253
- package/lib/edwards-point.js +10 -44
- package/lib/est.js +28 -764
- package/lib/framework-error.js +4 -182
- package/lib/guard-all.js +1 -58
- package/lib/guard-async.js +1 -36
- package/lib/guard-bytes.js +6 -650
- package/lib/guard-compress.js +5 -102
- package/lib/guard-crypto.js +1 -40
- package/lib/guard-der.js +2 -93
- package/lib/guard-encoding.js +2 -60
- package/lib/guard-header.js +1 -36
- package/lib/guard-identifier.js +7 -531
- package/lib/guard-intrinsic.js +1 -150
- package/lib/guard-json.js +10 -73
- package/lib/guard-limits.js +1 -77
- package/lib/guard-list.js +1 -49
- package/lib/guard-name.js +3 -209
- package/lib/guard-parsed.js +7 -335
- package/lib/guard-range.js +1 -71
- package/lib/guard-secret.js +1 -102
- package/lib/guard-text.js +1 -64
- package/lib/guard-time.js +1 -97
- package/lib/hpke.js +3 -105
- package/lib/http-digest.js +14 -142
- package/lib/http-retry-after.js +1 -47
- package/lib/http-transport.js +21 -131
- package/lib/inspect.js +30 -284
- package/lib/ip-utils.js +6 -47
- package/lib/jose.js +7 -142
- package/lib/key.js +2 -67
- package/lib/lint.js +9 -194
- package/lib/merkle.js +1 -27
- package/lib/mime.js +9 -82
- package/lib/ocsp-request-verify.js +1 -14
- package/lib/ocsp-verify.js +1 -82
- package/lib/ocsp.js +11 -169
- package/lib/oid.js +5 -337
- package/lib/path-validate.js +103 -1492
- package/lib/pbes2.js +2 -89
- package/lib/pkcs12-build.js +31 -379
- package/lib/pki-build.js +64 -290
- package/lib/rc2.js +2 -18
- package/lib/rfc3339.js +4 -27
- package/lib/scep.js +59 -233
- package/lib/schema-all.js +0 -122
- package/lib/schema-attrcert.js +4 -146
- package/lib/schema-c509.js +153 -802
- package/lib/schema-cmc.js +5 -220
- package/lib/schema-cmp.js +2 -223
- package/lib/schema-cms.js +13 -427
- package/lib/schema-crl.js +5 -68
- package/lib/schema-crmf.js +6 -167
- package/lib/schema-csr.js +1 -54
- package/lib/schema-csrattrs.js +2 -87
- package/lib/schema-engine.js +0 -152
- package/lib/schema-ocsp.js +0 -117
- package/lib/schema-pkcs12.js +2 -93
- package/lib/schema-pkcs8.js +3 -35
- package/lib/schema-pkix.js +24 -655
- package/lib/schema-smime.js +0 -54
- package/lib/schema-tsp.js +3 -99
- package/lib/schema-x509.js +4 -63
- package/lib/shbs.js +11 -95
- package/lib/sign-scheme.js +2 -97
- package/lib/sigstore.js +9 -169
- package/lib/sleep.js +2 -8
- package/lib/smime.js +15 -408
- package/lib/tls-cert-compress.js +0 -62
- package/lib/trust.js +19 -185
- package/lib/tsp-sign.js +16 -270
- package/lib/validator-all.js +1 -30
- package/lib/validator-attcert.js +2 -46
- package/lib/validator-cose.js +2 -153
- package/lib/validator-keydesc.js +1 -37
- package/lib/validator-sig.js +1 -46
- package/lib/validator-tls.js +1 -88
- package/lib/validator-tpm.js +19 -122
- package/lib/verify-seam.js +1 -24
- package/lib/webauthn-mds.js +3 -400
- package/lib/webauthn.js +12 -833
- package/lib/webcrypto.js +5 -485
- package/lib/x509-sign.js +4 -108
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/schema-cmc.js
CHANGED
|
@@ -53,7 +53,6 @@ var TAGS = asn1.TAGS;
|
|
|
53
53
|
function E(code, message, cause) { return new CmcError(code, message, cause); }
|
|
54
54
|
function O(name) { return oid.byName(name); }
|
|
55
55
|
|
|
56
|
-
// Registry constants -- never a dotted literal in a format module.
|
|
57
56
|
var OID_PKI_DATA = O("id-cct-PKIData");
|
|
58
57
|
var OID_PKI_RESPONSE = O("id-cct-PKIResponse");
|
|
59
58
|
var OID_STATUS_INFO = O("id-cmc-statusInfo");
|
|
@@ -61,21 +60,13 @@ var OID_STATUS_INFO_V2 = O("id-cmc-statusInfoV2");
|
|
|
61
60
|
var OID_RESPONSE_BODY = O("id-cmc-responseBody");
|
|
62
61
|
var OID_RA_IDENTITY_WITNESS = O("id-cmc-raIdentityWitness");
|
|
63
62
|
|
|
64
|
-
// BodyPartID ::= INTEGER (0..4294967295), RFC 5272 sec. 3.2.2.
|
|
65
63
|
var BODY_PART_MAX = 4294967295n;
|
|
66
64
|
|
|
67
|
-
// CMCStatus ::= INTEGER { success(0), failed(2), pending(3), noSupport(4),
|
|
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
65
|
var STATUS_BY_VALUE = {
|
|
72
66
|
0: "success", 2: "failed", 3: "pending", 4: "noSupport",
|
|
73
67
|
5: "confirmRequired", 6: "popRequired", 7: "partial",
|
|
74
68
|
};
|
|
75
69
|
|
|
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
|
-
// for private extended reasons, so an unknown number is legal data.
|
|
79
70
|
var FAIL_INFO_BY_VALUE = {
|
|
80
71
|
0: "badAlg", 1: "badMessageCheck", 2: "badRequest", 3: "badTime", 4: "badCertId",
|
|
81
72
|
5: "unsupportedExt", 6: "mustArchiveKeys", 7: "badIdentity", 8: "popRequired",
|
|
@@ -83,35 +74,18 @@ var FAIL_INFO_BY_VALUE = {
|
|
|
83
74
|
13: "authDataFail",
|
|
84
75
|
};
|
|
85
76
|
|
|
86
|
-
// The CMS content types a Full PKI Request / Response may ride in, per RFC 5272
|
|
87
|
-
// sec. 3.2 ("encapsulated in either a SignedData or an AuthenticatedData") and
|
|
88
|
-
// sec. 4.2, which says the same of the response.
|
|
89
|
-
// The names are the OID registry's, read off `oid.name(...)` and never taken from the
|
|
90
|
-
// spec's prose: RFC 5652 calls the type AuthenticatedData, the registry row is
|
|
91
|
-
// `authData`, and hard-coding the prose spelling would reject every conforming
|
|
92
|
-
// AuthenticatedData response while looking correct.
|
|
93
77
|
var CMC_CARRIERS = { signedData: 1, authData: 1 };
|
|
94
78
|
|
|
95
|
-
// Control PLACEMENT -- RFC 6402 sec. 2.5 / 2.6 give two MUSTs about where a
|
|
96
|
-
// control may appear. An OID-keyed table states them once, in the shape of the
|
|
97
|
-
// shipped CMS attribute-placement registry, so a new placement rule is a row.
|
|
98
79
|
var CONTROL_PLACEMENT = {};
|
|
99
|
-
CONTROL_PLACEMENT[OID_RESPONSE_BODY] = "pkiResponse";
|
|
100
|
-
CONTROL_PLACEMENT[OID_RA_IDENTITY_WITNESS] = "pkiData";
|
|
80
|
+
CONTROL_PLACEMENT[OID_RESPONSE_BODY] = "pkiResponse";
|
|
81
|
+
CONTROL_PLACEMENT[OID_RA_IDENTITY_WITNESS] = "pkiData";
|
|
101
82
|
|
|
102
|
-
// ---- leaves ----------------------------------------------------------
|
|
103
83
|
|
|
104
|
-
// A BodyPartID read + bounded + narrowed atomically. `guard.range.int` takes
|
|
105
|
-
// BigInt bounds and returns a Number only when the value is inside them, so an
|
|
106
|
-
// out-of-range identifier can never reach the caller as a silently-truncated int.
|
|
107
84
|
function readBodyPartId(node, label) {
|
|
108
85
|
var v = asn1.read.integer(node);
|
|
109
86
|
return guard.range.int(v, 0n, BODY_PART_MAX, E, "cmc/bad-body-part-id", label || "a BodyPartID");
|
|
110
87
|
}
|
|
111
88
|
|
|
112
|
-
// The identity of an element, where 0 is reserved (RFC 5272 sec. 3.2.2: "The
|
|
113
|
-
// bodyPartID value of 0 is reserved for use as the reference to the current
|
|
114
|
-
// PKIData object"). A REFERENCE may be 0; an identity may not.
|
|
115
89
|
function readBodyPartIdentity(node) {
|
|
116
90
|
var id = readBodyPartId(node, "a body part identity");
|
|
117
91
|
if (id === 0) {
|
|
@@ -121,16 +95,7 @@ function readBodyPartIdentity(node) {
|
|
|
121
95
|
return id;
|
|
122
96
|
}
|
|
123
97
|
|
|
124
|
-
// ---- controls --------------------------------------------------------
|
|
125
98
|
|
|
126
|
-
// TaggedAttribute ::= SEQUENCE { bodyPartID BodyPartID, attrType OBJECT
|
|
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
|
-
//
|
|
132
|
-
// The placement rule is not checked here: it depends on which message the control
|
|
133
|
-
// sits in, which the enclosing walk knows and this shape does not.
|
|
134
99
|
var TAGGED_ATTRIBUTE = schema.seq([
|
|
135
100
|
schema.field("bodyPartID", schema.decode(readBodyPartIdentity)),
|
|
136
101
|
schema.field("attrType", schema.oidLeaf()),
|
|
@@ -149,8 +114,6 @@ var TAGGED_ATTRIBUTE = schema.seq([
|
|
|
149
114
|
},
|
|
150
115
|
});
|
|
151
116
|
|
|
152
|
-
// RFC 6402 sec. 2.5 / 2.6 give two MUSTs about WHERE a control may appear, and
|
|
153
|
-
// both are one-directional. Applied against the enclosing message kind.
|
|
154
117
|
function assertControlPlacement(control, where) {
|
|
155
118
|
var placement = CONTROL_PLACEMENT[control.attrType];
|
|
156
119
|
if (placement && placement !== where) {
|
|
@@ -160,11 +123,7 @@ function assertControlPlacement(control, where) {
|
|
|
160
123
|
}
|
|
161
124
|
}
|
|
162
125
|
|
|
163
|
-
// ---- status controls -------------------------------------------------
|
|
164
126
|
|
|
165
|
-
// BodyPartReference ::= CHOICE { bodyPartID BodyPartID, bodyPartPath BodyPartPath }
|
|
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
127
|
function parseBodyPartReference(node) {
|
|
169
128
|
if (node.tagClass === "universal" && node.tagNumber === TAGS.INTEGER) {
|
|
170
129
|
return { bodyPartID: readBodyPartId(node), bodyPartPath: null };
|
|
@@ -178,33 +137,8 @@ function parseBodyPartReference(node) {
|
|
|
178
137
|
throw E("cmc/bad-status-info", "a BodyPartReference must be a BodyPartID INTEGER or a BodyPartPath SEQUENCE");
|
|
179
138
|
}
|
|
180
139
|
|
|
181
|
-
|
|
182
|
-
* OtherStatusInfo ::= CHOICE { failInfo CMCFailInfo, pendInfo PendInfo,
|
|
183
|
-
* extendedFailInfo SEQUENCE { ... } }
|
|
184
|
-
*
|
|
185
|
-
* The load-bearing ambiguity. RFC 5272 App A and RFC 6402 App A.1 (the 1988
|
|
186
|
-
* module) leave `extendedFailInfo` UNTAGGED, so it collides with `pendInfo` --
|
|
187
|
-
* both are a bare SEQUENCE. RFC 6402 App A.2 (the 2008 module) tags it `[1]`.
|
|
188
|
-
* Both encodings occur on the wire, so both are decoded:
|
|
189
|
-
*
|
|
190
|
-
* universal INTEGER -> failInfo
|
|
191
|
-
* context [1] -> extendedFailInfo (2008 module)
|
|
192
|
-
* universal SEQUENCE -> disambiguate on the first child's tag:
|
|
193
|
-
* OCTET STRING -> pendInfo (PendInfo ::= SEQUENCE
|
|
194
|
-
* { pendToken OCTET STRING, pendTime GeneralizedTime })
|
|
195
|
-
* OBJECT IDENTIFIER -> extendedFailInfo
|
|
196
|
-
* anything else -> refuse
|
|
197
|
-
*
|
|
198
|
-
* The final arm is the point: a shape that matches neither rule is REFUSED, not
|
|
199
|
-
* guessed at. Guessing here would attach a failure reason, or a pending token,
|
|
200
|
-
* that the responder never sent.
|
|
201
|
-
*/
|
|
140
|
+
|
|
202
141
|
function parseOtherStatusInfo(node, version) {
|
|
203
|
-
// The extendedFailInfo arm belongs to CMCStatusInfoV2 alone. RFC 5272 sec. 6.1.1
|
|
204
|
-
// gives the v1 otherInfo exactly two arms, `CHOICE { failInfo CMCFailInfo,
|
|
205
|
-
// pendInfo PendInfo }`, so accepting the third here would read a failure
|
|
206
|
-
// verdict out of a v1 control that cannot legally express one, and the
|
|
207
|
-
// disambiguation below would be answering a question v1 never asks.
|
|
208
142
|
var extendedAllowed = version !== 1;
|
|
209
143
|
if (node.tagClass === "universal" && node.tagNumber === TAGS.INTEGER) {
|
|
210
144
|
var v = asn1.read.integer(node);
|
|
@@ -216,13 +150,6 @@ function parseOtherStatusInfo(node, version) {
|
|
|
216
150
|
throw E("cmc/bad-status-info",
|
|
217
151
|
"a v1 CMCStatusInfo otherInfo is failInfo or pendInfo only; the [1] extendedFailInfo arm is CMCStatusInfoV2's (RFC 5272 sec. 6.1.1)");
|
|
218
152
|
}
|
|
219
|
-
// IMPLICIT, so the [1] REPLACES the SEQUENCE tag and its children are the
|
|
220
|
-
// failInfoOID / failInfoValue directly -- there is no inner SEQUENCE to
|
|
221
|
-
// unwrap. Both RFC 6402 modules are IMPLICIT: A.1 (rfc6402.txt:805) and A.2
|
|
222
|
-
// (rfc6402.txt:1314) each open `DEFINITIONS IMPLICIT TAGS ::=`, and the
|
|
223
|
-
// EXPLICIT-override rule does not apply here because the tagged type is a
|
|
224
|
-
// plain SEQUENCE, not a CHOICE or an open type. Reading this arm as
|
|
225
|
-
// EXPLICIT would reject every conforming 2008-module response.
|
|
226
153
|
return { failInfo: null, failInfoName: null, pendInfo: null, extendedFailInfo: readExtendedFailInfo(node.children, "[1] extendedFailInfo") };
|
|
227
154
|
}
|
|
228
155
|
if (node.tagClass === "universal" && node.tagNumber === TAGS.SEQUENCE && node.constructed) {
|
|
@@ -230,9 +157,6 @@ function parseOtherStatusInfo(node, version) {
|
|
|
230
157
|
if (!first) throw E("cmc/ambiguous-status-info", "an empty OtherStatusInfo SEQUENCE matches neither pendInfo nor extendedFailInfo");
|
|
231
158
|
if (first.tagClass === "universal" && first.tagNumber === TAGS.OCTET_STRING) {
|
|
232
159
|
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 and do not
|
|
234
|
-
// accept either time form: a UTCTime here is a different encoding, and
|
|
235
|
-
// reading it anyway would let a two-digit year through the retry clock.
|
|
236
160
|
var timeNode = node.children[1];
|
|
237
161
|
if (timeNode.tagClass !== "universal" || timeNode.tagNumber !== TAGS.GENERALIZED_TIME) {
|
|
238
162
|
throw E("cmc/bad-status-info", "PendInfo pendTime must be a GeneralizedTime (RFC 5272 sec. 6.1.1)");
|
|
@@ -255,10 +179,6 @@ function parseOtherStatusInfo(node, version) {
|
|
|
255
179
|
throw E("cmc/bad-status-info", "OtherStatusInfo must be a CMCFailInfo INTEGER, a PendInfo SEQUENCE, or an extendedFailInfo");
|
|
256
180
|
}
|
|
257
181
|
|
|
258
|
-
// ExtendedFailInfo ::= SEQUENCE { failInfoOID OBJECT IDENTIFIER, failInfoValue ANY }
|
|
259
|
-
// The OID is surfaced raw and never mapped onto a CMCFailInfo: RFC 5272 sec.
|
|
260
|
-
// 6.1.1 makes internalCAError only a MAY for an unrecognized extended error, and
|
|
261
|
-
// synthesizing it would report a reason the responder did not send.
|
|
262
182
|
function readExtendedFailInfo(children, what) {
|
|
263
183
|
if (!children || children.length !== 2) {
|
|
264
184
|
throw E("cmc/bad-status-info", what + " must be SEQUENCE { failInfoOID, failInfoValue }");
|
|
@@ -266,12 +186,6 @@ function readExtendedFailInfo(children, what) {
|
|
|
266
186
|
return { failInfoOID: asn1.read.oid(children[0]), failInfoValueBytes: children[1].bytes };
|
|
267
187
|
}
|
|
268
188
|
|
|
269
|
-
// CMCStatusInfoV2 ::= SEQUENCE { cMCStatus, bodyList SEQUENCE SIZE(1..MAX) OF
|
|
270
|
-
// BodyPartReference, statusString UTF8String OPTIONAL, otherInfo OPTIONAL }
|
|
271
|
-
// The v1 CMCStatusInfo is the same shape with a SEQUENCE OF BodyPartID bodyList
|
|
272
|
-
// and no extendedFailInfo arm; both are decoded into one surface so a caller
|
|
273
|
-
// reads one ordered list of verdicts (RFC 5272 sec. 6.1: a client MUST cope with
|
|
274
|
-
// several status controls).
|
|
275
189
|
function parseStatusInfo(valueDer, version, bodyPartID) {
|
|
276
190
|
var root = asn1.decode(valueDer);
|
|
277
191
|
if (root.tagClass !== "universal" || root.tagNumber !== TAGS.SEQUENCE || !root.constructed) {
|
|
@@ -284,8 +198,6 @@ function parseStatusInfo(valueDer, version, bodyPartID) {
|
|
|
284
198
|
var statusNum = guard.range.int(statusValue, 0n, 0xFFFFFFFFn, E, "cmc/bad-status", "a CMCStatus");
|
|
285
199
|
var statusName = STATUS_BY_VALUE[statusNum];
|
|
286
200
|
if (!statusName) {
|
|
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 and never mapped.
|
|
289
201
|
throw E("cmc/bad-status", "unknown CMCStatus " + statusNum + " (1 is reserved; RFC 5272 sec. 6.1.3)");
|
|
290
202
|
}
|
|
291
203
|
|
|
@@ -300,17 +212,10 @@ function parseStatusInfo(valueDer, version, bodyPartID) {
|
|
|
300
212
|
return version === 2 ? parseBodyPartReference(c) : { bodyPartID: readBodyPartId(c), bodyPartPath: null };
|
|
301
213
|
});
|
|
302
214
|
|
|
303
|
-
// The two OPTIONAL tail fields are ORDERED by the ASN.1: statusString precedes
|
|
304
|
-
// otherInfo. Tracking only "have I seen a statusString yet" would accept them
|
|
305
|
-
// reversed -- a second encoding of a structure DER admits exactly one encoding
|
|
306
|
-
// of -- so the walk refuses anything once otherInfo has been consumed.
|
|
307
215
|
var statusString = null, other = null, sawOther = false;
|
|
308
216
|
for (var i = 2; i < kids.length; i++) {
|
|
309
217
|
var k = kids[i];
|
|
310
218
|
if (k.tagClass === "universal" && k.tagNumber === TAGS.UTF8_STRING && statusString === null && !sawOther) {
|
|
311
|
-
// read.string, not a per-type reader: the codec exposes one strict text reader
|
|
312
|
-
// for the DirectoryString family and there is no `read.utf8`. The tag is
|
|
313
|
-
// already asserted UTF8String above, so this decodes exactly that.
|
|
314
219
|
statusString = asn1.read.string(k);
|
|
315
220
|
continue;
|
|
316
221
|
}
|
|
@@ -334,11 +239,6 @@ function parseStatusInfo(valueDer, version, bodyPartID) {
|
|
|
334
239
|
return out;
|
|
335
240
|
}
|
|
336
241
|
|
|
337
|
-
// The cross-field coupling (RFC 5272 sec. 6.1.1 / 6.1.2): failInfo and
|
|
338
|
-
// extendedFailInfo are "present only if cMCStatus contains the value failed",
|
|
339
|
-
// and pendInfo "MUST be populated for a cMCStatus value of pending or partial".
|
|
340
|
-
// A message whose status and detail disagree is refused, never reconciled:
|
|
341
|
-
// the two halves would otherwise let a caller read a verdict neither field states.
|
|
342
242
|
function _assertStatusCoherent(s) {
|
|
343
243
|
var isFailed = s.status === "failed";
|
|
344
244
|
var isPending = s.status === "pending" || s.status === "partial";
|
|
@@ -356,19 +256,7 @@ function _assertStatusCoherent(s) {
|
|
|
356
256
|
}
|
|
357
257
|
}
|
|
358
258
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
// TaggedRequest ::= CHOICE { tcr [0] TaggedCertificationRequest,
|
|
362
|
-
// crm [1] CertReqMsg,
|
|
363
|
-
// orm [2] SEQUENCE { bodyPartID, requestMessageType,
|
|
364
|
-
// requestMessageValue } }
|
|
365
|
-
// The CMC module is IMPLICIT TAGS, so each context tag REPLACES the arm's own
|
|
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
|
-
// (and invalid) message, which is what C5 pins.
|
|
369
|
-
// An IMPLICIT context-tagged constructed node re-encoded under the universal
|
|
370
|
-
// SEQUENCE tag it stands in for. IMPLICIT tagging REPLACES the tag and leaves the
|
|
371
|
-
// content untouched, so restoring it is exactly a re-headering of the same bytes.
|
|
259
|
+
|
|
372
260
|
function _asUniversalSequence(node) {
|
|
373
261
|
var headerLen = node.header.end - node.header.start;
|
|
374
262
|
return asn1.encode(0x00, true, TAGS.SEQUENCE, node.bytes.subarray(headerLen));
|
|
@@ -379,7 +267,6 @@ function parseTaggedRequest(node) {
|
|
|
379
267
|
throw E("cmc/bad-tagged-request", "a TaggedRequest must be a context-tagged constructed CHOICE arm (RFC 5272 sec. 3.2.1.2)");
|
|
380
268
|
}
|
|
381
269
|
if (node.tagNumber === 0) {
|
|
382
|
-
// TaggedCertificationRequest ::= SEQUENCE { bodyPartID, certificationRequest }
|
|
383
270
|
if (node.children.length !== 2) {
|
|
384
271
|
throw E("cmc/bad-tagged-request", "a tcr arm is [0] IMPLICIT { bodyPartID, certificationRequest }; the module is IMPLICIT TAGS, so an EXPLICIT wrapper is not this arm");
|
|
385
272
|
}
|
|
@@ -394,23 +281,6 @@ function parseTaggedRequest(node) {
|
|
|
394
281
|
};
|
|
395
282
|
}
|
|
396
283
|
if (node.tagNumber === 1) {
|
|
397
|
-
// crm [1] IMPLICIT CertReqMsg.
|
|
398
|
-
//
|
|
399
|
-
// This arm still has a body part IDENTITY, and it is not a separate field:
|
|
400
|
-
// RFC 5272 sec. 3.2.2 says "Body part identifiers are encoded in the
|
|
401
|
-
// certReqIds field for CertReqMsg objects (in a TaggedRequest) or in the
|
|
402
|
-
// bodyPartID field of the other objects", and sec. 6.5.2 repeats it
|
|
403
|
-
// ("... either the bodyPartID of a TaggedCertificationRequest (PKCS #10) or
|
|
404
|
-
// the certReqId of the CertRequest within a CertReqMsg (CRMF)"). So the
|
|
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
|
-
// exempt this arm from it.
|
|
408
|
-
//
|
|
409
|
-
// CertReqMsg ::= SEQUENCE { certReq CertRequest, popo OPTIONAL, regInfo OPTIONAL }
|
|
410
|
-
// CertRequest ::= SEQUENCE { certReqId INTEGER, certTemplate CertTemplate, ... }
|
|
411
|
-
//
|
|
412
|
-
// Only the identity is decoded here; the CertReqMsg's own conformance is
|
|
413
|
-
// pki.schema.crmf's to enforce, and duplicating it would fork the rules.
|
|
414
284
|
var certReq = node.children[0];
|
|
415
285
|
if (!certReq || certReq.tagClass !== "universal" || certReq.tagNumber !== TAGS.SEQUENCE || !certReq.constructed) {
|
|
416
286
|
throw E("cmc/bad-tagged-request", "a crm arm is [1] IMPLICIT CertReqMsg, whose first element is a CertRequest SEQUENCE");
|
|
@@ -421,9 +291,6 @@ function parseTaggedRequest(node) {
|
|
|
421
291
|
return {
|
|
422
292
|
arm: "crm", bodyPartID: readBodyPartIdentity(certReq.children[0]),
|
|
423
293
|
certificationRequestBytes: null,
|
|
424
|
-
// Re-tagged from the IMPLICIT [1] back to the universal SEQUENCE the CRMF
|
|
425
|
-
// parser expects: handing back the context-tagged bytes would produce a
|
|
426
|
-
// structure no CertReqMsg reader accepts.
|
|
427
294
|
certReqMsgBytes: _asUniversalSequence(node),
|
|
428
295
|
requestMessageType: null, requestMessageValueBytes: null,
|
|
429
296
|
};
|
|
@@ -442,12 +309,7 @@ function parseTaggedRequest(node) {
|
|
|
442
309
|
throw E("cmc/bad-tagged-request", "unknown TaggedRequest alternative [" + node.tagNumber + "] (RFC 5272 sec. 3.2.1.2)");
|
|
443
310
|
}
|
|
444
311
|
|
|
445
|
-
// ---- body-part identity ----------------------------------------------
|
|
446
312
|
|
|
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 whole message: a checker that ran
|
|
449
|
-
// per-sequence would accept a control and a request that collide, which is
|
|
450
|
-
// exactly the shape B2 pins.
|
|
451
313
|
function assertUniqueBodyPartIds(ids) {
|
|
452
314
|
var seen = Object.create(null);
|
|
453
315
|
for (var i = 0; i < ids.length; i++) {
|
|
@@ -460,21 +322,7 @@ function assertUniqueBodyPartIds(ids) {
|
|
|
460
322
|
}
|
|
461
323
|
}
|
|
462
324
|
|
|
463
|
-
|
|
464
|
-
|
|
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 (the bug class
|
|
467
|
-
// a hand-rolled cursor keeps reintroducing), while the rules that genuinely span
|
|
468
|
-
// fields (whole-message body-part uniqueness, control placement, the status
|
|
469
|
-
// cross-checks) run in the build / the caller, which is where they can see
|
|
470
|
-
// everything they need.
|
|
471
|
-
// A repeat's walk result is the match object `{ node, items: [{ node, value }] }`,
|
|
472
|
-
// so each of these carries a `build` that flattens it to the plain list the
|
|
473
|
-
// enclosing build wants. The three differ by what an item's `value` IS, which is
|
|
474
|
-
// decided by the item schema:
|
|
475
|
-
// - a `seq` item walks to its own MATCH, so its build output is at .value.result
|
|
476
|
-
// - a `decode` leaf walks straight to whatever its function returned
|
|
477
|
-
// - an `any` item walks to the node, whose .bytes is the raw TLV
|
|
325
|
+
|
|
478
326
|
function builtList(match) { return match.items.map(function (i) { return i.value.result; }); }
|
|
479
327
|
function decodedList(match) { return match.items.map(function (i) { return i.value; }); }
|
|
480
328
|
function rawList(match) { return match.items.map(function (i) { return i.node.bytes; }); }
|
|
@@ -484,26 +332,12 @@ var CONTROL_SEQUENCE = schema.seqOf(TAGGED_ATTRIBUTE,
|
|
|
484
332
|
var REQ_SEQUENCE = schema.seqOf(schema.decode(parseTaggedRequest),
|
|
485
333
|
{ code: "cmc/bad-tagged-request", what: "a reqSequence", build: decodedList });
|
|
486
334
|
|
|
487
|
-
// TaggedContentInfo ::= SEQUENCE { bodyPartID, contentInfo } (RFC 5272 sec.
|
|
488
|
-
// 3.2.1.3) and OtherMsg ::= SEQUENCE { bodyPartID, otherMsgType, otherMsgValue }
|
|
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
|
-
// "each element of a PKIData or PKIResponse". Decoding just the identifier is
|
|
493
|
-
// what lets that rule cover them without this layer claiming to understand the
|
|
494
|
-
// payload.
|
|
495
335
|
var TAGGED_CONTENT_INFO = schema.seq([
|
|
496
336
|
schema.field("bodyPartID", schema.decode(readBodyPartIdentity)),
|
|
497
337
|
schema.field("contentInfo", schema.any()),
|
|
498
338
|
], {
|
|
499
339
|
assert: "sequence", arity: { exact: 2 }, code: "cmc/bad-cms-sequence", what: "a TaggedContentInfo",
|
|
500
340
|
build: function (m) {
|
|
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
|
-
// OBJECT IDENTIFIER, content [0] EXPLICIT ANY OPTIONAL } (RFC 5652 sec. 3).
|
|
504
|
-
// Surfacing an INTEGER, or anything else that cannot be one, as a content
|
|
505
|
-
// info would hand the caller a "CMS message" no CMS reader will accept, which
|
|
506
|
-
// is not the same thing as a content type this decoder happens not to handle.
|
|
507
341
|
var ci = m.fields.contentInfo.value;
|
|
508
342
|
var kids = ci.children || [];
|
|
509
343
|
var lead = kids[0];
|
|
@@ -511,23 +345,11 @@ var TAGGED_CONTENT_INFO = schema.seq([
|
|
|
511
345
|
throw E("cmc/bad-cms-sequence",
|
|
512
346
|
"a TaggedContentInfo carries a CMS ContentInfo, which leads with its contentType OBJECT IDENTIFIER (RFC 5652 sec. 3)");
|
|
513
347
|
}
|
|
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
|
-
// toolkit's own CMS parser refuses them, so accepting them here would let the
|
|
518
|
-
// builder sign a "CMS message" pki.schema.cms.parse will not read back. The
|
|
519
|
-
// VALUE is still not interpreted: this layer only establishes that there is a
|
|
520
|
-
// content type to interpret.
|
|
521
348
|
try { asn1.read.oid(lead); }
|
|
522
349
|
catch (e) {
|
|
523
350
|
throw E("cmc/bad-cms-sequence",
|
|
524
351
|
"a TaggedContentInfo carries a CMS ContentInfo, whose contentType must be a readable OBJECT IDENTIFIER (RFC 5652 sec. 3)", e);
|
|
525
352
|
}
|
|
526
|
-
// The whole envelope, not just its first field: content is `[0] EXPLICIT ANY
|
|
527
|
-
// DEFINED BY contentType OPTIONAL`, so at most one more element and it is that
|
|
528
|
-
// tag. An INTEGER sitting where the content belongs, or a third field, is not
|
|
529
|
-
// a ContentInfo any CMS reader will take, and stopping at the leading OID
|
|
530
|
-
// would surface it as one.
|
|
531
353
|
if (kids.length > 2) {
|
|
532
354
|
throw E("cmc/bad-cms-sequence",
|
|
533
355
|
"a CMS ContentInfo is { contentType, [0] EXPLICIT content OPTIONAL }; this carries " +
|
|
@@ -539,10 +361,6 @@ var TAGGED_CONTENT_INFO = schema.seq([
|
|
|
539
361
|
throw E("cmc/bad-cms-sequence",
|
|
540
362
|
"a CMS ContentInfo's second field is the [0] EXPLICIT content (RFC 5652 sec. 3)");
|
|
541
363
|
}
|
|
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
|
-
// builder's readback would pass a message its own CMS reader will not accept.
|
|
546
364
|
if (!content.children || content.children.length !== 1) {
|
|
547
365
|
throw E("cmc/bad-cms-sequence",
|
|
548
366
|
"a ContentInfo's [0] EXPLICIT content wraps exactly one value, got " +
|
|
@@ -568,9 +386,6 @@ var OTHER_MSG = schema.seq([
|
|
|
568
386
|
},
|
|
569
387
|
});
|
|
570
388
|
|
|
571
|
-
// PKIData ::= SEQUENCE { controlSequence, reqSequence, cmsSequence,
|
|
572
|
-
// otherMsgSequence }, RFC 5272 sec. 3.2.1. All four are mandatory though each
|
|
573
|
-
// may be empty, so `arity: { exact: 4 }` is the rule, not a convenience.
|
|
574
389
|
var PKI_DATA = schema.seq([
|
|
575
390
|
schema.field("controlSequence", CONTROL_SEQUENCE),
|
|
576
391
|
schema.field("reqSequence", REQ_SEQUENCE),
|
|
@@ -588,17 +403,11 @@ var PKI_DATA = schema.seq([
|
|
|
588
403
|
requests: m.fields.reqSequence.value.result,
|
|
589
404
|
cmsSequence: m.fields.cmsSequence.value.result,
|
|
590
405
|
otherMsgs: m.fields.otherMsgSequence.value.result,
|
|
591
|
-
// The raw reqSequence TLV, tag and length included: the Identity Proof V2
|
|
592
|
-
// witness is computed over exactly these bytes "encoded exactly as it
|
|
593
|
-
// appears in the Full PKI Request" (RFC 5272 sec. 6.2.1), so it is taken
|
|
594
|
-
// off the matched node, never re-encoded from the decoded requests.
|
|
595
406
|
reqSequenceBytes: m.fields.reqSequence.node.bytes,
|
|
596
407
|
};
|
|
597
408
|
},
|
|
598
409
|
});
|
|
599
410
|
|
|
600
|
-
// PKIResponse ::= SEQUENCE { controlSequence, cmsSequence, otherMsgSequence } --
|
|
601
|
-
// RFC 5272 sec. 4.2.1. THREE fields: there is no reqSequence in a response.
|
|
602
411
|
var PKI_RESPONSE = schema.seq([
|
|
603
412
|
schema.field("controlSequence", CONTROL_SEQUENCE),
|
|
604
413
|
schema.field("cmsSequence", schema.seqOf(TAGGED_CONTENT_INFO,
|
|
@@ -620,10 +429,6 @@ var PKI_RESPONSE = schema.seq([
|
|
|
620
429
|
},
|
|
621
430
|
});
|
|
622
431
|
|
|
623
|
-
// Collect the status controls out of an already-parsed control list, in wire
|
|
624
|
-
// order. Absence is not an error: RFC 5272 sec. 6.1.2 says "If no status exists
|
|
625
|
-
// for a Simple or Full PKI Request, then the value of success is assumed", so a
|
|
626
|
-
// response with no status control is a successful one.
|
|
627
432
|
function _collectStatuses(controls) {
|
|
628
433
|
var out = [];
|
|
629
434
|
for (var i = 0; i < controls.length; i++) {
|
|
@@ -638,21 +443,12 @@ function _collectStatuses(controls) {
|
|
|
638
443
|
return out;
|
|
639
444
|
}
|
|
640
445
|
|
|
641
|
-
// The cross-cutting rules that a per-field walk cannot see: where each control is
|
|
642
|
-
// allowed to appear, which of them are status verdicts, which are unrecognized
|
|
643
|
-
// (surfaced, never a fault), and the whole-message body-part uniqueness.
|
|
644
446
|
function _finishBody(body, where) {
|
|
645
447
|
for (var i = 0; i < body.controls.length; i++) assertControlPlacement(body.controls[i], where);
|
|
646
448
|
body.statuses = _collectStatuses(body.controls);
|
|
647
449
|
body.unhandled = body.controls.filter(function (c) {
|
|
648
450
|
return c.attrType !== OID_STATUS_INFO && c.attrType !== OID_STATUS_INFO_V2 && c.attrName === null;
|
|
649
451
|
});
|
|
650
|
-
// RFC 5272 sec. 3.2.2 scopes uniqueness to "each element of a PKIData or
|
|
651
|
-
// PKIResponse", and four element kinds carry an identity. Enumerated here in
|
|
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, the tcr/orm
|
|
654
|
-
// bodyPartID and the crm arm's certReqId), content infos (sec. 3.2.1.3) and
|
|
655
|
-
// other messages (sec. 3.2.1.4).
|
|
656
452
|
var ids = [];
|
|
657
453
|
function collect(list) { for (var j = 0; j < list.length; j++) ids.push(list[j].bodyPartID); }
|
|
658
454
|
collect(body.controls);
|
|
@@ -745,20 +541,9 @@ function parsePkiResponse(input) {
|
|
|
745
541
|
* pki.schema.cmc.parse(der).kind; // "pkiData"
|
|
746
542
|
*/
|
|
747
543
|
function parse(input) {
|
|
748
|
-
// Bytes or an already-parsed CMS? Decided by a POSITIVE marker of the parsed
|
|
749
|
-
// shape, never by "it is an object" -- a Uint8Array is an object too, so the
|
|
750
|
-
// looser test routes perfectly good DER to the parsed branch, where it has no
|
|
751
|
-
// content type and is refused as not-CMC while the identical Buffer parses.
|
|
752
|
-
// encapContentInfo is the field this function goes on to read, so requiring it
|
|
753
|
-
// is the same question the code below already asks.
|
|
754
544
|
var parsedCms = (input && typeof input === "object" && !ArrayBuffer.isView(input) &&
|
|
755
545
|
!guard.bytes.isByteSource(input) && input.encapContentInfo != null)
|
|
756
546
|
? input : cms.parse(input);
|
|
757
|
-
// The CARRIER is part of the identity, not just the encapsulated content type.
|
|
758
|
-
// RFC 5272 sec. 3.2 / 4.2 admit a SignedData or an AuthenticatedData; other CMS
|
|
759
|
-
// types also expose an encapContentInfo (CompressedData does), and dispatching
|
|
760
|
-
// on the inner OID alone would accept an unsigned, unauthenticated wrapper as a
|
|
761
|
-
// Full PKI message.
|
|
762
547
|
if (CMC_CARRIERS[parsedCms.contentTypeName] !== 1) {
|
|
763
548
|
throw E("cmc/not-cmc",
|
|
764
549
|
"a Full PKI Request / Response is carried in a CMS SignedData or AuthenticatedData, got " +
|