@blamejs/pki 0.8.55 → 0.8.57
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 +94 -0
- package/README.md +1 -1
- package/lib/acme.js +2 -2
- package/lib/asn1-der.js +17 -5
- package/lib/attrcert-verify.js +1 -1
- package/lib/cmc-verify.js +1 -1
- package/lib/cmp-session.js +2 -2
- package/lib/cmp-verify.js +2 -2
- package/lib/cms-decrypt.js +4 -1
- package/lib/cms-encrypt.js +4 -1
- package/lib/cms-sign.js +26 -12
- package/lib/cms-verify.js +1 -1
- package/lib/composite-kem.js +30 -12
- package/lib/composite-sig.js +42 -23
- package/lib/constants.js +7 -2
- package/lib/crl-verify.js +1 -1
- package/lib/crmf-sign.js +72 -43
- package/lib/crmf-verify.js +1 -1
- package/lib/csr-verify.js +1 -1
- package/lib/ct.js +2 -2
- package/lib/edwards-point.js +1 -1
- package/lib/est.js +3 -3
- package/lib/guard-async.js +22 -1
- package/lib/guard-bytes.js +6 -3
- package/lib/guard-identifier.js +1 -1
- package/lib/guard-intrinsic.js +474 -0
- package/lib/guard-parsed.js +23 -7
- package/lib/http-digest.js +146 -62
- package/lib/http-transport.js +82 -17
- package/lib/identity-match.js +2 -2
- package/lib/inspect.js +1 -1
- package/lib/lint.js +51 -3
- package/lib/merkle.js +2 -2
- package/lib/ocsp-request-verify.js +1 -1
- package/lib/ocsp.js +53 -22
- package/lib/oid.js +7 -0
- package/lib/path-validate.js +169 -76
- package/lib/pbes2.js +14 -7
- package/lib/schema-attrcert.js +2 -2
- package/lib/schema-cmc.js +2 -2
- package/lib/schema-cmp.js +2 -2
- package/lib/schema-cms.js +115 -10
- package/lib/schema-crmf.js +2 -2
- package/lib/schema-csr.js +2 -2
- package/lib/schema-ocsp.js +41 -6
- package/lib/schema-pkix.js +3 -3
- package/lib/schema-x509.js +2 -2
- package/lib/sigstore.js +1 -1
- package/lib/tlog.js +7 -4
- package/lib/trust.js +187 -104
- package/lib/tsp-sign.js +1 -1
- package/lib/webauthn-mds.js +257 -121
- package/lib/webauthn.js +327 -164
- package/lib/webcrypto.js +10 -3
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/schema-attrcert.js
CHANGED
|
@@ -736,7 +736,7 @@ var decodeExtensions = DOOR.decodeExtensions;
|
|
|
736
736
|
*/
|
|
737
737
|
var decodeExtension = DOOR.decodeExtension;
|
|
738
738
|
|
|
739
|
-
module.exports = {
|
|
739
|
+
module.exports = intrinsic.freeze({
|
|
740
740
|
parse: parse,
|
|
741
741
|
parseV1: parseV1,
|
|
742
742
|
pemDecode: pemDecode,
|
|
@@ -749,4 +749,4 @@ module.exports = {
|
|
|
749
749
|
readGeneralName: readGeneralName,
|
|
750
750
|
validateAttributeValue: validateAttributeValue,
|
|
751
751
|
validateExtensionValue: validateExtensionValue,
|
|
752
|
-
};
|
|
752
|
+
});
|
package/lib/schema-cmc.js
CHANGED
|
@@ -802,7 +802,7 @@ function parse(input, callerOpts) {
|
|
|
802
802
|
return body;
|
|
803
803
|
}
|
|
804
804
|
|
|
805
|
-
module.exports = {
|
|
805
|
+
module.exports = Object.freeze({
|
|
806
806
|
parseEncryptedPop: parseEncryptedPop,
|
|
807
807
|
parseTaggedRequest: parseTaggedRequest,
|
|
808
808
|
parse: parse,
|
|
@@ -810,4 +810,4 @@ module.exports = {
|
|
|
810
810
|
parsePkiResponse: parsePkiResponse,
|
|
811
811
|
STATUS_BY_VALUE: STATUS_BY_VALUE,
|
|
812
812
|
FAIL_INFO_BY_VALUE: FAIL_INFO_BY_VALUE,
|
|
813
|
-
};
|
|
813
|
+
});
|
package/lib/schema-cmp.js
CHANGED
|
@@ -883,7 +883,7 @@ function _readKem(sch, der, code, what) {
|
|
|
883
883
|
function readKemBMParameter(p) { return _readKem(KEM_BM_PARAMETER, p, "cmp/bad-kem-data", "KemBMParameter"); }
|
|
884
884
|
function readKemCiphertextInfo(d) { return _readKem(KEM_CIPHERTEXT_INFO, d, "cmp/bad-kem-ciphertext", "KemCiphertextInfo"); }
|
|
885
885
|
|
|
886
|
-
module.exports = {
|
|
886
|
+
module.exports = Object.freeze({
|
|
887
887
|
parse: parse,
|
|
888
888
|
pemDecode: pemDecode,
|
|
889
889
|
pemEncode: pemEncode,
|
|
@@ -897,4 +897,4 @@ module.exports = {
|
|
|
897
897
|
readName: readName,
|
|
898
898
|
readKemBMParameter: readKemBMParameter,
|
|
899
899
|
readKemCiphertextInfo: readKemCiphertextInfo,
|
|
900
|
-
};
|
|
900
|
+
});
|
package/lib/schema-cms.js
CHANGED
|
@@ -34,8 +34,13 @@
|
|
|
34
34
|
* (sec. 5.4); `authAttrsBytes` plays the same role for the sec. 9.2 MAC input and the
|
|
35
35
|
* RFC 5083 sec. 2.2 AAD. Embedded certificates and CRLs are surfaced as raw DER +
|
|
36
36
|
* their outer tag, validated against the closed CertificateChoices /
|
|
37
|
-
* RevocationInfoChoice tag sets, so an
|
|
38
|
-
*
|
|
37
|
+
* RevocationInfoChoice tag sets, so an out-of-set element fails the parse. A
|
|
38
|
+
* certificate alternative is surfaced raw whatever it holds; a `crls` entry in the
|
|
39
|
+
* `other [1]` arm is read as RFC 5652 sec. 10.2.1 `OtherRevocationInfoFormat`,
|
|
40
|
+
* which names its format and, for RFC 5940 `id-ri-ocsp-response`, carries the
|
|
41
|
+
* embedded OCSP response as a record revocation checking takes directly. An
|
|
42
|
+
* unrecognized format stays opaque; a malformed one fails the parse. DER-only,
|
|
43
|
+
* fail-closed.
|
|
39
44
|
*
|
|
40
45
|
* @card
|
|
41
46
|
* Parse DER / PEM CMS (RFC 5652 / 5083 / 9629) into structured, validated fields:
|
|
@@ -49,6 +54,9 @@ var schema = require("./schema-engine");
|
|
|
49
54
|
var pkix = require("./schema-pkix");
|
|
50
55
|
var guard = require("./guard-all");
|
|
51
56
|
var oid = require("./oid");
|
|
57
|
+
/** @internal The OCSP response schema, for the RFC 5940 `crls` arm. `schema-ocsp` requires neither
|
|
58
|
+
* this module nor anything that reaches it, so this is an ordinary top-of-file require. */
|
|
59
|
+
var ocspSchema = require("./schema-ocsp");
|
|
52
60
|
var frameworkError = require("./framework-error");
|
|
53
61
|
var schemaX509 = require("./schema-x509");
|
|
54
62
|
var schemaCrl = require("./schema-crl");
|
|
@@ -383,6 +391,78 @@ var CRL_CHOICE_TAGS = Object.assign(Object.create(null), {
|
|
|
383
391
|
universal: Object.create(null),
|
|
384
392
|
context: Object.assign(Object.create(null), { 1: true }),
|
|
385
393
|
});
|
|
394
|
+
|
|
395
|
+
/** @internal RFC 5652 sec. 10.2.1's `other` arm:
|
|
396
|
+
*
|
|
397
|
+
* OtherRevocationInfoFormat ::= SEQUENCE { otherRevInfoFormat OBJECT IDENTIFIER, otherRevInfo ANY }
|
|
398
|
+
*
|
|
399
|
+
* Exactly two components. OpenSSL's `crypto/cms/cms_asn1.c` makes `otherRevInfo` optional, so it both
|
|
400
|
+
* accepts and can emit an entry carrying no revocation information at all; the ASN.1 above has no
|
|
401
|
+
* OPTIONAL and the one-element form is refused here. */
|
|
402
|
+
var OTHER_REV_INFO = schema.seq([
|
|
403
|
+
schema.field("otherRevInfoFormat", schema.oidLeaf()),
|
|
404
|
+
schema.field("otherRevInfo", schema.any()),
|
|
405
|
+
], {
|
|
406
|
+
assert: "implicit", implicitTag: 1, arity: { exact: 2 },
|
|
407
|
+
code: "cms/bad-other-rev-info", what: "OtherRevocationInfoFormat",
|
|
408
|
+
build: function (m) {
|
|
409
|
+
return {
|
|
410
|
+
otherRevInfoFormat: m.fields.otherRevInfoFormat.value,
|
|
411
|
+
otherRevInfoNode: m.fields.otherRevInfo.value,
|
|
412
|
+
};
|
|
413
|
+
},
|
|
414
|
+
});
|
|
415
|
+
var OID_ID_RI_OCSP_RESPONSE = oid.byName("id-ri-ocsp-response");
|
|
416
|
+
|
|
417
|
+
/** @internal The crls element mapper, which unlike `rawChoiceElement` needs the walk's own ctx: the
|
|
418
|
+
* `other` arm's interior is decoded under the caller's caps and budget rather than at defaults.
|
|
419
|
+
*
|
|
420
|
+
* The universal arm is surfaced raw as before, with one new refusal: RFC 5940 sec. 2 states that an
|
|
421
|
+
* OCSP response is carried in the `other` arm, so a response placed where a CertificateList belongs
|
|
422
|
+
* is a smuggle. It is detected POSITIVELY, by the OCSP module's own response detector, because the
|
|
423
|
+
* CRL detector answers false for shapes that are not full CertificateLists and "not a CRL" would
|
|
424
|
+
* therefore refuse more than sec. 2 forbids. */
|
|
425
|
+
/** @internal The mapper bound to one walk's ctx, so it fits the one-argument shape a list projection
|
|
426
|
+
* passes. The ctx is the walk's own, not a fresh one: that is what carries the caller's caps and the
|
|
427
|
+
* budget already being spent into the embedded response. */
|
|
428
|
+
function _mapCrlsWith(ctx, code, what) {
|
|
429
|
+
var mapper = crlChoiceElement(code, what);
|
|
430
|
+
return function (item) { return mapper(item, ctx); };
|
|
431
|
+
}
|
|
432
|
+
function crlChoiceElement(code, what) {
|
|
433
|
+
return function (item, ctx) {
|
|
434
|
+
var n = item.node;
|
|
435
|
+
var byClass = CRL_CHOICE_TAGS[n.tagClass];
|
|
436
|
+
if (!n.constructed || !byClass || byClass[n.tagNumber] !== true) {
|
|
437
|
+
throw NS.E(code, what + " element tag is outside the closed CHOICE set (RFC 5652 sec. 10.2.1-sec. 10.2.2)");
|
|
438
|
+
}
|
|
439
|
+
var out = rawElement(item);
|
|
440
|
+
if (n.tagClass !== "context") {
|
|
441
|
+
if (ocspSchema.matchesResponse(n)) {
|
|
442
|
+
throw NS.E("cms/misplaced-ocsp-response", "an OCSP response is carried in the other [1] " +
|
|
443
|
+
"RevocationInfoChoice arm under id-ri-ocsp-response, not in the CertificateList arm (RFC 5940 sec. 2)");
|
|
444
|
+
}
|
|
445
|
+
return out;
|
|
446
|
+
}
|
|
447
|
+
var other = schema.walk(OTHER_REV_INFO, n, ctx).result;
|
|
448
|
+
out.otherRevInfoFormat = other.otherRevInfoFormat;
|
|
449
|
+
out.otherRevInfoFormatName = ctx.oid.name(other.otherRevInfoFormat) || null;
|
|
450
|
+
out.otherRevInfoBytes = other.otherRevInfoNode.bytes;
|
|
451
|
+
out.ocspResponse = null;
|
|
452
|
+
if (other.otherRevInfoFormat === OID_ID_RI_OCSP_RESPONSE) {
|
|
453
|
+
var resp = ocspSchema.walkOcspResponse(other.otherRevInfoNode, ctx);
|
|
454
|
+
/** @internal RFC 5940 sec. 3: the carried response states a status. A `tryLater` or `malformed`
|
|
455
|
+
* OCSPResponse is a VALID OCSPResponse that conveys no revocation information, so it is refused
|
|
456
|
+
* HERE rather than in the OCSP module, whose standalone door must keep accepting it. */
|
|
457
|
+
if (resp.responseStatus.code !== 0) {
|
|
458
|
+
throw NS.E("cms/bad-ocsp-rev-info", "an id-ri-ocsp-response revocation entry carries a " +
|
|
459
|
+
resp.responseStatus.name + " OCSP response, which states no revocation status (RFC 5940 sec. 3)");
|
|
460
|
+
}
|
|
461
|
+
out.ocspResponse = resp;
|
|
462
|
+
}
|
|
463
|
+
return out;
|
|
464
|
+
};
|
|
465
|
+
}
|
|
386
466
|
CERT_CHOICE_TAGS.universal[asn1.TAGS.SEQUENCE] = true;
|
|
387
467
|
CRL_CHOICE_TAGS.universal[asn1.TAGS.SEQUENCE] = true;
|
|
388
468
|
function rawChoiceElement(allowed, code, what) {
|
|
@@ -648,7 +728,8 @@ var SIGNED_DATA = schema.seq([
|
|
|
648
728
|
var version = m.fields.version.value;
|
|
649
729
|
var encapContentInfo = m.fields.encapContentInfo.value.result;
|
|
650
730
|
var certificates = m.fields.certificates.present ? m.fields.certificates.value.items.map(rawChoiceElement(CERT_CHOICE_TAGS, "cms/bad-certificates", "certificates")) : [];
|
|
651
|
-
var crls = m.fields.crls.present
|
|
731
|
+
var crls = m.fields.crls.present
|
|
732
|
+
? guard.list.copyMap(m.fields.crls.value.items, _mapCrlsWith(ctx, "cms/bad-crls", "crls")) : [];
|
|
652
733
|
var signerInfos = m.fields.signerInfos.value.items.map(function (it) { return it.value.result; });
|
|
653
734
|
|
|
654
735
|
if (encapContentInfo.eContentType !== OID_DATA) {
|
|
@@ -909,10 +990,16 @@ var ORIGINATOR_INFO = schema.seq([
|
|
|
909
990
|
schema.optional("crls", schema.implicitSetOf(1, schema.any(), { min: 1, code: "cms/bad-originator-crls", what: "crls" }), { tag: 1 }),
|
|
910
991
|
], {
|
|
911
992
|
assert: "constructed", code: "cms/bad-originator-info", what: "OriginatorInfo",
|
|
912
|
-
build
|
|
993
|
+
/** @internal The build takes `ctx` because the crls mapper decodes the RFC 5940 arm under the
|
|
994
|
+
* caller's own caps and budget. This one record is shared by EnvelopedData (RFC 5652 sec. 6.1),
|
|
995
|
+
* AuthenticatedData (sec. 9.1) and AuthEnvelopedData (RFC 5083 sec. 2.1), so decoding only in
|
|
996
|
+
* SignedData would leave three of the four content types RFC 5940 sec. 1 names undecoded. */
|
|
997
|
+
build: function (m, ctx) {
|
|
913
998
|
return {
|
|
914
999
|
certs: m.fields.certs.present ? m.fields.certs.value.items.map(rawChoiceElement(CERT_CHOICE_TAGS, "cms/bad-originator-certs", "OriginatorInfo certs")) : [],
|
|
915
|
-
crls: m.fields.crls.present
|
|
1000
|
+
crls: m.fields.crls.present
|
|
1001
|
+
? guard.list.copyMap(m.fields.crls.value.items,
|
|
1002
|
+
_mapCrlsWith(ctx, "cms/bad-originator-crls", "OriginatorInfo crls")) : [],
|
|
916
1003
|
};
|
|
917
1004
|
},
|
|
918
1005
|
});
|
|
@@ -1184,7 +1271,14 @@ var CONTENT_INFO = schema.seq([
|
|
|
1184
1271
|
* unauthAttrs }` (`aead` holds the validated AES-GCM/CCM nonce + ICV length, or
|
|
1185
1272
|
* `null` for an unrecognized algorithm). A KEM recipient (RFC 9629) surfaces as
|
|
1186
1273
|
* `{ type: "ori", oriType, oriValue, kemri }` with the parsed KEMRecipientInfo in
|
|
1187
|
-
* `kemri`.
|
|
1274
|
+
* `kemri`. A `crls` entry is `{ bytes, tagClass, tagNumber }` for a CertificateList;
|
|
1275
|
+
* an entry in the `other [1]` arm additionally carries `otherRevInfoFormat` (dotted),
|
|
1276
|
+
* `otherRevInfoFormatName` (or `null`), `otherRevInfoBytes` (the raw ANY TLV) and,
|
|
1277
|
+
* for RFC 5940 `id-ri-ocsp-response`, `ocspResponse` holding the embedded response
|
|
1278
|
+
* as a record `pki.path.ocspChecker` and `pki.path.verifyOcspResponse` take
|
|
1279
|
+
* directly. A response whose `responseType` is one this build does not decode keeps
|
|
1280
|
+
* its envelope in `ocspResponse` with `basicResponse` null and the body in
|
|
1281
|
+
* `responseBytes.response` as it arrived. `originatorInfo.crls` carries the same shapes. Every result additionally carries `contentType` (the dotted OID) and
|
|
1188
1282
|
* `contentTypeName` (its registry name) naming which of the five shapes was
|
|
1189
1283
|
* dispatched. The raw byte ranges an external verifier hashes (`eContent`,
|
|
1190
1284
|
* `signature`, `signedAttrsBytes`, `authAttrsBytes`, `mac`) are surfaced
|
|
@@ -1337,16 +1431,27 @@ function parseCertsOnly(der, E, prefix, maxCerts, requireCert) {
|
|
|
1337
1431
|
} else if (allCerts.length === 0 && allCrls.length === 0) {
|
|
1338
1432
|
throw E(prefix + "/no-certificates", "a certs-only message must contain at least one certificate or CRL (RFC 8551 sec. 3.8)");
|
|
1339
1433
|
}
|
|
1434
|
+
/** @internal `maxCerts` caps the certificates and CRLs PARSED and RETURNED, which is a resource
|
|
1435
|
+
* bound. It may not decide which messages are accepted. The tag-class test reads a field off an
|
|
1436
|
+
* entry the parse already produced and costs nothing, so it runs over EVERY entry before the cap
|
|
1437
|
+
* is applied: taken after it, a cap leaving no room for the crls dropped a tagged entry silently,
|
|
1438
|
+
* so a capped reader accepted a message an uncapped reader refuses and handed back an empty crls
|
|
1439
|
+
* list where revocation information had been. Only the X.509 and CRL parsing below stays bounded,
|
|
1440
|
+
* which is the work the bound exists for. */
|
|
1441
|
+
for (var ci = 0; ci < allCerts.length; ci++) {
|
|
1442
|
+
if (allCerts[ci].tagClass !== "universal") throw E(prefix + "/bad-certificate-choice", "a certs-only response exchanges plain X.509 certificates; a tagged CertificateChoices alternative is not permitted (RFC 5272)");
|
|
1443
|
+
}
|
|
1444
|
+
for (var cj = 0; cj < allCrls.length; cj++) {
|
|
1445
|
+
if (allCrls[cj].tagClass !== "universal") throw E(prefix + "/bad-crl", "a certs-only response CRL must be a plain X.509 CertificateList, not a tagged otherRevInfo alternative (RFC 5652 sec. 10.2.1)");
|
|
1446
|
+
}
|
|
1340
1447
|
var certs = (maxCerts != null && allCerts.length > maxCerts) ? allCerts.slice(0, maxCerts) : allCerts;
|
|
1341
1448
|
for (var i = 0; i < certs.length; i++) {
|
|
1342
|
-
if (certs[i].tagClass !== "universal") throw E(prefix + "/bad-certificate-choice", "a certs-only response exchanges plain X.509 certificates; a tagged CertificateChoices alternative is not permitted (RFC 5272)");
|
|
1343
1449
|
try { schemaX509.parse(certs[i].bytes); }
|
|
1344
1450
|
catch (e) { throw E(prefix + "/bad-certificate", "a certs-only response carried a non-certificate in its certificates field (RFC 5272 sec. 4.1)", e); }
|
|
1345
1451
|
}
|
|
1346
1452
|
var crlCap = maxCerts != null ? (maxCerts - certs.length) : null;
|
|
1347
1453
|
var crls = (crlCap != null && allCrls.length > crlCap) ? allCrls.slice(0, crlCap) : allCrls;
|
|
1348
1454
|
for (var j = 0; j < crls.length; j++) {
|
|
1349
|
-
if (crls[j].tagClass !== "universal") throw E(prefix + "/bad-crl", "a certs-only response CRL must be a plain X.509 CertificateList, not a tagged otherRevInfo alternative (RFC 5652 sec. 10.2.1)");
|
|
1350
1455
|
try { schemaCrl.parse(crls[j].bytes); }
|
|
1351
1456
|
catch (e) { throw E(prefix + "/bad-crl", "a certs-only response carried a non-CRL in its crls field", e); }
|
|
1352
1457
|
}
|
|
@@ -1465,7 +1570,7 @@ function sameAlgorithmModuloDefaults(a, c) {
|
|
|
1465
1570
|
return ak === ck;
|
|
1466
1571
|
}
|
|
1467
1572
|
|
|
1468
|
-
module.exports = {
|
|
1573
|
+
module.exports = Object.freeze({
|
|
1469
1574
|
parse: parse,
|
|
1470
1575
|
parseAlgorithmProtection: parseAlgorithmProtection,
|
|
1471
1576
|
algorithmProtectionReport: algorithmProtectionReport, // @internal
|
|
@@ -1490,4 +1595,4 @@ module.exports = {
|
|
|
1490
1595
|
AEAD_ALGS: AEAD_ALGS,
|
|
1491
1596
|
AEAD_GCM_ICVLENS: AEAD_GCM_ICVLENS,
|
|
1492
1597
|
AEAD_CCM_ICVLENS: AEAD_CCM_ICVLENS,
|
|
1493
|
-
};
|
|
1598
|
+
});
|
package/lib/schema-crmf.js
CHANGED
|
@@ -515,7 +515,7 @@ var decodeExtensions = DOOR.decodeExtensions;
|
|
|
515
515
|
*/
|
|
516
516
|
var decodeExtension = DOOR.decodeExtension;
|
|
517
517
|
|
|
518
|
-
module.exports = {
|
|
518
|
+
module.exports = intrinsic.freeze({
|
|
519
519
|
parse: parse,
|
|
520
520
|
pemDecode: pemDecode,
|
|
521
521
|
pemEncode: pemEncode,
|
|
@@ -525,4 +525,4 @@ module.exports = {
|
|
|
525
525
|
walkCertReqMessages: walkCertReqMessages,
|
|
526
526
|
walkCertTemplate: walkCertTemplate,
|
|
527
527
|
certReqMessagesSchema: CERT_REQ_MESSAGES,
|
|
528
|
-
};
|
|
528
|
+
});
|
package/lib/schema-csr.js
CHANGED
|
@@ -383,7 +383,7 @@ var decodeExtensions = DOOR.decodeExtensions;
|
|
|
383
383
|
*/
|
|
384
384
|
var decodeExtension = DOOR.decodeExtension;
|
|
385
385
|
|
|
386
|
-
module.exports = {
|
|
386
|
+
module.exports = intrinsic.freeze({
|
|
387
387
|
parse: parse,
|
|
388
388
|
pemDecode: pemDecode,
|
|
389
389
|
pemEncode: pemEncode,
|
|
@@ -391,4 +391,4 @@ module.exports = {
|
|
|
391
391
|
decodeExtensions: decodeExtensions,
|
|
392
392
|
decodeExtension: decodeExtension,
|
|
393
393
|
certificationRequestSchema: CERTIFICATION_REQUEST,
|
|
394
|
-
};
|
|
394
|
+
});
|
package/lib/schema-ocsp.js
CHANGED
|
@@ -40,6 +40,7 @@ var oid = require("./oid");
|
|
|
40
40
|
var constants = require("./constants");
|
|
41
41
|
var frameworkError = require("./framework-error");
|
|
42
42
|
var intrinsic = require("./guard-intrinsic");
|
|
43
|
+
var guard = require("./guard-all");
|
|
43
44
|
var _hasOwn = intrinsic.hasOwn;
|
|
44
45
|
var _String = intrinsic.String;
|
|
45
46
|
|
|
@@ -319,18 +320,24 @@ var RESPONSE_BYTES = schema.seq([
|
|
|
319
320
|
schema.field("response", schema.octetString()),
|
|
320
321
|
], {
|
|
321
322
|
assert: "sequence", arity: { exact: 2 }, code: "ocsp/bad-response-bytes", what: "ResponseBytes",
|
|
323
|
+
/** @internal RFC 6960 sec. 4.2.1 dispatches the body on `responseType`, so a type this build does
|
|
324
|
+
* not read leaves `basicResponse` null and the body in `response` exactly as it arrived. The
|
|
325
|
+
* envelope is the part every reader shares, and which bodies this build decodes is its own support
|
|
326
|
+
* limit rather than a shape rule: `parseResponse` refuses an unread body at its door, where the
|
|
327
|
+
* caller asked for a response to read, and a response carried inside another structure stays
|
|
328
|
+
* readable as far as its envelope goes. A body that claims id-pkix-ocsp-basic is still decoded,
|
|
329
|
+
* and still refused when it is not one. */
|
|
322
330
|
build: function (m, ctx) {
|
|
323
331
|
var responseType = m.fields.responseType.value;
|
|
324
332
|
var raw = m.fields.response.value;
|
|
325
|
-
if (responseType !== OID_OCSP_BASIC) {
|
|
326
|
-
throw NS.E("ocsp/unsupported-response-type", (ctx.oid.name(responseType) || responseType) + " is not id-pkix-ocsp-basic (RFC 6960 sec. 4.2.1)");
|
|
327
|
-
}
|
|
328
333
|
return {
|
|
329
334
|
responseType: responseType,
|
|
330
335
|
responseTypeName: ctx.oid.name(responseType) || null,
|
|
331
336
|
response: raw,
|
|
332
|
-
basicResponse:
|
|
333
|
-
|
|
337
|
+
basicResponse: responseType === OID_OCSP_BASIC
|
|
338
|
+
? schema.embeddedDer(BASIC_OCSP_RESPONSE, raw, ctx,
|
|
339
|
+
{ code: "ocsp/bad-der", what: "BasicOCSPResponse" }).result
|
|
340
|
+
: null,
|
|
334
341
|
};
|
|
335
342
|
},
|
|
336
343
|
});
|
|
@@ -506,7 +513,19 @@ var parseRequest = pkix.makeParser({ pemLabel: "OCSP REQUEST", PemError: PemErro
|
|
|
506
513
|
* above it, or an option outside this set, is refused. A parse that exceeds one is
|
|
507
514
|
* refused with the `/too-large` code of this format.
|
|
508
515
|
*/
|
|
509
|
-
var
|
|
516
|
+
var _parseResponseRecorded = pkix.makeRecordingParser({ pemLabel: "OCSP RESPONSE", PemError: PemError, ErrorClass: OcspError, prefix: "ocsp", what: "OCSP response", topSchema: OCSP_RESPONSE, ns: NS }, "ocspResponse");
|
|
517
|
+
/** @internal The decision is made on `responseType`, the field RFC 6960 sec. 4.2.1 dispatches on,
|
|
518
|
+
* rather than on the absent `basicResponse` that follows from it. A non-successful response carries
|
|
519
|
+
* no responseBytes at all and keeps reaching the caller, which is the status a live responder
|
|
520
|
+
* returns. */
|
|
521
|
+
function parseResponse(input, caps) {
|
|
522
|
+
var out = _parseResponseRecorded(input, caps);
|
|
523
|
+
var rb = out.responseBytes;
|
|
524
|
+
if (rb && rb.responseType !== OID_OCSP_BASIC) {
|
|
525
|
+
throw NS.E("ocsp/unsupported-response-type", (rb.responseTypeName || rb.responseType) + " is not id-pkix-ocsp-basic (RFC 6960 sec. 4.2.1)");
|
|
526
|
+
}
|
|
527
|
+
return out;
|
|
528
|
+
}
|
|
510
529
|
|
|
511
530
|
/**
|
|
512
531
|
* @primitive pki.schema.ocsp.pemDecode
|
|
@@ -580,9 +599,25 @@ function matchesRequest(root) {
|
|
|
580
599
|
return true;
|
|
581
600
|
}
|
|
582
601
|
|
|
602
|
+
/** @internal Walk an ALREADY-DECODED OCSPResponse node under the caller's own parse resources, for a
|
|
603
|
+
* carrier that embeds one: RFC 5940 sec. 2 puts an OCSPResponse in a CMS `crls` field, and the CMS
|
|
604
|
+
* parse reaches it with a node it has already decoded and a budget it is already spending.
|
|
605
|
+
* `parseResponse(bytes)` cannot serve that: it re-decodes from scratch and starts a fresh budget, so
|
|
606
|
+
* a caller's tightened caps would not reach the embedded walk. `withResources` carries the caps and
|
|
607
|
+
* the budget while keeping this module's own error class, so a malformed embedded response still
|
|
608
|
+
* reports an `ocsp/*` code. The record is a recording one, so `guard.parsed.fromTrustedSource`
|
|
609
|
+
* accepts it wherever a parsed response is taken and a rebuilt copy is still refused. The re-decode
|
|
610
|
+
* this walker performs on raw DER is STRICT, unlike the CMS one: an OCSPResponse has no
|
|
611
|
+
* BER-tolerant door. */
|
|
612
|
+
var walkOcspResponse = guard.parsed.recordingWalker("ocspResponse", function (node, ctx) {
|
|
613
|
+
return schema.walk(OCSP_RESPONSE, node, schema.withResources(NS, ctx)).result;
|
|
614
|
+
// allow:bare-decode the re-decode a recording walker performs on raw DER a caller hands it, outside any parse.
|
|
615
|
+
}, function (der) { return asn1.decode(der); });
|
|
616
|
+
|
|
583
617
|
module.exports = {
|
|
584
618
|
parseRequest: parseRequest,
|
|
585
619
|
parseResponse: parseResponse,
|
|
620
|
+
walkOcspResponse: walkOcspResponse,
|
|
586
621
|
pemDecode: pemDecode,
|
|
587
622
|
pemEncode: pemEncode,
|
|
588
623
|
matchesRequest: matchesRequest,
|
package/lib/schema-pkix.js
CHANGED
|
@@ -2630,7 +2630,7 @@ function dnsNameProblem(s) {
|
|
|
2630
2630
|
if (_hasWhitespace(s)) return "whitespace";
|
|
2631
2631
|
if (_charAt(s, 0) === "." || _charAt(s, s.length - 1) === ".") return "leading/trailing dot";
|
|
2632
2632
|
if (_strIndexOf(s, "_") !== -1) return "underscore forbidden in dNSName";
|
|
2633
|
-
var labels =
|
|
2633
|
+
var labels = intrinsic.splitChar(s, 46);
|
|
2634
2634
|
for (var i = 0; i < labels.length; i++) {
|
|
2635
2635
|
var label = labels[i];
|
|
2636
2636
|
if (label.length === 0) return "empty label";
|
|
@@ -2644,7 +2644,7 @@ function dnsNameProblem(s) {
|
|
|
2644
2644
|
return null;
|
|
2645
2645
|
}
|
|
2646
2646
|
|
|
2647
|
-
module.exports = {
|
|
2647
|
+
module.exports = intrinsic.freeze({
|
|
2648
2648
|
dnsNameProblem: dnsNameProblem,
|
|
2649
2649
|
pemDecode: pemDecode,
|
|
2650
2650
|
pemDecodeAll: pemDecodeAll,
|
|
@@ -2720,5 +2720,5 @@ module.exports = {
|
|
|
2720
2720
|
attribute: attribute,
|
|
2721
2721
|
extension: extension,
|
|
2722
2722
|
extensions: extensions,
|
|
2723
|
-
};
|
|
2723
|
+
});
|
|
2724
2724
|
|
package/lib/schema-x509.js
CHANGED
|
@@ -337,7 +337,7 @@ var decodeExtensions = DOOR.decodeExtensions;
|
|
|
337
337
|
*/
|
|
338
338
|
var decodeExtension = DOOR.decodeExtension;
|
|
339
339
|
|
|
340
|
-
module.exports = {
|
|
340
|
+
module.exports = intrinsic.freeze({
|
|
341
341
|
parse: parse,
|
|
342
342
|
pemDecode: pemDecode,
|
|
343
343
|
pemEncode: pemEncode,
|
|
@@ -350,4 +350,4 @@ module.exports = {
|
|
|
350
350
|
* Not on the curated `pki.schema.x509` surface. */
|
|
351
351
|
tbsCertificateSchema: TBS_CERTIFICATE_ALONE,
|
|
352
352
|
certificateSchema: CERTIFICATE,
|
|
353
|
-
};
|
|
353
|
+
});
|
package/lib/sigstore.js
CHANGED
|
@@ -1501,7 +1501,7 @@ var SAN_OID = oid.byName("subjectAltName");
|
|
|
1501
1501
|
* reports for a certificate it cannot read, so the fix adds no new code to the public surface. */
|
|
1502
1502
|
var _sanDecoder = pkix.certExtensionDecoders(
|
|
1503
1503
|
pkix.makeNS("x509", frameworkError.CertificateError, oid)).byOid[SAN_OID];
|
|
1504
|
-
var FULCIO_PREFIX = _join(_arraySlice(
|
|
1504
|
+
var FULCIO_PREFIX = _join(_arraySlice(intrinsic.splitChar(oid.byName("otherName"), 46), 0, -1), ".") + ".";
|
|
1505
1505
|
function _identity(leaf) {
|
|
1506
1506
|
var out = { san: null, extensions: _create(null) };
|
|
1507
1507
|
try {
|
package/lib/tlog.js
CHANGED
|
@@ -391,7 +391,7 @@ function parseNote(input) {
|
|
|
391
391
|
var signedBytes = _subarray(buf, 0, end);
|
|
392
392
|
var rest = _bufToString(_subarray(buf, end + 1), "utf8");
|
|
393
393
|
var signatures = [];
|
|
394
|
-
var lines = rest.length === 0 ? [] :
|
|
394
|
+
var lines = rest.length === 0 ? [] : intrinsic.splitChar(rest, 10);
|
|
395
395
|
/** @internal A note ends with a newline, so the split leaves one empty tail element; anything
|
|
396
396
|
* else after it is a line the format does not have. The drop goes through the capture: dispatching
|
|
397
397
|
* through `Array.prototype.pop` let a replacement pop TWICE, taking the last signature line with the
|
|
@@ -603,7 +603,7 @@ function parseCheckpoint(input) {
|
|
|
603
603
|
var note = parseNote(input);
|
|
604
604
|
var text = note.text;
|
|
605
605
|
/** @internal The text ends with a newline, so the split leaves an empty tail that is not a line. */
|
|
606
|
-
var lines =
|
|
606
|
+
var lines = intrinsic.splitChar(text, 10);
|
|
607
607
|
if (lines.length > 0 && lines[lines.length - 1] === "") _pop(lines);
|
|
608
608
|
if (lines.length < MIN_CHECKPOINT_LINES) {
|
|
609
609
|
throw _err("tlog/bad-checkpoint", "a checkpoint body is an origin, a tree size and a root hash, got " +
|
|
@@ -918,7 +918,7 @@ function _segmentDigits(segment, expectX) {
|
|
|
918
918
|
*/
|
|
919
919
|
function parseTilePath(path) {
|
|
920
920
|
if (typeof path !== "string") throw _err("tlog/bad-input", "a tile path is a string");
|
|
921
|
-
var parts =
|
|
921
|
+
var parts = intrinsic.splitChar(path, 47);
|
|
922
922
|
if (parts.length < 3 || parts[0] !== "tile") {
|
|
923
923
|
throw _err("tlog/bad-tile", "a tile path begins tile/<level>/<index>");
|
|
924
924
|
}
|
|
@@ -1034,7 +1034,10 @@ function parseEntryBundle(bytes) {
|
|
|
1034
1034
|
if (p + 2 > buf.length) {
|
|
1035
1035
|
throw _err("tlog/bad-bundle", "a trailing byte at offset " + p + " is not a whole length prefix");
|
|
1036
1036
|
}
|
|
1037
|
-
|
|
1037
|
+
/** @internal The length prefix comes from the capture. It decides where this entry ends and the
|
|
1038
|
+
* next begins, so a replaced reader hands the bounds check below a length the bundle does not
|
|
1039
|
+
* state and the entry that is parsed is a different run of bytes. */
|
|
1040
|
+
var len = intrinsic.readUInt16BE(buf, p);
|
|
1038
1041
|
p += 2;
|
|
1039
1042
|
if (p + len > buf.length) {
|
|
1040
1043
|
throw _err("tlog/bad-bundle", "the entry at offset " + (p - 2) + " declares " + len +
|