@blamejs/pki 0.1.21 → 0.1.23
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 +47 -0
- package/README.md +17 -6
- package/index.js +16 -12
- package/lib/asn1-der.js +126 -68
- package/lib/constants.js +46 -21
- package/lib/ct.js +54 -39
- package/lib/framework-error.js +64 -32
- package/lib/oid.js +114 -58
- package/lib/path-validate.js +302 -135
- package/lib/schema-all.js +54 -40
- package/lib/schema-attrcert.js +101 -64
- package/lib/schema-cmp.js +152 -131
- package/lib/schema-cms.js +621 -163
- package/lib/schema-crl.js +43 -21
- package/lib/schema-crmf.js +136 -79
- package/lib/schema-csr.js +63 -14
- package/lib/schema-engine.js +92 -43
- package/lib/schema-ocsp.js +101 -55
- package/lib/schema-pkcs12.js +80 -64
- package/lib/schema-pkcs8.js +12 -12
- package/lib/schema-pkix.js +306 -111
- package/lib/schema-smime.js +363 -0
- package/lib/schema-tsp.js +108 -59
- package/lib/schema-x509.js +36 -25
- package/lib/webcrypto.js +156 -22
- package/package.json +3 -2
- package/sbom.cdx.json +6 -6
package/lib/schema-ocsp.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* @slug ocsp
|
|
10
10
|
*
|
|
11
11
|
* @intro
|
|
12
|
-
* OCSP request and response handling per RFC 6960 (
|
|
13
|
-
* OCSPResponse). Two entry points
|
|
12
|
+
* OCSP request and response handling per RFC 6960 (sec. 4.1 OCSPRequest, sec. 4.2
|
|
13
|
+
* OCSPResponse). Two entry points -- `parseRequest` and `parseResponse` -- turn a
|
|
14
14
|
* DER or PEM message into a structured object. A response is a two-stage
|
|
15
15
|
* OID-dispatch: `OCSPResponse` carries an ENUMERATED `responseStatus` and an
|
|
16
16
|
* OPTIONAL `responseBytes`; when the `responseType` is `id-pkix-ocsp-basic` its
|
|
@@ -23,11 +23,13 @@
|
|
|
23
23
|
* `issuerNameHash` / `issuerKeyHash` as raw octets, and the responder's `byKey`
|
|
24
24
|
* hash and the raw `signature` bytes are left for the caller to verify. Embedded
|
|
25
25
|
* certificates are surfaced as raw DER, so an unknown alternative never fails the
|
|
26
|
-
* parse.
|
|
26
|
+
* parse. The nonce extension's value syntax is validated (OCTET STRING of 1..128
|
|
27
|
+
* octets, RFC 9654 sec. 2.1) with the decoded bytes surfaced as `nonce` alongside the
|
|
28
|
+
* raw value; other extension values stay opaque. DER-only, fail-closed.
|
|
27
29
|
*
|
|
28
30
|
* @card
|
|
29
31
|
* Parse DER / PEM OCSP requests and responses (RFC 6960) into structured,
|
|
30
|
-
* validated fields
|
|
32
|
+
* validated fields -- per-certificate status, responder identity, raw tbs bytes for
|
|
31
33
|
* external verification, certificates kept raw, fail-closed.
|
|
32
34
|
*/
|
|
33
35
|
|
|
@@ -56,31 +58,32 @@ var VERSION = pkix.versionReader(NS, {});
|
|
|
56
58
|
// other is recognized-and-deferred. Resolved from the registry, never a literal.
|
|
57
59
|
var OID_OCSP_BASIC = oid.byName("ocspBasic");
|
|
58
60
|
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
+
// id-pkix-ocsp-nonce -- the request/response binding extension whose value syntax
|
|
62
|
+
// RFC 9654 sec. 2.1 (updating RFC 6960 sec. 4.4.1) fixes to OCTET STRING (SIZE(1..128)).
|
|
63
|
+
var OID_OCSP_NONCE = oid.byName("ocspNonce");
|
|
64
|
+
|
|
65
|
+
// OCSPResponseStatus ::= ENUMERATED -- value 4 is "not used" and >= 7 is undefined,
|
|
66
|
+
// so both are rejected (RFC 6960 sec. 4.2.1).
|
|
61
67
|
var STATUS_NAMES = { "0": "successful", "1": "malformedRequest", "2": "internalError", "3": "tryLater", "5": "sigRequired", "6": "unauthorized" };
|
|
62
68
|
|
|
63
|
-
// CRLReason ::= ENUMERATED
|
|
64
|
-
|
|
69
|
+
// CRLReason ::= ENUMERATED -- value 7 is "not used"; the rest are RFC 5280
|
|
70
|
+
// sec. 5.3.1. The legal set + names live once in pkix.CRL_REASON_NAMES (shared
|
|
71
|
+
// with the CRL reasonCode decoder, which surfaces the numeric code).
|
|
72
|
+
var CRL_REASONS = pkix.CRL_REASON_NAMES;
|
|
65
73
|
|
|
66
74
|
// ---- shared leaves ---------------------------------------------------
|
|
67
75
|
|
|
68
76
|
// A GeneralizedTime-only leaf. OCSP times (producedAt / thisUpdate / nextUpdate /
|
|
69
77
|
// revocationTime) are GeneralizedTime, never UTCTime (RFC 6960); assert the tag
|
|
70
78
|
// before the codec reads it so a UTCTime is rejected ocsp/bad-time.
|
|
71
|
-
var GENERALIZED_TIME =
|
|
72
|
-
if (n.tagClass !== "universal" || n.tagNumber !== asn1.TAGS.GENERALIZED_TIME) {
|
|
73
|
-
throw ctx.E("ocsp/bad-time", "OCSP time must be a GeneralizedTime (RFC 6960)");
|
|
74
|
-
}
|
|
75
|
-
return asn1.read.time(n);
|
|
76
|
-
});
|
|
79
|
+
var GENERALIZED_TIME = pkix.generalizedTime(NS, { code: "ocsp/bad-time", message: "OCSP time must be a GeneralizedTime (RFC 6960)" });
|
|
77
80
|
|
|
78
81
|
// OCSPResponseStatus decode-and-whitelist leaf. read.enumerated is tag-checked, so
|
|
79
82
|
// an INTEGER-encoded status is rejected at the leaf (asn1/unexpected-tag).
|
|
80
83
|
var OCSP_RESPONSE_STATUS = schema.decode(function (n, ctx) {
|
|
81
84
|
var v = asn1.read.enumerated(n);
|
|
82
85
|
var key = v.toString();
|
|
83
|
-
if (!Object.prototype.hasOwnProperty.call(STATUS_NAMES, key)) throw ctx.E("ocsp/bad-response-status", "undefined OCSPResponseStatus " + key + " (RFC 6960
|
|
86
|
+
if (!Object.prototype.hasOwnProperty.call(STATUS_NAMES, key)) throw ctx.E("ocsp/bad-response-status", "undefined OCSPResponseStatus " + key + " (RFC 6960 sec. 4.2.1)");
|
|
84
87
|
return { code: Number(v), name: STATUS_NAMES[key] };
|
|
85
88
|
});
|
|
86
89
|
|
|
@@ -88,25 +91,20 @@ var OCSP_RESPONSE_STATUS = schema.decode(function (n, ctx) {
|
|
|
88
91
|
var CRL_REASON = schema.decode(function (n, ctx) {
|
|
89
92
|
var v = asn1.read.enumerated(n);
|
|
90
93
|
var key = v.toString();
|
|
91
|
-
if (!Object.prototype.hasOwnProperty.call(CRL_REASONS, key)) throw ctx.E("ocsp/bad-revocation-reason", "undefined CRLReason " + key + " (RFC 5280
|
|
94
|
+
if (!Object.prototype.hasOwnProperty.call(CRL_REASONS, key)) throw ctx.E("ocsp/bad-revocation-reason", "undefined CRLReason " + key + " (RFC 5280 sec. 5.3.1)");
|
|
92
95
|
return CRL_REASONS[key];
|
|
93
96
|
});
|
|
94
97
|
|
|
95
98
|
// A certs [0] SEQUENCE OF Certificate element. Unlike the CMS CertificateChoices
|
|
96
|
-
// (a tagged CHOICE), an OCSP certs element is a plain Certificate
|
|
97
|
-
// SEQUENCE (RFC 5280
|
|
99
|
+
// (a tagged CHOICE), an OCSP certs element is a plain Certificate -- a universal
|
|
100
|
+
// SEQUENCE (RFC 5280 sec. 4.1) -- so assert that shape before surfacing its raw DER, and
|
|
98
101
|
// reject a non-SEQUENCE element rather than reporting arbitrary bytes as a cert.
|
|
99
102
|
function certificateBytes() {
|
|
100
|
-
return
|
|
101
|
-
if (n.tagClass !== "universal" || n.tagNumber !== asn1.TAGS.SEQUENCE || !n.children) {
|
|
102
|
-
throw ctx.E("ocsp/bad-certs", "each certs element must be a Certificate (a SEQUENCE) (RFC 6960 §4.1.1/§4.2.1)");
|
|
103
|
-
}
|
|
104
|
-
return n.bytes;
|
|
105
|
-
});
|
|
103
|
+
return pkix.rawNonEmptySequence(NS, { code: "ocsp/bad-certs", message: "each certs element must be a non-empty Certificate SEQUENCE (RFC 6960 sec. 4.1.1/sec. 4.2.1)" });
|
|
106
104
|
}
|
|
107
105
|
|
|
108
|
-
// requestorName [1] EXPLICIT GeneralName (RFC 6960
|
|
109
|
-
// via the shared pkix.generalName primitive (RFC 5280
|
|
106
|
+
// requestorName [1] EXPLICIT GeneralName (RFC 6960 sec. 4.1.1) -- validated + surfaced raw
|
|
107
|
+
// via the shared pkix.generalName primitive (RFC 5280 sec. 4.2.1.6), so a malformed
|
|
110
108
|
// GeneralName fails closed and the OCSP + TSP parsers cannot drift on this grammar.
|
|
111
109
|
var GENERAL_NAME_RAW = pkix.generalName(NS, { code: "ocsp/bad-requestor-name" });
|
|
112
110
|
|
|
@@ -119,15 +117,43 @@ function _rawSignature(field) {
|
|
|
119
117
|
return sig.bytes;
|
|
120
118
|
}
|
|
121
119
|
|
|
122
|
-
// certs [0] EXPLICIT SEQUENCE OF Certificate
|
|
120
|
+
// certs [0] EXPLICIT SEQUENCE OF Certificate -- each element raw. Shared by the
|
|
123
121
|
// request Signature and the BasicOCSPResponse.
|
|
124
122
|
var CERTS = schema.seqOf(certificateBytes(), { assert: "sequence", code: "ocsp/bad-certs", what: "certs" });
|
|
125
123
|
|
|
124
|
+
// Validate the OCSP protocol extension values in a decoded extension list --
|
|
125
|
+
// applied to every extension surface (request / single-request / response /
|
|
126
|
+
// single-response) so a hostile placement cannot dodge the syntax rule. The
|
|
127
|
+
// nonce is the one extension whose value the module names: Nonce ::= OCTET
|
|
128
|
+
// STRING (SIZE(1..128)) (RFC 9654 sec. 2.1, updating RFC 6960 sec. 4.4.1 -- the 128
|
|
129
|
+
// upper bound is the RFC 9654 responder rule; RFC 8954's was 32). The decoded
|
|
130
|
+
// nonce bytes are surfaced on the extension as `nonce` alongside the raw value;
|
|
131
|
+
// other extension values stay opaque (an unknown extension is representable,
|
|
132
|
+
// not a fault).
|
|
133
|
+
function _validateOcspExtensions(list) {
|
|
134
|
+
if (!list) return list;
|
|
135
|
+
for (var i = 0; i < list.length; i++) {
|
|
136
|
+
var ext = list[i];
|
|
137
|
+
if (ext.oid !== OID_OCSP_NONCE) continue;
|
|
138
|
+
var node;
|
|
139
|
+
try { node = asn1.decode(ext.value); }
|
|
140
|
+
catch (e) { throw NS.E("ocsp/bad-nonce", "the nonce extension value did not decode: " + ((e && e.message) || String(e)), e); }
|
|
141
|
+
if (!(node.tagClass === "universal" && node.tagNumber === asn1.TAGS.OCTET_STRING && node.content)) {
|
|
142
|
+
throw NS.E("ocsp/bad-nonce", "the nonce extension value must be an OCTET STRING (RFC 9654 sec. 2.1)");
|
|
143
|
+
}
|
|
144
|
+
if (node.content.length < 1 || node.content.length > 128) {
|
|
145
|
+
throw NS.E("ocsp/bad-nonce", "the nonce must be 1..128 octets (RFC 9654 sec. 2.1)");
|
|
146
|
+
}
|
|
147
|
+
ext.nonce = node.content;
|
|
148
|
+
}
|
|
149
|
+
return list;
|
|
150
|
+
}
|
|
151
|
+
|
|
126
152
|
// ---- CertID (shared request + response) ------------------------------
|
|
127
153
|
|
|
128
154
|
// CertID ::= SEQUENCE { hashAlgorithm AlgorithmIdentifier, issuerNameHash OCTET
|
|
129
155
|
// STRING, issuerKeyHash OCTET STRING, serialNumber CertificateSerialNumber }
|
|
130
|
-
// (RFC 6960
|
|
156
|
+
// (RFC 6960 sec. 4.1.1). The two hashes are surfaced raw for the caller to verify.
|
|
131
157
|
var CERT_ID = schema.seq([
|
|
132
158
|
schema.field("hashAlgorithm", ALGORITHM_IDENTIFIER),
|
|
133
159
|
schema.field("issuerNameHash", schema.octetString()),
|
|
@@ -149,7 +175,7 @@ var CERT_ID = schema.seq([
|
|
|
149
175
|
// ---- response tree ---------------------------------------------------
|
|
150
176
|
|
|
151
177
|
// RevokedInfo ::= SEQUENCE { revocationTime GeneralizedTime, revocationReason [0]
|
|
152
|
-
// EXPLICIT CRLReason OPTIONAL } (RFC 6960
|
|
178
|
+
// EXPLICIT CRLReason OPTIONAL } (RFC 6960 sec. 4.2.1). Walked on the [1] IMPLICIT node,
|
|
153
179
|
// so assert:"constructed" (the context tag replaced the universal SEQUENCE tag).
|
|
154
180
|
var REVOKED_INFO = schema.seq([
|
|
155
181
|
schema.field("revocationTime", GENERALIZED_TIME),
|
|
@@ -167,7 +193,7 @@ var REVOKED_INFO = schema.seq([
|
|
|
167
193
|
});
|
|
168
194
|
|
|
169
195
|
// CertStatus ::= CHOICE { good [0] IMPLICIT NULL, revoked [1] IMPLICIT RevokedInfo,
|
|
170
|
-
// unknown [2] IMPLICIT NULL } (RFC 6960
|
|
196
|
+
// unknown [2] IMPLICIT NULL } (RFC 6960 sec. 4.2.1). good/unknown are primitive empty
|
|
171
197
|
// context nodes; revoked is a constructed context [1] whose body is RevokedInfo.
|
|
172
198
|
var CERT_STATUS = schema.choice([
|
|
173
199
|
{ when: { tagClass: "context", tagNumber: 0 }, schema: schema.implicitNull(0) },
|
|
@@ -185,7 +211,7 @@ function _shapeCertStatus(field) {
|
|
|
185
211
|
|
|
186
212
|
// SingleResponse ::= SEQUENCE { certID, certStatus, thisUpdate GeneralizedTime,
|
|
187
213
|
// nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, singleExtensions [1] EXPLICIT
|
|
188
|
-
// Extensions OPTIONAL } (RFC 6960
|
|
214
|
+
// Extensions OPTIONAL } (RFC 6960 sec. 4.2.1). certStatus is consumed positionally
|
|
189
215
|
// (before thisUpdate), so its context [0] good arm cannot be confused with the
|
|
190
216
|
// trailing nextUpdate [0].
|
|
191
217
|
var SINGLE_RESPONSE = schema.seq([
|
|
@@ -204,12 +230,12 @@ var SINGLE_RESPONSE = schema.seq([
|
|
|
204
230
|
certStatus: _shapeCertStatus(m.fields.certStatus),
|
|
205
231
|
thisUpdate: m.fields.thisUpdate.value,
|
|
206
232
|
nextUpdate: m.fields.nextUpdate.present ? m.fields.nextUpdate.value : null,
|
|
207
|
-
singleExtensions: m.fields.singleExtensions.present ? m.fields.singleExtensions.value.result : null,
|
|
233
|
+
singleExtensions: m.fields.singleExtensions.present ? _validateOcspExtensions(m.fields.singleExtensions.value.result) : null,
|
|
208
234
|
};
|
|
209
235
|
},
|
|
210
236
|
});
|
|
211
237
|
|
|
212
|
-
// ResponderID ::= CHOICE { byName [1] Name, byKey [2] KeyHash } (RFC 6960
|
|
238
|
+
// ResponderID ::= CHOICE { byName [1] Name, byKey [2] KeyHash } (RFC 6960 sec. 4.2.1) --
|
|
213
239
|
// both EXPLICIT (the module is EXPLICIT TAGS). byKey is an EXPLICIT-wrapped universal
|
|
214
240
|
// OCTET STRING (0xA2 04 ...), NOT an IMPLICIT primitive [2].
|
|
215
241
|
var RESPONDER_ID = schema.choice([
|
|
@@ -219,16 +245,16 @@ var RESPONDER_ID = schema.choice([
|
|
|
219
245
|
|
|
220
246
|
function _shapeResponderID(field) {
|
|
221
247
|
if (field.node.tagNumber === 1) return { byName: field.value.result };
|
|
222
|
-
// RFC 6960
|
|
248
|
+
// RFC 6960 sec. 4.2.1 -- KeyHash is the SHA-1 hash of the responder's public key, so a
|
|
223
249
|
// conformant byKey ResponderID is exactly 20 octets; reject any other length.
|
|
224
250
|
var keyHash = field.value;
|
|
225
|
-
if (keyHash.length !== 20) throw NS.E("ocsp/bad-responder-id", "ResponderID byKey (KeyHash) must be a 20-byte SHA-1 hash (RFC 6960
|
|
251
|
+
if (keyHash.length !== 20) throw NS.E("ocsp/bad-responder-id", "ResponderID byKey (KeyHash) must be a 20-byte SHA-1 hash (RFC 6960 sec. 4.2.1)");
|
|
226
252
|
return { byKey: keyHash };
|
|
227
253
|
}
|
|
228
254
|
|
|
229
255
|
// ResponseData ::= SEQUENCE { version [0] EXPLICIT DEFAULT v1, responderID,
|
|
230
256
|
// producedAt GeneralizedTime, responses SEQUENCE OF SingleResponse,
|
|
231
|
-
// responseExtensions [1] EXPLICIT Extensions OPTIONAL } (RFC 6960
|
|
257
|
+
// responseExtensions [1] EXPLICIT Extensions OPTIONAL } (RFC 6960 sec. 4.2.1).
|
|
232
258
|
var RESPONSE_DATA = schema.seq([
|
|
233
259
|
schema.optional("version", VERSION, { tag: 0, explicit: true, default: 1, emptyCode: "ocsp/bad-version" }),
|
|
234
260
|
schema.field("responderID", RESPONDER_ID),
|
|
@@ -245,15 +271,15 @@ var RESPONSE_DATA = schema.seq([
|
|
|
245
271
|
responderID: _shapeResponderID(m.fields.responderID),
|
|
246
272
|
producedAt: m.fields.producedAt.value,
|
|
247
273
|
responses: m.fields.responses.value.items.map(function (it) { return it.value.result; }),
|
|
248
|
-
responseExtensions: m.fields.responseExtensions.present ? m.fields.responseExtensions.value.result : null,
|
|
274
|
+
responseExtensions: m.fields.responseExtensions.present ? _validateOcspExtensions(m.fields.responseExtensions.value.result) : null,
|
|
249
275
|
};
|
|
250
276
|
},
|
|
251
277
|
});
|
|
252
278
|
|
|
253
279
|
// BasicOCSPResponse ::= SEQUENCE { tbsResponseData ResponseData, signatureAlgorithm
|
|
254
280
|
// AlgorithmIdentifier, signature BIT STRING, certs [0] EXPLICIT SEQUENCE OF
|
|
255
|
-
// Certificate OPTIONAL } (RFC 6960
|
|
256
|
-
// signed region (no CMS-style re-tag divergence
|
|
281
|
+
// Certificate OPTIONAL } (RFC 6960 sec. 4.2.1). tbsResponseData.node.bytes is the exact
|
|
282
|
+
// signed region (no CMS-style re-tag divergence -- the clean case).
|
|
257
283
|
var BASIC_OCSP_RESPONSE = schema.seq([
|
|
258
284
|
schema.field("tbsResponseData", RESPONSE_DATA),
|
|
259
285
|
schema.field("signatureAlgorithm", ALGORITHM_IDENTIFIER),
|
|
@@ -280,7 +306,7 @@ var BASIC_OCSP_RESPONSE = schema.seq([
|
|
|
280
306
|
});
|
|
281
307
|
|
|
282
308
|
// ResponseBytes ::= SEQUENCE { responseType OBJECT IDENTIFIER, response OCTET STRING }
|
|
283
|
-
// (RFC 6960
|
|
309
|
+
// (RFC 6960 sec. 4.2.1). For id-pkix-ocsp-basic the response OCTET STRING content is a
|
|
284
310
|
// fresh DER BasicOCSPResponse (decoded + walked); an unknown responseType is
|
|
285
311
|
// recognized-and-deferred with a precise ocsp/unsupported-response-type.
|
|
286
312
|
var RESPONSE_BYTES = schema.seq([
|
|
@@ -292,7 +318,7 @@ var RESPONSE_BYTES = schema.seq([
|
|
|
292
318
|
var responseType = m.fields.responseType.value;
|
|
293
319
|
var raw = m.fields.response.value;
|
|
294
320
|
if (responseType !== OID_OCSP_BASIC) {
|
|
295
|
-
throw NS.E("ocsp/unsupported-response-type", (ctx.oid.name(responseType) || responseType) + " is not id-pkix-ocsp-basic (RFC 6960
|
|
321
|
+
throw NS.E("ocsp/unsupported-response-type", (ctx.oid.name(responseType) || responseType) + " is not id-pkix-ocsp-basic (RFC 6960 sec. 4.2.1)");
|
|
296
322
|
}
|
|
297
323
|
return {
|
|
298
324
|
responseType: responseType,
|
|
@@ -305,9 +331,11 @@ var RESPONSE_BYTES = schema.seq([
|
|
|
305
331
|
});
|
|
306
332
|
|
|
307
333
|
// OCSPResponse ::= SEQUENCE { responseStatus OCSPResponseStatus, responseBytes [0]
|
|
308
|
-
// EXPLICIT ResponseBytes OPTIONAL } (RFC 6960
|
|
334
|
+
// EXPLICIT ResponseBytes OPTIONAL } (RFC 6960 sec. 4.2.1). The status <-> responseBytes
|
|
309
335
|
// biconditional (successful iff responseBytes present) is the OCSP-specific
|
|
310
|
-
// cross-field guard.
|
|
336
|
+
// cross-field guard. The top-level reject code predates the
|
|
337
|
+
// `<prefix>/not-a-<structure>` convention (ocsp/not-a-request follows it) and
|
|
338
|
+
// stays frozen on the public error-code surface.
|
|
311
339
|
var OCSP_RESPONSE = schema.seq([
|
|
312
340
|
schema.field("responseStatus", OCSP_RESPONSE_STATUS),
|
|
313
341
|
schema.optional("responseBytes", RESPONSE_BYTES, { tag: 0, explicit: true, emptyCode: "ocsp/bad-ocsp-response" }),
|
|
@@ -316,10 +344,10 @@ var OCSP_RESPONSE = schema.seq([
|
|
|
316
344
|
build: function (m) {
|
|
317
345
|
var status = m.fields.responseStatus.value;
|
|
318
346
|
var present = m.fields.responseBytes.present;
|
|
319
|
-
// RFC 6960
|
|
347
|
+
// RFC 6960 sec. 4.2.1 -- a successful response MUST carry responseBytes; any other
|
|
320
348
|
// status MUST NOT (there is nothing to convey but the status).
|
|
321
|
-
if (status.code === 0 && !present) throw NS.E("ocsp/bad-response-bytes", "a successful OCSPResponse must carry responseBytes (RFC 6960
|
|
322
|
-
if (status.code !== 0 && present) throw NS.E("ocsp/bad-response-bytes", "a non-successful OCSPResponse must not carry responseBytes (RFC 6960
|
|
349
|
+
if (status.code === 0 && !present) throw NS.E("ocsp/bad-response-bytes", "a successful OCSPResponse must carry responseBytes (RFC 6960 sec. 4.2.1)");
|
|
350
|
+
if (status.code !== 0 && present) throw NS.E("ocsp/bad-response-bytes", "a non-successful OCSPResponse must not carry responseBytes (RFC 6960 sec. 4.2.1)");
|
|
323
351
|
var rb = present ? m.fields.responseBytes.value.result : null;
|
|
324
352
|
return {
|
|
325
353
|
responseStatus: status,
|
|
@@ -332,7 +360,7 @@ var OCSP_RESPONSE = schema.seq([
|
|
|
332
360
|
// ---- request tree ----------------------------------------------------
|
|
333
361
|
|
|
334
362
|
// Request ::= SEQUENCE { reqCert CertID, singleRequestExtensions [0] EXPLICIT
|
|
335
|
-
// Extensions OPTIONAL } (RFC 6960
|
|
363
|
+
// Extensions OPTIONAL } (RFC 6960 sec. 4.1.1).
|
|
336
364
|
var REQUEST = schema.seq([
|
|
337
365
|
schema.field("reqCert", CERT_ID),
|
|
338
366
|
schema.trailing([
|
|
@@ -343,13 +371,13 @@ var REQUEST = schema.seq([
|
|
|
343
371
|
build: function (m) {
|
|
344
372
|
return {
|
|
345
373
|
certID: m.fields.reqCert.value.result,
|
|
346
|
-
singleRequestExtensions: m.fields.singleRequestExtensions.present ? m.fields.singleRequestExtensions.value.result : null,
|
|
374
|
+
singleRequestExtensions: m.fields.singleRequestExtensions.present ? _validateOcspExtensions(m.fields.singleRequestExtensions.value.result) : null,
|
|
347
375
|
};
|
|
348
376
|
},
|
|
349
377
|
});
|
|
350
378
|
|
|
351
379
|
// Signature ::= SEQUENCE { signatureAlgorithm AlgorithmIdentifier, signature BIT
|
|
352
|
-
// STRING, certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } (RFC 6960
|
|
380
|
+
// STRING, certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } (RFC 6960 sec. 4.1.1).
|
|
353
381
|
var SIGNATURE = schema.seq([
|
|
354
382
|
schema.field("signatureAlgorithm", ALGORITHM_IDENTIFIER),
|
|
355
383
|
schema.field("signature", schema.bitString()),
|
|
@@ -369,7 +397,7 @@ var SIGNATURE = schema.seq([
|
|
|
369
397
|
|
|
370
398
|
// TBSRequest ::= SEQUENCE { version [0] EXPLICIT DEFAULT v1, requestorName [1]
|
|
371
399
|
// EXPLICIT GeneralName OPTIONAL, requestList SEQUENCE OF Request, requestExtensions
|
|
372
|
-
// [2] EXPLICIT Extensions OPTIONAL } (RFC 6960
|
|
400
|
+
// [2] EXPLICIT Extensions OPTIONAL } (RFC 6960 sec. 4.1.1). requestorName is surfaced
|
|
373
401
|
// raw (GeneralName is a wide CHOICE with no factory yet).
|
|
374
402
|
var TBS_REQUEST = schema.seq([
|
|
375
403
|
schema.optional("version", VERSION, { tag: 0, explicit: true, default: 1, emptyCode: "ocsp/bad-version" }),
|
|
@@ -386,13 +414,13 @@ var TBS_REQUEST = schema.seq([
|
|
|
386
414
|
version: m.fields.version.value,
|
|
387
415
|
requestorName: rn.present ? { bytes: rn.value.bytes, tagClass: rn.value.tagClass, tagNumber: rn.value.tagNumber } : null,
|
|
388
416
|
requestList: m.fields.requestList.value.items.map(function (it) { return it.value.result; }),
|
|
389
|
-
requestExtensions: m.fields.requestExtensions.present ? m.fields.requestExtensions.value.result : null,
|
|
417
|
+
requestExtensions: m.fields.requestExtensions.present ? _validateOcspExtensions(m.fields.requestExtensions.value.result) : null,
|
|
390
418
|
};
|
|
391
419
|
},
|
|
392
420
|
});
|
|
393
421
|
|
|
394
422
|
// OCSPRequest ::= SEQUENCE { tbsRequest TBSRequest, optionalSignature [0] EXPLICIT
|
|
395
|
-
// Signature OPTIONAL } (RFC 6960
|
|
423
|
+
// Signature OPTIONAL } (RFC 6960 sec. 4.1.1). tbsRequest.node.bytes is the raw signed
|
|
396
424
|
// region for external verification.
|
|
397
425
|
var OCSP_REQUEST = schema.seq([
|
|
398
426
|
schema.field("tbsRequest", TBS_REQUEST),
|
|
@@ -402,11 +430,11 @@ var OCSP_REQUEST = schema.seq([
|
|
|
402
430
|
build: function (m) {
|
|
403
431
|
var tbs = m.fields.tbsRequest.value.result;
|
|
404
432
|
var optionalSignature = m.fields.optionalSignature.present ? m.fields.optionalSignature.value.result : null;
|
|
405
|
-
// RFC 6960
|
|
433
|
+
// RFC 6960 sec. 4.1.2 -- if the request is signed, the requestor SHALL specify its
|
|
406
434
|
// name in requestorName (the identity the signature binds to). A signed request
|
|
407
435
|
// without it is internally inconsistent; reject fail-closed.
|
|
408
436
|
if (optionalSignature && tbs.requestorName === null) {
|
|
409
|
-
throw NS.E("ocsp/missing-requestor-name", "a signed OCSPRequest must specify requestorName (RFC 6960
|
|
437
|
+
throw NS.E("ocsp/missing-requestor-name", "a signed OCSPRequest must specify requestorName (RFC 6960 sec. 4.1.2)");
|
|
410
438
|
}
|
|
411
439
|
return {
|
|
412
440
|
tbsRequestBytes: m.fields.tbsRequest.node.bytes,
|
|
@@ -482,6 +510,23 @@ var parseResponse = pkix.makeParser({ pemLabel: "OCSP RESPONSE", PemError: PemEr
|
|
|
482
510
|
*/
|
|
483
511
|
function pemDecode(text, label) { return pkix.pemDecode(text, label || "OCSP RESPONSE", PemError); }
|
|
484
512
|
|
|
513
|
+
/**
|
|
514
|
+
* @primitive pki.schema.ocsp.pemEncode
|
|
515
|
+
* @signature pki.schema.ocsp.pemEncode(der, label?) -> string
|
|
516
|
+
* @since 0.1.23
|
|
517
|
+
* @status stable
|
|
518
|
+
* @spec RFC 7468, RFC 6960
|
|
519
|
+
* @related pki.schema.ocsp.pemDecode
|
|
520
|
+
*
|
|
521
|
+
* Wrap DER bytes in a PEM envelope (default label `OCSP RESPONSE`, the same
|
|
522
|
+
* default `pemDecode` reads; pass `"OCSP REQUEST"` for a request). Throws
|
|
523
|
+
* `PemError` on a malformed label.
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* var pem = pki.schema.ocsp.pemEncode(der);
|
|
527
|
+
*/
|
|
528
|
+
function pemEncode(der, label) { return pkix.pemEncode(der, label || "OCSP RESPONSE", PemError); }
|
|
529
|
+
|
|
485
530
|
// An OCSPResponse root is the only registered structure that leads with an
|
|
486
531
|
// ENUMERATED child (a SEQUENCE of 1-2: responseStatus + an optional context [0]
|
|
487
532
|
// responseBytes). Every other root leads with a SEQUENCE (x509/crl/csr/ocsp-request),
|
|
@@ -499,7 +544,7 @@ function matchesResponse(root) {
|
|
|
499
544
|
// An OCSPRequest root is a SEQUENCE of 1-2 whose first child is the tbsRequest
|
|
500
545
|
// SEQUENCE (optionally followed by a context [0] EXPLICIT signature). It leads with
|
|
501
546
|
// a SEQUENCE like x509/crl/csr, but those require EXACTLY 3 children (the
|
|
502
|
-
// signed-envelope trio), while an OCSPRequest is 1-2
|
|
547
|
+
// signed-envelope trio), while an OCSPRequest is 1-2 -- so it is excluded by arity.
|
|
503
548
|
function matchesRequest(root) {
|
|
504
549
|
var T = asn1.TAGS;
|
|
505
550
|
var k = pkix.rootSequenceChildren(root, 1, 2);
|
|
@@ -513,6 +558,7 @@ module.exports = {
|
|
|
513
558
|
parseRequest: parseRequest,
|
|
514
559
|
parseResponse: parseResponse,
|
|
515
560
|
pemDecode: pemDecode,
|
|
561
|
+
pemEncode: pemEncode,
|
|
516
562
|
matchesRequest: matchesRequest,
|
|
517
563
|
matchesResponse: matchesResponse,
|
|
518
564
|
};
|