@blamejs/pki 0.6.14 → 0.6.16
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 +27 -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 +140 -286
- 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-smime.js
CHANGED
|
@@ -55,23 +55,12 @@ var TAGS = asn1.TAGS;
|
|
|
55
55
|
|
|
56
56
|
var ALGID = pkix.algorithmIdentifier(NS);
|
|
57
57
|
|
|
58
|
-
// Registry constants -- never a dotted literal in a format module.
|
|
59
58
|
var OID_SHA1 = oid.byName("sha1");
|
|
60
59
|
var OID_SHA256 = oid.byName("sha256");
|
|
61
60
|
var OID_SIGNING_CERTIFICATE = oid.byName("signingCertificate");
|
|
62
61
|
var OID_SIGNING_CERTIFICATE_V2 = oid.byName("signingCertificateV2");
|
|
63
62
|
var OID_SMIME_CAPABILITIES = oid.byName("smimeCapabilities");
|
|
64
63
|
|
|
65
|
-
// IssuerSerial ::= SEQUENCE { issuer GeneralNames, serialNumber CertificateSerialNumber }
|
|
66
|
-
// (RFC 5035 App A). A bare universal SEQUENCE of exactly two fields, distinct
|
|
67
|
-
// from the RFC 5755 attribute-certificate IssuerSerial (three fields, reached
|
|
68
|
-
// IMPLICIT-tagged), so declared per-format. `issuer` composes the shared
|
|
69
|
-
// GeneralNames factory (every CHOICE arm validated + surfaced raw, SIZE 1..MAX)
|
|
70
|
-
// and is surfaced unchanged here: the RFC 5035 sec. 5 directoryName narrowing applies
|
|
71
|
-
// only to a non-attribute (public-key) certificate reference, and an ESSCertID
|
|
72
|
-
// entry may reference an attribute certificate whose issuer is a full GeneralNames.
|
|
73
|
-
// The first cert is known to be the signing public-key cert (RFC 5035 sec. 3), so the
|
|
74
|
-
// directoryName rule is enforced in the SigningCertificate build and not here.
|
|
75
64
|
var GENERAL_NAME_DIRECTORY = 4;
|
|
76
65
|
var ISSUER_SERIAL = schema.seq([
|
|
77
66
|
schema.field("issuer", pkix.generalNames(NS, { code: "smime/bad-general-names" })),
|
|
@@ -87,11 +76,6 @@ var ISSUER_SERIAL = schema.seq([
|
|
|
87
76
|
},
|
|
88
77
|
});
|
|
89
78
|
|
|
90
|
-
// RFC 5035 sec. 5: the signing certificate (certs[0], the public-key cert that made
|
|
91
|
-
// the signature per sec. 3) MUST identify its issuer as exactly one directoryName [4].
|
|
92
|
-
// Additional certs[1..] may reference attribute certificates, whose issuer is a
|
|
93
|
-
// GeneralNames -- left unconstrained (a verifier-tier profile concern). `fail`
|
|
94
|
-
// throws the caller's code.
|
|
95
79
|
function assertSignerIssuerIsDirectoryName(certs, fail) {
|
|
96
80
|
if (!certs.length || !certs[0].issuerSerial) return;
|
|
97
81
|
var names = certs[0].issuerSerial.issuer.names;
|
|
@@ -100,9 +84,6 @@ function assertSignerIssuerIsDirectoryName(certs, fail) {
|
|
|
100
84
|
}
|
|
101
85
|
}
|
|
102
86
|
|
|
103
|
-
// PolicyInformation ::= SEQUENCE { policyIdentifier CertPolicyId,
|
|
104
|
-
// policyQualifiers SEQUENCE OF PolicyQualifierInfo OPTIONAL } (RFC 5280 sec. 4.2.1.4).
|
|
105
|
-
// Decode the policy OID (+ registry name); surface policyQualifiers raw.
|
|
106
87
|
var POLICY_INFORMATION = schema.seq([
|
|
107
88
|
schema.field("policyIdentifier", schema.oidLeaf()),
|
|
108
89
|
schema.optional("policyQualifiers", schema.any(), { whenUniversal: [TAGS.SEQUENCE] }),
|
|
@@ -111,9 +92,6 @@ var POLICY_INFORMATION = schema.seq([
|
|
|
111
92
|
build: function (m, ctx) {
|
|
112
93
|
var qualifiers = null;
|
|
113
94
|
if (m.fields.policyQualifiers.present) {
|
|
114
|
-
// The qualifier body stays raw (per the ESS scope), but the RFC 5280
|
|
115
|
-
// sec. 4.2.1.4 structure is validated fail-closed by the shared assertion, the
|
|
116
|
-
// same PolicyInformation shape the certificatePolicies decoder enforces.
|
|
117
95
|
var q = m.fields.policyQualifiers.node;
|
|
118
96
|
pkix.assertPolicyQualifiers(q, function (msg, cause) { throw ctx.E("smime/bad-policy-information", msg, cause); });
|
|
119
97
|
qualifiers = q.bytes;
|
|
@@ -125,17 +103,11 @@ var POLICY_INFORMATION = schema.seq([
|
|
|
125
103
|
};
|
|
126
104
|
},
|
|
127
105
|
});
|
|
128
|
-
// policies SEQUENCE OF PolicyInformation OPTIONAL -- order-preserving; the ASN.1
|
|
129
|
-
// module carries no SIZE bound, so an explicitly present but empty list is legal.
|
|
130
106
|
var POLICIES = schema.seqOf(POLICY_INFORMATION, {
|
|
131
107
|
assert: "sequence", min: 0, code: "smime/bad-policies", what: "policies",
|
|
132
108
|
build: function (m) { return m.items.map(function (it) { return it.value.result; }); },
|
|
133
109
|
});
|
|
134
110
|
|
|
135
|
-
// ESSCertID ::= SEQUENCE { certHash Hash, issuerSerial IssuerSerial OPTIONAL }
|
|
136
|
-
// (RFC 5035 sec. 5.4.2). Hash ::= OCTET STRING; for v1 it is the SHA-1 hash of the
|
|
137
|
-
// whole certificate with no algorithm field, so the hash algorithm is synthesized
|
|
138
|
-
// as the implied SHA-1 to make v1 shape-compatible with v2 for a verifier.
|
|
139
111
|
var ESS_CERT_ID = schema.seq([
|
|
140
112
|
schema.field("certHash", schema.octetString()),
|
|
141
113
|
schema.optional("issuerSerial", ISSUER_SERIAL, { whenUniversal: [TAGS.SEQUENCE] }),
|
|
@@ -150,11 +122,6 @@ var ESS_CERT_ID = schema.seq([
|
|
|
150
122
|
},
|
|
151
123
|
});
|
|
152
124
|
|
|
153
|
-
// ESSCertIDv2 ::= SEQUENCE { hashAlgorithm AlgorithmIdentifier DEFAULT
|
|
154
|
-
// {algorithm id-sha256}, certHash Hash, issuerSerial IssuerSerial OPTIONAL }
|
|
155
|
-
// (RFC 5035 sec. 5.4.1 / sec. 4). The mandatory certHash OCTET STRING pivots between the
|
|
156
|
-
// two optional SEQUENCEs, so a leading SEQUENCE is hashAlgorithm and a leading
|
|
157
|
-
// OCTET STRING means hashAlgorithm defaulted.
|
|
158
125
|
var ESS_CERT_ID_V2 = schema.seq([
|
|
159
126
|
schema.optional("hashAlgorithm", ALGID, { whenUniversal: [TAGS.SEQUENCE] }),
|
|
160
127
|
schema.field("certHash", schema.octetString()),
|
|
@@ -165,12 +132,6 @@ var ESS_CERT_ID_V2 = schema.seq([
|
|
|
165
132
|
var hashAlgorithm;
|
|
166
133
|
if (m.fields.hashAlgorithm.present) {
|
|
167
134
|
var alg = m.fields.hashAlgorithm.value.result;
|
|
168
|
-
// X.690 sec. 11.5: a DEFAULT value MUST be omitted in DER. The default is
|
|
169
|
-
// {algorithm id-sha256, parameters ABSENT}; an explicit encoding byte-equal
|
|
170
|
-
// to it is a non-canonical DEFAULT and is rejected fail-closed (the
|
|
171
|
-
// structured-value analogue of the primitive-DEFAULT rejection the toolkit
|
|
172
|
-
// already enforces). A present hashAlgorithm carrying a redundant NULL
|
|
173
|
-
// parameters is not byte-equal to the params-absent default and decodes.
|
|
174
135
|
if (alg.oid === OID_SHA256 && alg.parameters === null) {
|
|
175
136
|
throw ctx.E("smime/non-canonical-default",
|
|
176
137
|
"ESSCertIDv2 hashAlgorithm equal to the DEFAULT {algorithm id-sha256} MUST be omitted (X.690 sec. 11.5)");
|
|
@@ -187,10 +148,6 @@ var ESS_CERT_ID_V2 = schema.seq([
|
|
|
187
148
|
},
|
|
188
149
|
});
|
|
189
150
|
|
|
190
|
-
// SigningCertificate ::= SEQUENCE { certs SEQUENCE OF ESSCertID,
|
|
191
|
-
// policies SEQUENCE OF PolicyInformation OPTIONAL } (RFC 5035 sec. 5.4.2). certs is
|
|
192
|
-
// order-preserving (RFC 5035 sec. 3 makes the first element the signing certificate)
|
|
193
|
-
// and non-empty (an empty certs cannot name a signing cert).
|
|
194
151
|
function signingCertificateSchema(essCertId, code, what) {
|
|
195
152
|
return schema.seq([
|
|
196
153
|
schema.field("certs", schema.seqOf(essCertId, { assert: "sequence", min: 1, code: "smime/bad-certs", what: "certs" })),
|
|
@@ -210,10 +167,6 @@ function signingCertificateSchema(essCertId, code, what) {
|
|
|
210
167
|
var SIGNING_CERTIFICATE = signingCertificateSchema(ESS_CERT_ID, "smime/bad-signing-certificate", "SigningCertificate");
|
|
211
168
|
var SIGNING_CERTIFICATE_V2 = signingCertificateSchema(ESS_CERT_ID_V2, "smime/bad-signing-certificate-v2", "SigningCertificateV2");
|
|
212
169
|
|
|
213
|
-
// SMIMECapability ::= SEQUENCE { capabilityID OBJECT IDENTIFIER,
|
|
214
|
-
// parameters ANY DEFINED BY capabilityID OPTIONAL } (RFC 8551 sec. 2.5.2). Distinct
|
|
215
|
-
// domain from AlgorithmIdentifier though isomorphic -- surfaced as capabilityID +
|
|
216
|
-
// raw parameters (the ANY-DEFINED-BY interpretation is a negotiation concern).
|
|
217
170
|
var SMIME_CAPABILITY = schema.seq([
|
|
218
171
|
schema.field("capabilityID", schema.oidLeaf()),
|
|
219
172
|
schema.optional("parameters", schema.any(), { whenAny: true }),
|
|
@@ -227,8 +180,6 @@ var SMIME_CAPABILITY = schema.seq([
|
|
|
227
180
|
};
|
|
228
181
|
},
|
|
229
182
|
});
|
|
230
|
-
// SMIMECapabilities ::= SEQUENCE OF SMIMECapability, ordered by preference
|
|
231
|
-
// (RFC 8551 sec. 2.5.2), never sorted; an empty list is legal.
|
|
232
183
|
var SMIME_CAPABILITIES = schema.seqOf(SMIME_CAPABILITY, {
|
|
233
184
|
assert: "sequence", min: 0, code: "smime/bad-capabilities", what: "SMIMECapabilities",
|
|
234
185
|
build: function (m) { return { capabilities: m.items.map(function (it) { return it.value.result; }) }; },
|
|
@@ -334,11 +285,6 @@ function decodeAttribute(attr) {
|
|
|
334
285
|
if (!attr || typeof attr !== "object" || typeof attr.type !== "string" || !Array.isArray(attr.values)) {
|
|
335
286
|
throw new SmimeError("smime/bad-input", "decodeAttribute expects a CMS attribute { type, values }");
|
|
336
287
|
}
|
|
337
|
-
// Route on the attribute OID FIRST. The single-AttributeValue MUST (RFC 8551
|
|
338
|
-
// sec. 2.5.2) is specific to the ESS / SMIMECapabilities attributes, so it is
|
|
339
|
-
// enforced only inside the known-type branch; an unknown / custom attribute
|
|
340
|
-
// recognize-and-defers with its raw values intact regardless of value count
|
|
341
|
-
// (its own cardinality rules are not this decoder's to enforce).
|
|
342
288
|
if (attr.type === OID_SIGNING_CERTIFICATE || attr.type === OID_SIGNING_CERTIFICATE_V2 || attr.type === OID_SMIME_CAPABILITIES) {
|
|
343
289
|
if (attr.values.length !== 1) {
|
|
344
290
|
throw new SmimeError("smime/multi-valued-attribute",
|
package/lib/schema-tsp.js
CHANGED
|
@@ -49,47 +49,28 @@ var NS = pkix.makeNS("tsp", TspError, oid);
|
|
|
49
49
|
var ALGORITHM_IDENTIFIER = pkix.algorithmIdentifier(NS);
|
|
50
50
|
var EXTENSION = pkix.extension(NS);
|
|
51
51
|
|
|
52
|
-
// TSTInfo.version is INTEGER { v1(1) }; the only legal value is 1.
|
|
53
52
|
var VERSION = pkix.versionReader(NS, { "1": 1 });
|
|
54
53
|
|
|
55
|
-
// id-ct-TSTInfo is the eContentType that identifies a timestamp token (RFC 3161
|
|
56
|
-
// sec. 2.4.2); resolved from the registry, never a dotted literal.
|
|
57
54
|
var OID_TST_INFO = oid.byName("tSTInfo");
|
|
58
55
|
|
|
59
|
-
// The ESS certificate-identifier attributes: RFC 3161 sec. 2.4.2 requires the TSA
|
|
60
|
-
// certificate identifier as a SigningCertificate signed attribute; RFC 5816
|
|
61
|
-
// additionally permits SigningCertificateV2.
|
|
62
56
|
var OID_SIGNING_CERT = oid.byName("signingCertificate");
|
|
63
57
|
var OID_SIGNING_CERT_V2 = oid.byName("signingCertificateV2");
|
|
64
58
|
|
|
65
59
|
var TAGS = asn1.TAGS;
|
|
66
60
|
|
|
67
|
-
// PKIFailureInfo ::= BIT STRING NamedBitList (RFC 3161 sec. 2.4.2). bit 0 is the MSB of
|
|
68
|
-
// the first content octet; a set bit outside the named set is surfaced by index.
|
|
69
61
|
var FAILURE_BITS = {
|
|
70
62
|
0: "badAlg", 2: "badRequest", 5: "badDataFormat", 14: "timeNotAvailable",
|
|
71
63
|
15: "unacceptedPolicy", 16: "unacceptedExtension", 17: "addInfoNotAvailable", 25: "systemFailure",
|
|
72
64
|
};
|
|
73
65
|
|
|
74
|
-
// ---- shared leaves ---------------------------------------------------
|
|
75
66
|
|
|
76
|
-
// GeneralizedTime-only leaf. RFC 3161 sec. 2.4.2 requires genTime to be GeneralizedTime,
|
|
77
|
-
// never UTCTime; assert the tag before the (fractional-capable) codec read.
|
|
78
|
-
// RFC 3161 genTime may carry sub-second precision (X.690 sec. 11.7 fractional profile).
|
|
79
67
|
var GEN_TIME = pkix.generalizedTime(NS, { code: "tsp/bad-gentime", message: "genTime must be a GeneralizedTime (RFC 3161 sec. 2.4.2)", allowFractional: true });
|
|
80
68
|
|
|
81
|
-
// tsa [0] EXPLICIT GeneralName (RFC 3161 sec. 2.4.2), validated and surfaced raw via the
|
|
82
|
-
// shared pkix.generalName primitive (RFC 5280 sec. 4.2.1.6), which checks the chosen
|
|
83
|
-
// alternative's form and content so a malformed GeneralName fails closed.
|
|
84
69
|
var GENERAL_NAME_RAW = pkix.generalName(NS, { code: "tsp/bad-tsa" });
|
|
85
70
|
|
|
86
|
-
// A PKIFreeText element MUST be a UTF8String (RFC 3161 sec. 2.4.2).
|
|
87
71
|
var UTF8_TEXT = pkix.utf8Text(NS, { code: "tsp/bad-status-info", message: "PKIFreeText elements must be UTF8String" });
|
|
88
72
|
|
|
89
|
-
// ---- MessageImprint --------------------------------------------------
|
|
90
73
|
|
|
91
|
-
// MessageImprint ::= SEQUENCE { hashAlgorithm AlgorithmIdentifier, hashedMessage
|
|
92
|
-
// OCTET STRING } (RFC 3161 sec. 2.4.1). hashedMessage is a digest, surfaced raw.
|
|
93
74
|
var MESSAGE_IMPRINT = schema.seq([
|
|
94
75
|
schema.field("hashAlgorithm", ALGORITHM_IDENTIFIER),
|
|
95
76
|
schema.field("hashedMessage", schema.octetString()),
|
|
@@ -100,11 +81,7 @@ var MESSAGE_IMPRINT = schema.seq([
|
|
|
100
81
|
},
|
|
101
82
|
});
|
|
102
83
|
|
|
103
|
-
// ---- Accuracy --------------------------------------------------------
|
|
104
84
|
|
|
105
|
-
// Accuracy ::= SEQUENCE { seconds INTEGER OPTIONAL, millis [0] IMPLICIT INTEGER
|
|
106
|
-
// (1..999) OPTIONAL, micros [1] IMPLICIT INTEGER (1..999) OPTIONAL } (RFC 3161
|
|
107
|
-
// sec. 2.4.2). Every sub-field is optional; a missing one defaults to 0.
|
|
108
85
|
var ACCURACY = schema.seq([
|
|
109
86
|
schema.optional("seconds", schema.integerLeaf(), { whenUniversal: [TAGS.INTEGER] }),
|
|
110
87
|
schema.trailing([
|
|
@@ -117,20 +94,13 @@ var ACCURACY = schema.seq([
|
|
|
117
94
|
function sub(f) {
|
|
118
95
|
if (!f.present) return 0;
|
|
119
96
|
var v = f.value;
|
|
120
|
-
// millis / micros are constrained to 1..999 (RFC 3161 sec. 2.4.2); 0 is also
|
|
121
|
-
// excluded (and non-canonical for a positive count).
|
|
122
97
|
if (v < 1n || v > 999n) throw NS.E("tsp/bad-accuracy", "Accuracy millis/micros must be in 1..999");
|
|
123
98
|
return Number(v);
|
|
124
99
|
}
|
|
125
|
-
// Accuracy is the +/- deviation added to and subtracted from genTime (RFC
|
|
126
|
-
// 3161 sec. 2.4.2), so a negative seconds inverts the time window a verifier
|
|
127
|
-
// computes -- the same semantically-impossible class as a 0 millis/micros.
|
|
128
100
|
if (m.fields.seconds.present && m.fields.seconds.value < 0n) {
|
|
129
101
|
throw NS.E("tsp/bad-accuracy", "Accuracy seconds must not be negative (RFC 3161 sec. 2.4.2)");
|
|
130
102
|
}
|
|
131
103
|
return {
|
|
132
|
-
// seconds is an unbounded INTEGER -- keep it lossless as a BigInt (millis /
|
|
133
|
-
// micros are constrained to 1..999, so Number is exact for those).
|
|
134
104
|
seconds: m.fields.seconds.present ? m.fields.seconds.value : 0n,
|
|
135
105
|
millis: sub(m.fields.millis),
|
|
136
106
|
micros: sub(m.fields.micros),
|
|
@@ -138,12 +108,7 @@ var ACCURACY = schema.seq([
|
|
|
138
108
|
},
|
|
139
109
|
});
|
|
140
110
|
|
|
141
|
-
// ---- TSTInfo ---------------------------------------------------------
|
|
142
111
|
|
|
143
|
-
// TSTInfo ::= SEQUENCE { version, policy, messageImprint, serialNumber, genTime,
|
|
144
|
-
// accuracy OPTIONAL, ordering DEFAULT FALSE, nonce OPTIONAL, tsa [0] EXPLICIT
|
|
145
|
-
// GeneralName OPTIONAL, extensions [1] IMPLICIT Extensions OPTIONAL } (RFC 3161
|
|
146
|
-
// sec. 2.4.2). The five mandatory fields come first; the optionals are consumed by tag.
|
|
147
112
|
var TST_INFO = schema.seq([
|
|
148
113
|
schema.field("version", VERSION),
|
|
149
114
|
schema.field("policy", schema.oidLeaf()),
|
|
@@ -160,7 +125,6 @@ var TST_INFO = schema.seq([
|
|
|
160
125
|
], {
|
|
161
126
|
assert: "sequence", code: "tsp/bad-tst-info", what: "TSTInfo",
|
|
162
127
|
build: function (m, ctx) {
|
|
163
|
-
// ordering BOOLEAN DEFAULT FALSE -- an explicit FALSE must be omitted (DER).
|
|
164
128
|
if (m.fields.ordering.present && m.fields.ordering.value === false) {
|
|
165
129
|
throw NS.E("tsp/bad-ordering", "ordering is BOOLEAN DEFAULT FALSE -- an explicit FALSE must be omitted");
|
|
166
130
|
}
|
|
@@ -174,11 +138,7 @@ var TST_INFO = schema.seq([
|
|
|
174
138
|
serialNumber: m.fields.serialNumber.value,
|
|
175
139
|
serialNumberHex: m.fields.serialNumber.node.content.toString("hex"),
|
|
176
140
|
genTime: m.fields.genTime.value,
|
|
177
|
-
// genTime is surfaced as a millisecond-precision Date; genTimeFraction is the
|
|
178
|
-
// exact fractional-seconds digits (lossless for sub-millisecond precision), or
|
|
179
|
-
// null when genTime carries no fraction.
|
|
180
141
|
genTimeFraction: (function () {
|
|
181
|
-
// /\.(\d+)Z$/ : the fractional-seconds digits before a trailing "Z" (the "." and "Z" excluded), or null.
|
|
182
142
|
var s = m.fields.genTime.node.content.toString("latin1"), n = s.length;
|
|
183
143
|
if (n < 3 || s.charAt(n - 1) !== "Z") return null;
|
|
184
144
|
var dot = s.lastIndexOf(".");
|
|
@@ -197,10 +157,7 @@ var TST_INFO = schema.seq([
|
|
|
197
157
|
},
|
|
198
158
|
});
|
|
199
159
|
|
|
200
|
-
// ---- PKIStatusInfo / TimeStampResp -----------------------------------
|
|
201
160
|
|
|
202
|
-
// PKIStatusInfo ::= SEQUENCE { status PKIStatus (INTEGER), statusString PKIFreeText
|
|
203
|
-
// OPTIONAL, failInfo PKIFailureInfo OPTIONAL } (RFC 3161 sec. 2.4.2, from RFC 4210).
|
|
204
161
|
var PKI_STATUS_INFO = schema.seq([
|
|
205
162
|
schema.field("status", schema.integerLeaf()),
|
|
206
163
|
schema.optional("statusString", schema.seqOf(UTF8_TEXT, { assert: "sequence", min: 1, code: "tsp/bad-status-info", what: "statusString" }), { whenUniversal: [TAGS.SEQUENCE] }),
|
|
@@ -209,7 +166,6 @@ var PKI_STATUS_INFO = schema.seq([
|
|
|
209
166
|
assert: "sequence", code: "tsp/bad-status-info", what: "PKIStatusInfo",
|
|
210
167
|
build: function (m) {
|
|
211
168
|
var status = m.fields.status.value;
|
|
212
|
-
// PKIStatus ::= INTEGER { granted(0) .. revocationNotification(5) }.
|
|
213
169
|
if (status < 0n || status > 5n) throw NS.E("tsp/bad-status", "PKIStatus " + status + " is outside 0..5 (RFC 3161 sec. 2.4.2)");
|
|
214
170
|
var failInfo = null;
|
|
215
171
|
if (m.fields.failInfo.present) {
|
|
@@ -225,18 +181,10 @@ var PKI_STATUS_INFO = schema.seq([
|
|
|
225
181
|
},
|
|
226
182
|
});
|
|
227
183
|
|
|
228
|
-
// X.690 sec. 11.2.2 -- a BIT STRING typed with a NamedBitList (PKIFailureInfo) MUST have
|
|
229
|
-
// all trailing 0 bits removed under DER: no trailing all-zero content octet, and the
|
|
230
|
-
// declared unusedBits must sit exactly below the lowest set bit of the last octet (so
|
|
231
|
-
// the encoding is minimal). The empty value encodes as 0 content octets, 0 unusedBits.
|
|
232
184
|
function _assertMinimalNamedBits(unusedBits, bytes) {
|
|
233
185
|
schema.assertMinimalNamedBits(unusedBits, bytes, function (m) { throw NS.E("tsp/bad-failinfo", m); });
|
|
234
186
|
}
|
|
235
187
|
|
|
236
|
-
// Decode the set NamedBitList bits to their RFC 3161 names (bit 0 = MSB of byte 0).
|
|
237
|
-
// A set bit outside the defined set is an unsupported PKIFailureInfo value, and a client
|
|
238
|
-
// MUST error on a failInfo it does not understand (RFC 3161 sec. 2.4.2), so reject it
|
|
239
|
-
// instead of surfacing an opaque "bitN".
|
|
240
188
|
function _namedBits(bytes) {
|
|
241
189
|
var out = [];
|
|
242
190
|
for (var i = 0; i < bytes.length * 8; i++) {
|
|
@@ -249,11 +197,6 @@ function _namedBits(bytes) {
|
|
|
249
197
|
return out;
|
|
250
198
|
}
|
|
251
199
|
|
|
252
|
-
// TimeStampResp ::= SEQUENCE { status PKIStatusInfo, timeStampToken TimeStampToken
|
|
253
|
-
// OPTIONAL } (RFC 3161 sec. 2.4.2). There is NO version field. The token is a CMS
|
|
254
|
-
// ContentInfo, decoded via cms.parse; the status-to-token coupling is load-bearing.
|
|
255
|
-
// The top-level reject code predates the `<prefix>/not-a-<structure>` convention
|
|
256
|
-
// and stays frozen on the public error-code surface.
|
|
257
200
|
var TIME_STAMP_RESP = schema.seq([
|
|
258
201
|
schema.field("status", PKI_STATUS_INFO),
|
|
259
202
|
schema.optional("timeStampToken", schema.any(), { whenUniversal: [TAGS.SEQUENCE] }),
|
|
@@ -262,17 +205,10 @@ var TIME_STAMP_RESP = schema.seq([
|
|
|
262
205
|
build: function (m) {
|
|
263
206
|
var status = m.fields.status.value.result;
|
|
264
207
|
var present = m.fields.timeStampToken.present;
|
|
265
|
-
// RFC 3161 sec. 2.4.2 -- granted / grantedWithMods carry a token; any other status
|
|
266
|
-
// (rejection / waiting / revocation*) MUST NOT.
|
|
267
208
|
var granted = status.status === 0 || status.status === 1;
|
|
268
209
|
if (granted && !present) throw NS.E("tsp/missing-token", "a granted TimeStampResp must carry a timeStampToken (RFC 3161 sec. 2.4.2)");
|
|
269
210
|
if (!granted && present) throw NS.E("tsp/unexpected-token", "a non-granted TimeStampResp must not carry a timeStampToken (RFC 3161 sec. 2.4.2)");
|
|
270
|
-
// failInfo is the reason a request was rejected, so it is present only when the
|
|
271
|
-
// status is rejection(2), never on granted(0/1) nor on waiting / revocation* (3/4/5).
|
|
272
211
|
if (status.status !== 2 && status.failInfo) throw NS.E("tsp/unexpected-failinfo", "failInfo is present only when the status is rejection(2) (RFC 3161 sec. 2.4.2)");
|
|
273
|
-
// A granted response's timeStampToken MUST be a well-formed TimeStampToken, so
|
|
274
|
-
// decode it (composing the CMS parser) instead of surfacing arbitrary SEQUENCE
|
|
275
|
-
// bytes as a token; a malformed token fails the response parse.
|
|
276
212
|
return {
|
|
277
213
|
status: status.status,
|
|
278
214
|
statusString: status.statusString,
|
|
@@ -282,13 +218,7 @@ var TIME_STAMP_RESP = schema.seq([
|
|
|
282
218
|
},
|
|
283
219
|
});
|
|
284
220
|
|
|
285
|
-
// ---- TimeStampReq ----------------------------------------------------
|
|
286
221
|
|
|
287
|
-
// TimeStampReq ::= SEQUENCE { version INTEGER { v1(1) }, messageImprint MessageImprint,
|
|
288
|
-
// reqPolicy TSAPolicyId OPTIONAL, nonce INTEGER OPTIONAL, certReq BOOLEAN DEFAULT FALSE,
|
|
289
|
-
// extensions [0] IMPLICIT Extensions OPTIONAL } (RFC 3161 sec. 2.4.1). version + messageImprint
|
|
290
|
-
// are mandatory; the three optionals are consumed by universal tag (OID / INTEGER / BOOLEAN),
|
|
291
|
-
// then the [0] extensions. certReq DEFAULT FALSE follows the TSTInfo.ordering explicit-FALSE rule.
|
|
292
222
|
var TIME_STAMP_REQ = schema.seq([
|
|
293
223
|
schema.field("version", VERSION),
|
|
294
224
|
schema.field("messageImprint", MESSAGE_IMPRINT),
|
|
@@ -301,7 +231,6 @@ var TIME_STAMP_REQ = schema.seq([
|
|
|
301
231
|
], {
|
|
302
232
|
assert: "sequence", code: "tsp/bad-request", what: "TimeStampReq",
|
|
303
233
|
build: function (m, ctx) {
|
|
304
|
-
// certReq BOOLEAN DEFAULT FALSE -- an explicit FALSE must be omitted under DER.
|
|
305
234
|
if (m.fields.certReq.present && m.fields.certReq.value === false) {
|
|
306
235
|
throw NS.E("tsp/bad-request", "certReq is BOOLEAN DEFAULT FALSE -- an explicit FALSE must be omitted (DER)");
|
|
307
236
|
}
|
|
@@ -437,23 +366,13 @@ var parse = pkix.makeParser({ pemLabel: null, PemError: PemError, ErrorClass: Ts
|
|
|
437
366
|
* token.signerInfo.sid; // -> the TSA signer identifier
|
|
438
367
|
*/
|
|
439
368
|
function parseToken(input) {
|
|
440
|
-
// De-armor with TSP's label-agnostic PEM rules first (RFC 3161 has no standard
|
|
441
|
-
// label), THEN hand DER to the CMS parser -- otherwise cms.parse would reject any
|
|
442
|
-
// PEM block not labeled "CMS" before the TSP checks run.
|
|
443
369
|
var der = pkix.coerceToDer(input, { pemLabel: null, PemError: PemError, ErrorClass: TspError, prefix: "tsp" });
|
|
444
|
-
// Wrap the CMS decode so tsp.parse / parseToken keep their typed-TspError contract --
|
|
445
|
-
// a malformed token surfaces tsp/bad-token, not a bare cms/* error.
|
|
446
370
|
var signed;
|
|
447
371
|
try { signed = cms.parse(der); }
|
|
448
372
|
catch (e) {
|
|
449
373
|
if (e instanceof TspError) throw e;
|
|
450
374
|
throw new TspError("tsp/bad-token", "the timeStampToken did not decode as a CMS SignedData: " + ((e && e.message) || String(e)), e);
|
|
451
375
|
}
|
|
452
|
-
// cms.parse decodes every CMS content type; a TimeStampToken MUST be a
|
|
453
|
-
// SignedData (RFC 3161 sec. 2.4.2). Gate on the dispatched content type before
|
|
454
|
-
// any SignedData-only field access -- an AuthenticatedData / EnvelopedData
|
|
455
|
-
// whose eContentType happens to be id-ct-TSTInfo would otherwise reach a raw
|
|
456
|
-
// TypeError on the absent signerInfos.
|
|
457
376
|
if (signed.contentTypeName !== "signedData") {
|
|
458
377
|
throw new TspError("tsp/not-signed-data", "a TimeStampToken must be a CMS SignedData, got " + (signed.contentTypeName || signed.contentType));
|
|
459
378
|
}
|
|
@@ -465,12 +384,6 @@ function parseToken(input) {
|
|
|
465
384
|
if (signed.signerInfos.length !== 1) {
|
|
466
385
|
throw new TspError("tsp/multi-signer", "a TimeStampToken must contain exactly one (TSA) signerInfo (RFC 3161 sec. 2.4.2)");
|
|
467
386
|
}
|
|
468
|
-
// RFC 3161 sec. 2.4.2 / RFC 5816 -- the TSA certificate identifier MUST be present
|
|
469
|
-
// as a SigningCertificate (or SigningCertificateV2) signed attribute. Presence
|
|
470
|
-
// is a structural token property, same altitude as the single-signer rule; the
|
|
471
|
-
// hash-vs-certificate BINDING stays a verification-layer concern. signedAttrs
|
|
472
|
-
// is non-null here: the CMS parse already required it for a non-id-data
|
|
473
|
-
// eContentType (RFC 5652 sec. 5.3), and the eContentType is id-ct-TSTInfo.
|
|
474
387
|
var signedAttrs = signed.signerInfos[0].signedAttrs || [];
|
|
475
388
|
var hasSigningCert = false;
|
|
476
389
|
for (var a = 0; a < signedAttrs.length; a++) {
|
|
@@ -485,9 +398,6 @@ function parseToken(input) {
|
|
|
485
398
|
if (e instanceof TspError) throw e;
|
|
486
399
|
throw new TspError("tsp/bad-der", "the encapsulated TSTInfo did not decode: " + ((e && e.message) || String(e)), e);
|
|
487
400
|
}
|
|
488
|
-
// Surface the RAW eContent (the exact DER a verifier hashes for the CMS
|
|
489
|
-
// message-digest signed attribute) alongside the decoded TSTInfo -- a re-serialized
|
|
490
|
-
// tstInfo may not byte-match, so the raw bytes are the verification feed.
|
|
491
401
|
return { tstInfo: tstInfo.result, eContent: encap.eContent, signerInfo: signed.signerInfos[0], certificates: signed.certificates };
|
|
492
402
|
}
|
|
493
403
|
|
|
@@ -508,9 +418,7 @@ function parseToken(input) {
|
|
|
508
418
|
* var pemText = pki.schema.tsp.pemEncode(req, "TSP REQUEST");
|
|
509
419
|
* var der = pki.schema.tsp.pemDecode(pemText);
|
|
510
420
|
*/
|
|
511
|
-
//
|
|
512
|
-
// { tagClass, tagNumber, value } -- for a directoryName [4] the value is a decoded Name { rdns },
|
|
513
|
-
// so a verifier canonically compares it (guard.name.dnEqual) rather than by raw bytes. @internal.
|
|
421
|
+
// @internal
|
|
514
422
|
function decodeGeneralName(bytes) {
|
|
515
423
|
return schema.embeddedDer(pkix.generalName(NS, { decodeValue: true, code: "tsp/bad-tsa" }), bytes, NS, { code: "tsp/bad-tsa", what: "GeneralName" });
|
|
516
424
|
}
|
|
@@ -535,10 +443,6 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label || null, Pem
|
|
|
535
443
|
*/
|
|
536
444
|
function pemEncode(der, label) { return pkix.pemEncode(der, label, PemError); }
|
|
537
445
|
|
|
538
|
-
// A TimeStampResp root is a SEQUENCE of 1-2 whose first child is a PKIStatusInfo (a
|
|
539
|
-
// SEQUENCE whose own first child is an INTEGER status). Disjoint from the OID-first
|
|
540
|
-
// CMS ContentInfo, the INTEGER-first PKCS#8 key, and the exactly-3 signed-envelope
|
|
541
|
-
// trio, so it detects unambiguously regardless of registry order.
|
|
542
446
|
function matches(root) {
|
|
543
447
|
var k = pkix.rootSequenceChildren(root, 1, 2);
|
|
544
448
|
if (!k) return false;
|
|
@@ -551,9 +455,9 @@ function matches(root) {
|
|
|
551
455
|
|
|
552
456
|
module.exports = {
|
|
553
457
|
parse: parse,
|
|
554
|
-
parseResponse: parse,
|
|
458
|
+
parseResponse: parse,
|
|
555
459
|
parseRequest: parseRequest,
|
|
556
|
-
decodeGeneralName: decodeGeneralName, // @internal
|
|
460
|
+
decodeGeneralName: decodeGeneralName, // @internal
|
|
557
461
|
parseTstInfo: parseTstInfo,
|
|
558
462
|
parseToken: parseToken,
|
|
559
463
|
pemDecode: pemDecode,
|
package/lib/schema-x509.js
CHANGED
|
@@ -39,7 +39,6 @@ var pkix = require("./schema-pkix");
|
|
|
39
39
|
var CertificateError = frameworkError.CertificateError;
|
|
40
40
|
var PemError = frameworkError.PemError;
|
|
41
41
|
|
|
42
|
-
// ---- PEM -------------------------------------------------------------
|
|
43
42
|
|
|
44
43
|
/**
|
|
45
44
|
* @primitive pki.schema.x509.pemDecode
|
|
@@ -83,22 +82,12 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label === null ? n
|
|
|
83
82
|
*/
|
|
84
83
|
function pemEncode(der, label) { return pkix.pemEncode(der, label, PemError); }
|
|
85
84
|
|
|
86
|
-
// ---- helpers ---------------------------------------------------------
|
|
87
85
|
|
|
88
|
-
// The x509 error namespace the schema engine walks under: prefix names the
|
|
89
|
-
// error family and E constructs the typed CertificateError. The shared PKIX
|
|
90
|
-
// sub-schemas (AlgorithmIdentifier, Name, Extension) live in lib/pkix-schema.js
|
|
91
|
-
// as ns-parameterized factories so crl.js / cms.js reuse them while emitting
|
|
92
|
-
// their own crl/*, cms/* codes; here they are instantiated under this NS.
|
|
93
86
|
var NS = pkix.makeNS("x509", CertificateError, oid);
|
|
94
87
|
|
|
95
88
|
var ALGORITHM_IDENTIFIER = pkix.algorithmIdentifier(NS);
|
|
96
89
|
var NAME = pkix.name(NS);
|
|
97
90
|
|
|
98
|
-
// Validity ::= SEQUENCE { notBefore Time, notAfter Time } -- the outer tag is
|
|
99
|
-
// asserted (a SET-tagged body is a different ASN.1 type, not a Validity).
|
|
100
|
-
// pkix.time enforces the RFC 5280 sec. 4.1.2.5 encoding cutover on decode: a
|
|
101
|
-
// validity date through 2049 must be UTCTime, GeneralizedTime is 2050 onward.
|
|
102
91
|
var VALIDITY = schema.seq([
|
|
103
92
|
schema.field("notBefore", pkix.time(NS)),
|
|
104
93
|
schema.field("notAfter", pkix.time(NS)),
|
|
@@ -107,24 +96,12 @@ var VALIDITY = schema.seq([
|
|
|
107
96
|
build: function (m) { return { notBefore: m.fields.notBefore.value, notAfter: m.fields.notAfter.value }; },
|
|
108
97
|
});
|
|
109
98
|
|
|
110
|
-
// SubjectPublicKeyInfo -- the shared pkix.spki factory under the x509 NS.
|
|
111
99
|
var SPKI = pkix.spki(NS);
|
|
112
100
|
|
|
113
101
|
var EXTENSIONS = pkix.extensions(NS);
|
|
114
102
|
|
|
115
|
-
// Version ::= INTEGER { v1(0), v2(1), v3(2) }, [0] EXPLICIT DEFAULT v1. Read as a
|
|
116
|
-
// BigInt so an out-of-range value can't coerce to a float; reject an explicitly-
|
|
117
|
-
// encoded v1 (DER forbids encoding the DEFAULT).
|
|
118
|
-
// version ::= INTEGER { v1(0), v2(1), v3(2) } inside a [0] EXPLICIT DEFAULT v1 --
|
|
119
|
-
// DER forbids encoding the default, so 0 is rejected; 1->v2, 2->v3.
|
|
120
103
|
var CERTIFICATE_VERSION = pkix.versionReader(NS, { "1": 2, "2": 3 });
|
|
121
104
|
|
|
122
|
-
// TBSCertificate ::= SEQUENCE { version [0] EXPLICIT DEFAULT v1, serialNumber
|
|
123
|
-
// INTEGER, signature AlgorithmIdentifier, issuer Name, validity Validity,
|
|
124
|
-
// subject Name, subjectPublicKeyInfo SubjectPublicKeyInfo, issuerUniqueID [1]
|
|
125
|
-
// IMPLICIT OPTIONAL, subjectUniqueID [2] IMPLICIT OPTIONAL, extensions [3]
|
|
126
|
-
// EXPLICIT OPTIONAL }. The trailing fields are at-most-once, in increasing tag
|
|
127
|
-
// order (the engine enforces it); only extensions are surfaced.
|
|
128
105
|
var CERTIFICATE_TBS = schema.seq([
|
|
129
106
|
schema.optional("version", CERTIFICATE_VERSION, { tag: 0, explicit: true, emptyCode: "x509/bad-version", default: 1 }),
|
|
130
107
|
schema.field("serialNumber", schema.integerLeaf()),
|
|
@@ -134,49 +111,33 @@ var CERTIFICATE_TBS = schema.seq([
|
|
|
134
111
|
schema.field("subject", NAME),
|
|
135
112
|
schema.field("subjectPublicKeyInfo", SPKI),
|
|
136
113
|
schema.trailing([
|
|
137
|
-
// RFC 5280 sec. 4.1.2.8 -- the unique identifiers are [n] IMPLICIT BIT
|
|
138
|
-
// STRING (context PRIMITIVE, unusedBits validated), never an EXPLICIT wrap.
|
|
139
114
|
{ tag: 1, name: "issuerUniqueID", schema: schema.implicitBitString(1) },
|
|
140
115
|
{ tag: 2, name: "subjectUniqueID", schema: schema.implicitBitString(2) },
|
|
141
116
|
{ tag: 3, name: "extensions", schema: EXTENSIONS, explicit: true, emptyCode: "x509/bad-extensions" },
|
|
142
117
|
], { minTag: 1, maxTag: 3, unexpectedCode: "x509/bad-tbs", orderCode: "x509/bad-tbs" }),
|
|
143
118
|
], { assert: "sequence", code: "x509/bad-tbs", what: "tbsCertificate" });
|
|
144
119
|
|
|
145
|
-
// Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, signatureValue
|
|
146
|
-
// BIT STRING }. The build runs the RFC 5280 cross-field checks (sec. 4.1.1.2 sig-alg
|
|
147
|
-
// agreement, sec. 4.1.2.4 non-empty issuer, sec. 4.1.2.9 extensions-only-in-v3) after the
|
|
148
|
-
// structural walk, then assembles the public parse result. Raw-byte accessors
|
|
149
|
-
// (serialNumberHex, tbsBytes, sig-alg agreement) read off the match-tree nodes.
|
|
150
|
-
// Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, signatureValue }
|
|
151
|
-
// -- the shared SIGNED envelope. The certificate-specific invariants (outer==inner
|
|
152
|
-
// signatureAlgorithm agreement, non-empty issuer, v3-only extensions) live in the
|
|
153
|
-
// build; the envelope owns the SEQUENCE-of-3 shape and signature extraction.
|
|
154
120
|
var CERTIFICATE = pkix.signedEnvelope(NS, CERTIFICATE_TBS, {
|
|
155
121
|
code: "x509/not-a-certificate", what: "Certificate",
|
|
156
122
|
build: function (e, ctx) {
|
|
157
|
-
var tbs = e.tbsMatch;
|
|
123
|
+
var tbs = e.tbsMatch;
|
|
158
124
|
|
|
159
|
-
// RFC 5280 sec. 4.1.1.2 -- outer signatureAlgorithm MUST equal tbsCertificate.signature.
|
|
160
125
|
if (!e.outerSignatureAlgorithmBytes.equals(tbs.fields.signature.node.bytes)) {
|
|
161
126
|
throw ctx.E("x509/bad-signature-algorithm", "signatureAlgorithm must match tbsCertificate.signature (RFC 5280 sec. 4.1.1.2)");
|
|
162
127
|
}
|
|
163
128
|
|
|
164
|
-
var version = tbs.fields.version.value;
|
|
129
|
+
var version = tbs.fields.version.value;
|
|
165
130
|
var issuer = tbs.fields.issuer.value.result;
|
|
166
|
-
// RFC 5280 sec. 4.1.2.4 -- issuer MUST be non-empty (an empty subject is permitted, sec. 4.1.2.6).
|
|
167
131
|
if (!issuer.rdns.length) {
|
|
168
132
|
throw ctx.E("x509/bad-issuer", "issuer must be a non-empty distinguished name");
|
|
169
133
|
}
|
|
170
134
|
|
|
171
135
|
var extField = tbs.fields.extensions;
|
|
172
136
|
var hasExtensions = !!(extField && extField.present);
|
|
173
|
-
// RFC 5280 sec. 4.1.2.9 -- extensions appear only in a v3 certificate.
|
|
174
137
|
if (hasExtensions && version !== 3) {
|
|
175
138
|
throw ctx.E("x509/bad-version", "extensions are only permitted in a v3 certificate");
|
|
176
139
|
}
|
|
177
140
|
|
|
178
|
-
// RFC 5280 sec. 4.1.2.8 -- issuerUniqueID / subjectUniqueID MUST NOT
|
|
179
|
-
// appear when the version is 1; they require a v2 or v3 certificate.
|
|
180
141
|
if ((tbs.fields.issuerUniqueID.present || tbs.fields.subjectUniqueID.present) && version < 2) {
|
|
181
142
|
throw ctx.E("x509/bad-version", "issuerUniqueID/subjectUniqueID require a v2 or v3 certificate (RFC 5280 sec. 4.1.2.8)");
|
|
182
143
|
}
|
|
@@ -199,7 +160,6 @@ var CERTIFICATE = pkix.signedEnvelope(NS, CERTIFICATE_TBS, {
|
|
|
199
160
|
},
|
|
200
161
|
});
|
|
201
162
|
|
|
202
|
-
// ---- parse -----------------------------------------------------------
|
|
203
163
|
|
|
204
164
|
/**
|
|
205
165
|
* @primitive pki.schema.x509.parse
|
|
@@ -232,34 +192,15 @@ var CERTIFICATE = pkix.signedEnvelope(NS, CERTIFICATE_TBS, {
|
|
|
232
192
|
* cert.validity.notAfter; // Date
|
|
233
193
|
* cert.signatureAlgorithm.name; // "Ed25519" (the algorithm the issuer signed with)
|
|
234
194
|
*/
|
|
235
|
-
// The parser RECORDS the bytes it read, off the returned object, so a verb that computes a verdict
|
|
236
|
-
// can re-derive from them instead of trusting the object it was handed.
|
|
237
|
-
//
|
|
238
|
-
// Completeness alone cannot carry a certificate's meaning. A certificate is one signature over one
|
|
239
|
-
// byte range, but a parsed certificate presents that range (`tbsBytes`), the signature, and every
|
|
240
|
-
// field the range encodes as separate properties. Keep a real CA certificate's signed bytes and
|
|
241
|
-
// signature and replace only `subjectPublicKeyInfo`, and the signature check still passes over the
|
|
242
|
-
// original range while the substituted key is what gets used to verify the next certificate in the
|
|
243
|
-
// chain -- a forged chain out of a genuine certificate. Emptying `extensions` is the same move
|
|
244
|
-
// against basicConstraints, keyUsage, name constraints, and the unknown-critical rule.
|
|
245
|
-
//
|
|
246
|
-
// Recording the source is what makes the object safe to accept: the verdict verbs parse it again
|
|
247
|
-
// from these bytes, so anything done to the object afterwards is discarded rather than believed.
|
|
248
195
|
var parse = pkix.makeRecordingParser({ pemLabel: "CERTIFICATE", PemError: PemError, ErrorClass: CertificateError, prefix: "x509", what: "certificate", topSchema: CERTIFICATE, ns: NS }, "certificate");
|
|
249
196
|
|
|
250
|
-
// matches(root): does the decoded DER look like a Certificate? A CSR and a CRL
|
|
251
|
-
// share the outer SEQUENCE-of-3 envelope, so the discriminator is inside the
|
|
252
|
-
// tbs: a certificate has a Validity -- a SEQUENCE of exactly two Time values -- at
|
|
253
|
-
// position [serial, signature, issuer, VALIDITY] after the optional [0] version.
|
|
254
|
-
// A CSR's certificationRequestInfo has subjectPublicKeyInfo there (no Validity),
|
|
255
|
-
// and a CRL leads with a bare Time; neither matches. Used by the orchestrator.
|
|
256
197
|
function matches(root) {
|
|
257
198
|
var TAGS = asn1.TAGS;
|
|
258
199
|
var tbs = pkix.signedEnvelopeTbs(root);
|
|
259
200
|
if (!tbs) return false;
|
|
260
201
|
var kids = tbs.children;
|
|
261
|
-
var i = schema.isContext(kids[0], 0) ? 1 : 0;
|
|
262
|
-
var validity = kids[i + 3];
|
|
202
|
+
var i = schema.isContext(kids[0], 0) ? 1 : 0;
|
|
203
|
+
var validity = kids[i + 3];
|
|
263
204
|
return schema.isUniversal(validity, TAGS.SEQUENCE) &&
|
|
264
205
|
!!validity.children && validity.children.length === 2 &&
|
|
265
206
|
validity.children.every(function (t) {
|