@blamejs/pki 0.9.0 → 0.9.3
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 +36 -0
- package/MIGRATING.md +103 -0
- package/README.md +5 -5
- package/lib/alt-sig.js +3 -3
- package/lib/attrcert-sign.js +5 -5
- package/lib/cmc-verify.js +13 -9
- package/lib/cmp-build.js +2 -2
- package/lib/cmp-verify.js +1 -1
- package/lib/cms-sign.js +21 -0
- package/lib/cms-verify.js +5 -3
- package/lib/constants.js +3 -0
- package/lib/crl-sign.js +1 -1
- package/lib/crmf-sign.js +1 -1
- package/lib/csr-sign.js +4 -2
- package/lib/est.js +93 -21
- package/lib/guard-identifier.js +110 -0
- package/lib/guard-name.js +91 -4
- package/lib/inspect.js +4 -4
- package/lib/lint.js +2 -2
- package/lib/mime.js +124 -17
- package/lib/ocsp-verify.js +1 -1
- package/lib/pki-build.js +14 -0
- package/lib/possession.js +20 -14
- package/lib/related-cert.js +72 -11
- package/lib/schema-attrcert.js +5 -5
- package/lib/schema-c509.js +42 -6
- package/lib/schema-cmp.js +3 -3
- package/lib/schema-pkix.js +62 -3
- package/lib/schema-trustanchor.js +4 -4
- package/lib/sigstore.js +1 -1
- package/lib/smime.js +18 -7
- package/lib/trust.js +1 -1
- package/lib/trustanchor-build.js +1 -1
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/mime.js
CHANGED
|
@@ -26,6 +26,9 @@ var _push = guard.list.append;
|
|
|
26
26
|
var CRLF = Buffer.from("\r\n");
|
|
27
27
|
|
|
28
28
|
function _buf(v, E, code, label) { return guard.bytes.view(v, E, code, label); }
|
|
29
|
+
/** @internal `guard.limits.counter` throws `E(code, ...)`, and every consumer of this module hands in
|
|
30
|
+
* its error CLASS. */
|
|
31
|
+
function _factory(E) { return function (c, m) { return new E(c, m); }; }
|
|
29
32
|
|
|
30
33
|
function _splitPoint(bytes) {
|
|
31
34
|
for (var i = 0; i < bytes.length; i++) {
|
|
@@ -57,27 +60,83 @@ function _stripLeadingHtab(s) {
|
|
|
57
60
|
return s.slice(i);
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
/** @internal The end of the line starting at `from`, and where the next one starts. A line is ended by
|
|
64
|
+
* CRLF, by a lone CR or by LF, which is the separator set `_toLf` followed by a split produces. */
|
|
65
|
+
function _lineEnd(s, from) {
|
|
66
|
+
for (var i = from; i < s.length; i++) {
|
|
67
|
+
var c = s.charCodeAt(i);
|
|
68
|
+
if (c === 0x0a) return { end: i, next: i + 1 };
|
|
69
|
+
if (c === 0x0d) return { end: i, next: (s.charCodeAt(i + 1) === 0x0a) ? i + 2 : i + 1 };
|
|
70
|
+
}
|
|
71
|
+
return { end: s.length, next: s.length };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** @internal One line is cut at a time and EVERY line is charged, folded continuations and empty lines
|
|
75
|
+
* included, so the refusal costs the cap rather than the input. Splitting the whole header area first
|
|
76
|
+
* and counting fields afterwards read a 16 MiB area of 2,097,150 one-line fields in 1114 ms and
|
|
77
|
+
* 653 MiB of resident memory before the count refused it, and charging only fields left a 16 MiB area
|
|
78
|
+
* of 1,864,130 continuation lines folding into one 13 MB value in 1648 ms without the count ever
|
|
79
|
+
* reaching two. */
|
|
80
|
+
function _unfoldHeaders(headerText, counted) {
|
|
62
81
|
var out = [];
|
|
63
|
-
for (var
|
|
64
|
-
var
|
|
82
|
+
for (var at = 0; at < headerText.length;) {
|
|
83
|
+
var cut = _lineEnd(headerText, at);
|
|
84
|
+
var ln = headerText.slice(at, cut.end);
|
|
85
|
+
at = cut.next;
|
|
86
|
+
counted.tick();
|
|
65
87
|
if (ln === "") continue;
|
|
66
|
-
if ((ln
|
|
67
|
-
|
|
88
|
+
if ((ln.charAt(0) === " " || ln.charAt(0) === "\t") && out.length) {
|
|
89
|
+
out[out.length - 1] += " " + _stripLeadingHtab(ln);
|
|
90
|
+
} else {
|
|
91
|
+
_push(out, ln);
|
|
92
|
+
}
|
|
68
93
|
}
|
|
69
94
|
return out;
|
|
70
95
|
}
|
|
71
96
|
|
|
72
|
-
|
|
73
|
-
|
|
97
|
+
/**
|
|
98
|
+
* @internal
|
|
99
|
+
* RFC 2045 sec. 5.1 gives a parameter one occurrence, so a header naming one twice has no value this
|
|
100
|
+
* function can resolve: reading the last occurrence and reading the first are both conforming, and they
|
|
101
|
+
* disagree. These values decide what a signature covers (`boundary` sets where the parts begin),
|
|
102
|
+
* whether the entity is accepted (`protocol`, `smime-type`) and which digest is reported (`micalg`), so
|
|
103
|
+
* a repeat is refused here, where the record is built, rather than per parameter at each reader. An
|
|
104
|
+
* occurrence counts whether or not it carries a value, which is the `hp; hp="clear"` form; a single
|
|
105
|
+
* bare attribute still reaches the reader that owns it.
|
|
106
|
+
*
|
|
107
|
+
* What is refused is two readings that produce DIFFERENT VALUES, which is why the two RFC 2231
|
|
108
|
+
* spellings are not treated alike. A plain occurrence beside a starred one of the same attribute,
|
|
109
|
+
* `boundary*=us-ascii''DECOY; boundary=REAL`, gives this reader REAL and an RFC 2231 reader DECOY, so
|
|
110
|
+
* it is refused. Continuation segments alone, `name*0=a; name*1=b`, give this reader NO value for that
|
|
111
|
+
* attribute, so a consumer sees it as absent and the one place absence matters already fails closed:
|
|
112
|
+
* `splitMultipart` refuses an entity with no boundary. Refusing those would reject a conforming
|
|
113
|
+
* message over a parameter that cannot change what the signature covers.
|
|
114
|
+
*/
|
|
115
|
+
function _parseStructured(headerValue, E, code, label) {
|
|
116
|
+
var counted = guard.limits.counter(C.LIMITS.MIME_MAX_PARAMS, _factory(E), code, label + " parameter");
|
|
117
|
+
var parts = _splitSemicolons(headerValue, counted);
|
|
74
118
|
var type = _stripComments(parts[0]).trim().toLowerCase();
|
|
75
119
|
var params = Object.create(null);
|
|
120
|
+
var seenExact = Object.create(null), seenPlain = Object.create(null), seenStarred = Object.create(null);
|
|
76
121
|
for (var i = 1; i < parts.length; i++) {
|
|
77
122
|
var p = _stripComments(parts[i]);
|
|
78
123
|
var eq = p.indexOf("=");
|
|
124
|
+
var name = (eq < 0 ? p : p.slice(0, eq)).trim().toLowerCase();
|
|
125
|
+
if (name === "") continue;
|
|
126
|
+
var base = _baseAttribute(name);
|
|
127
|
+
var starred = base !== name;
|
|
128
|
+
if (seenExact[name] === true) {
|
|
129
|
+
throw new E(code, label + " declares the " + JSON.stringify(name) +
|
|
130
|
+
" parameter more than once, so its value is ambiguous (RFC 2045 sec. 5.1)");
|
|
131
|
+
}
|
|
132
|
+
if (starred ? seenPlain[base] === true : seenStarred[base] === true) {
|
|
133
|
+
throw new E(code, label + " spells the " + JSON.stringify(base) +
|
|
134
|
+
" parameter both plainly and as an RFC 2231 extended or continued value, and the two spellings " +
|
|
135
|
+
"carry different values (RFC 2231 sec. 7)");
|
|
136
|
+
}
|
|
137
|
+
seenExact[name] = true;
|
|
138
|
+
if (starred) seenStarred[base] = true; else seenPlain[base] = true;
|
|
79
139
|
if (eq < 0) continue;
|
|
80
|
-
var name = p.slice(0, eq).trim().toLowerCase();
|
|
81
140
|
var val = p.slice(eq + 1).trim();
|
|
82
141
|
if (val.length >= 2 && val[0] === '"' && val[val.length - 1] === '"') val = val.slice(1, -1);
|
|
83
142
|
params[name] = val;
|
|
@@ -99,7 +158,28 @@ function _stripComments(s) {
|
|
|
99
158
|
return out;
|
|
100
159
|
}
|
|
101
160
|
|
|
102
|
-
|
|
161
|
+
/** @internal The attribute an RFC 2231 name belongs to. Sections 3 and 4 spell one parameter three
|
|
162
|
+
* ways, `name`, `name*` for an extended value and `name*0` / `name*1` for a continuation, and
|
|
163
|
+
* section 7 makes them the same logical parameter. This module assembles neither starred form, so a
|
|
164
|
+
* reader that does takes a different value from the same bytes: measured, `boundary*=us-ascii''DECOY;
|
|
165
|
+
* boundary=REAL` verified here while an RFC 2231 reader splits the body on DECOY. Comparing the
|
|
166
|
+
* attribute rather than the spelling is what makes the two the same occurrence. A leading asterisk
|
|
167
|
+
* is a name in its own right and keeps it. */
|
|
168
|
+
function _baseAttribute(name) {
|
|
169
|
+
var star = name.indexOf("*");
|
|
170
|
+
return star > 0 ? name.slice(0, star) : name;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** @internal The first segment is the media type rather than a parameter, so it is not charged. The
|
|
174
|
+
* charge comes BEFORE the segment is stored, so the refusal costs the cap and not the input: charging
|
|
175
|
+
* the segments after the split instead read a 3.8 MiB Content-Type of 1,000,000 parameters in 192 ms
|
|
176
|
+
* and 73 MiB of resident memory before the same cap refused it. */
|
|
177
|
+
function _pushSegment(out, seg, counted) {
|
|
178
|
+
if (counted !== undefined && out.length > 0) counted.tick();
|
|
179
|
+
_push(out, seg);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function _splitSemicolons(s, counted) {
|
|
103
183
|
var out = [], cur = "", inQ = false, depth = 0;
|
|
104
184
|
for (var i = 0; i < s.length; i++) {
|
|
105
185
|
var ch = s[i];
|
|
@@ -107,21 +187,29 @@ function _splitSemicolons(s) {
|
|
|
107
187
|
else if (ch === '"' && depth === 0) { inQ = !inQ; cur += ch; }
|
|
108
188
|
else if (ch === "(" && !inQ) { depth++; cur += ch; }
|
|
109
189
|
else if (ch === ")" && !inQ && depth > 0) { depth--; cur += ch; }
|
|
110
|
-
else if (ch === ";" && !inQ && depth === 0) {
|
|
190
|
+
else if (ch === ";" && !inQ && depth === 0) { _pushSegment(out, cur, counted); cur = ""; }
|
|
111
191
|
else cur += ch;
|
|
112
192
|
}
|
|
113
|
-
|
|
193
|
+
_pushSegment(out, cur, counted);
|
|
114
194
|
return out;
|
|
115
195
|
}
|
|
116
196
|
|
|
117
197
|
function parse(input, E, code) {
|
|
118
|
-
|
|
119
|
-
|
|
198
|
+
/** @internal A snapshot, not a view: the record below holds `subarray`s of these bytes and a caller
|
|
199
|
+
* reads them back after the parse returns, so a view would let the caller's own later write change
|
|
200
|
+
* what the record says the entity was. Measured on the shipped verb: `pki.smime.verify` returned
|
|
201
|
+
* `valid: true` with `content` sharing the caller's store, and a write into that store afterwards
|
|
202
|
+
* changed the bytes the caller read as verified. The copy is byte-identical, so the octets a
|
|
203
|
+
* signature covers are unchanged. */
|
|
204
|
+
var viewed = _buf(input, E, code, "the MIME entity");
|
|
205
|
+
guard.limits.byteCap(viewed, C.LIMITS.MIME_MAX_BYTES, _factory(E), code, "the MIME entity");
|
|
206
|
+
var bytes = guard.bytes.snapshot(viewed, E, code, "the MIME entity");
|
|
120
207
|
var sp = _splitPoint(bytes);
|
|
121
208
|
var headerBytes = sp ? bytes.subarray(0, sp.headerEnd) : bytes;
|
|
122
209
|
var bodyBytes = sp ? bytes.subarray(sp.bodyStart) : Buffer.alloc(0);
|
|
123
210
|
var headerText = guard.text.decode(headerBytes, C.LIMITS.MIME_MAX_BYTES, E, { charset: "latin1", tooLarge: code, badInput: code, label: "the MIME headers" });
|
|
124
|
-
var
|
|
211
|
+
var countedHeaders = guard.limits.counter(C.LIMITS.MIME_MAX_HEADERS, _factory(E), code, "the MIME header line");
|
|
212
|
+
var rawHeaders = _unfoldHeaders(headerText, countedHeaders);
|
|
125
213
|
var headers = [];
|
|
126
214
|
for (var i = 0; i < rawHeaders.length; i++) {
|
|
127
215
|
var colon = rawHeaders[i].indexOf(":");
|
|
@@ -130,12 +218,29 @@ function parse(input, E, code) {
|
|
|
130
218
|
var body = rawHeaders[i].slice(colon + 1);
|
|
131
219
|
_push(headers,{ name: nm, lname: nm.toLowerCase(), value: body.trim(), rawValue: body.charAt(0) === " " ? body.slice(1) : body });
|
|
132
220
|
}
|
|
133
|
-
|
|
221
|
+
/** @internal Resolving a field that occurs more than once means choosing an occurrence, and reading
|
|
222
|
+
* the first and reading the last are both conforming (RFC 5322 sec. 3.6 gives a singleton field one
|
|
223
|
+
* occurrence). The media type decides which reader a body goes to and the transfer encoding decides
|
|
224
|
+
* how its octets are recovered, so a repeat is refused rather than resolved by position. Every
|
|
225
|
+
* occurrence stays in `headers`, where a field that may legally repeat, such as `Received`, is
|
|
226
|
+
* read instead. */
|
|
227
|
+
function header(name) {
|
|
228
|
+
var l = name.toLowerCase(), found = null;
|
|
229
|
+
for (var j = 0; j < headers.length; j++) {
|
|
230
|
+
if (headers[j].lname !== l) continue;
|
|
231
|
+
if (found !== null) {
|
|
232
|
+
throw new E(code, "the entity declares the " + JSON.stringify(headers[j].name) +
|
|
233
|
+
" field more than once, so resolving it to a single value is ambiguous (RFC 5322 sec. 3.6)");
|
|
234
|
+
}
|
|
235
|
+
found = headers[j];
|
|
236
|
+
}
|
|
237
|
+
return found === null ? null : found.value;
|
|
238
|
+
}
|
|
134
239
|
var ctv = header("content-type");
|
|
135
240
|
var cte = (header("content-transfer-encoding") || "").trim().toLowerCase() || "7bit";
|
|
136
241
|
return {
|
|
137
242
|
headers: headers, header: header,
|
|
138
|
-
contentType: ctv != null ? _parseStructured(ctv) : { value: "text/plain", type: "text/plain", params: Object.create(null) },
|
|
243
|
+
contentType: ctv != null ? _parseStructured(ctv, E, code, "the Content-Type") : { value: "text/plain", type: "text/plain", params: Object.create(null) },
|
|
139
244
|
cte: cte,
|
|
140
245
|
headerBytes: headerBytes, bodyBytes: bodyBytes, body: bodyBytes, bytes: bytes,
|
|
141
246
|
};
|
|
@@ -158,7 +263,9 @@ function splitMultipart(bodyBytes, boundary, E, code) {
|
|
|
158
263
|
if (idx < 0) throw new E(code, "no boundary delimiter found in the multipart body");
|
|
159
264
|
var pos = _afterLine(body, idx + delim.length, E, code);
|
|
160
265
|
var parts = [];
|
|
266
|
+
var counted = guard.limits.counter(C.LIMITS.MIME_MAX_PARTS, _factory(E), code, "the multipart body part");
|
|
161
267
|
while (true) {
|
|
268
|
+
counted.tick();
|
|
162
269
|
var next = _findDelim(body, delim, pos);
|
|
163
270
|
if (next < 0) throw new E(code, "an unterminated multipart body part (no closing boundary)");
|
|
164
271
|
var partEnd = next;
|
package/lib/ocsp-verify.js
CHANGED
|
@@ -25,7 +25,7 @@ var OID_OCSP_NOCHECK = oid.byName("ocspNoCheck");
|
|
|
25
25
|
var OID_EKU = oid.byName("extKeyUsage");
|
|
26
26
|
var OID_KEY_USAGE = oid.byName("keyUsage");
|
|
27
27
|
|
|
28
|
-
function ocspDigest(alg, buf) { return subtle.digest(alg, buf)
|
|
28
|
+
function ocspDigest(alg, buf) { return intrinsic.chain(subtle.digest(alg, buf), function (h) { return _bufferFrom(h); }); }
|
|
29
29
|
|
|
30
30
|
function ocspKeyValue(spkiDer) {
|
|
31
31
|
return asn1.read.bitString(asn1.decode(spkiDer).children[1]).bytes;
|
package/lib/pki-build.js
CHANGED
|
@@ -522,6 +522,20 @@ function makeBuilder(ctx) {
|
|
|
522
522
|
}
|
|
523
523
|
function encodeName(spec) {
|
|
524
524
|
if (guard.bytes.isByteSource(spec)) { var _nd = guard.bytes.snapshotSource(spec, ErrorClass, ctx.prefix + "/bad-name", "raw Name DER"); assertValidNameDer(_nd); return _nd; }
|
|
525
|
+
/** @internal A bare string is the common-name shorthand, and a caller who writes a distinguished
|
|
526
|
+
* name here gets one commonName holding the whole text: measured, `"CN=Example CA, O=Example"`
|
|
527
|
+
* certified the single RDN `CN=CN=Example CA\, O=Example`, the doubled `CN=` being the caller's
|
|
528
|
+
* own attribute type read as part of the value. Only one of the two readings can be what was
|
|
529
|
+
* meant, so the distinguished-name one is refused rather than certified as a name nobody asked
|
|
530
|
+
* for, and the caller says which by passing `pki.x509.parseDn(s).bytes` or the RDN array. A
|
|
531
|
+
* common name that contains an `=` further along does not open as a distinguished name
|
|
532
|
+
* and is untouched. OpenSSL refuses the same string for the same reason, naming the form it
|
|
533
|
+
* wants; Go has no string form and .NET reads a string as the distinguished name. */
|
|
534
|
+
if (typeof spec === "string" && guard.name.opensAsDnString(spec)) {
|
|
535
|
+
throw E("bad-name", "a bare name string is a common name, and " + guard.text.showValue(spec) +
|
|
536
|
+
" opens as a distinguished name. Pass pki.x509.parseDn(s).bytes for the distinguished name, " +
|
|
537
|
+
"or [{ commonName: s }] to certify it as one common name");
|
|
538
|
+
}
|
|
525
539
|
if (typeof spec === "string") spec = [{ commonName: spec }];
|
|
526
540
|
if (!_isArray(spec)) throw E("bad-name", "a name must be a string, an array of RDNs, or raw Name DER");
|
|
527
541
|
/** @internal Held to the same density every other authoring list is held to. Mapping over a
|
package/lib/possession.js
CHANGED
|
@@ -160,12 +160,12 @@ function _namesCertificate(signer, certParsed) {
|
|
|
160
160
|
* var kemSpki = await pki.key.export(kem.publicKey, { format: "der" });
|
|
161
161
|
* var sigSpki = await pki.key.export(sig.publicKey, { format: "der" });
|
|
162
162
|
* var sigCert = await pki.x509.sign({
|
|
163
|
-
* subject: "
|
|
163
|
+
* subject: "kem.example", subjectPublicKey: sigSpki, serialNumber: 34n,
|
|
164
164
|
* notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z"),
|
|
165
165
|
* }, { key: sig.privateKey });
|
|
166
166
|
* var parsedCert = pki.schema.x509.parse(sigCert);
|
|
167
167
|
* var csr = await pki.csr.sign({
|
|
168
|
-
* subject: "
|
|
168
|
+
* subject: "kem.example", subjectPublicKey: kemSpki,
|
|
169
169
|
* privateKeyPossessionStatement: {
|
|
170
170
|
* signer: { issuer: parsedCert.issuer.bytes, serialNumber: parsedCert.serialNumber },
|
|
171
171
|
* certificate: sigCert,
|
|
@@ -354,18 +354,18 @@ var _isSignatureOnlyKey = pkix.signatureOnlyKeyOid(NS);
|
|
|
354
354
|
* var ca = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
|
|
355
355
|
* var caSpki = await pki.key.export(ca.publicKey, { format: "der" });
|
|
356
356
|
* var caCert = await pki.x509.sign({
|
|
357
|
-
* subject: "
|
|
357
|
+
* subject: "Possession CA", subjectPublicKey: caSpki, serialNumber: 1n,
|
|
358
358
|
* notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z"),
|
|
359
359
|
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign"] },
|
|
360
360
|
* }, { key: ca.privateKey });
|
|
361
361
|
* var sigCert = await pki.x509.sign({
|
|
362
|
-
* subject: "
|
|
362
|
+
* subject: "kem.example", subjectPublicKey: sigSpki, serialNumber: 34n,
|
|
363
363
|
* notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z"),
|
|
364
364
|
* extensions: { keyUsage: ["digitalSignature"] },
|
|
365
365
|
* }, { cert: caCert, key: ca.privateKey });
|
|
366
366
|
* var p = pki.schema.x509.parse(sigCert);
|
|
367
367
|
* var csr = await pki.csr.sign({
|
|
368
|
-
* subject: "
|
|
368
|
+
* subject: "kem.example", subjectPublicKey: kemSpki,
|
|
369
369
|
* privateKeyPossessionStatement: {
|
|
370
370
|
* signer: { issuer: p.issuer.bytes, serialNumber: p.serialNumber }, certificate: sigCert,
|
|
371
371
|
* },
|
|
@@ -522,13 +522,15 @@ function _verifyRequest(request, opts) {
|
|
|
522
522
|
"authorize a certification request (RFC 5280 sec. 4.2.1.3)");
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
-
return
|
|
526
|
-
|
|
525
|
+
return intrinsic.chain(
|
|
526
|
+
_promiseResolve(_Promise, _validatePath(chain, { trustAnchors: o.trustAnchors, time: at })),
|
|
527
|
+
function (pathResult) {
|
|
527
528
|
var pathValidated = pathResult.valid === true;
|
|
528
529
|
if (!pathValidated) _push(reasons, "the signature certificate's certification path is not valid, and RFC 9883 sec. 4 makes that a MUST");
|
|
529
|
-
return
|
|
530
|
-
|
|
531
|
-
|
|
530
|
+
return intrinsic.chain(
|
|
531
|
+
_seam.verify(parsed.signatureAlgorithm, parsed.signatureValue,
|
|
532
|
+
sigCert.subjectPublicKeyInfo.bytes, parsed.certificationRequestInfoBytes),
|
|
533
|
+
function (verified) {
|
|
532
534
|
if (verified !== true) _push(reasons, "the request signature does not verify under the signature certificate's public key, and RFC 9883 sec. 4 makes that a MUST");
|
|
533
535
|
return guard.verdict.of({}, {
|
|
534
536
|
valid: verified === true && pathValidated && signerMaySign && !requestsSignatureCertificate &&
|
|
@@ -543,15 +545,19 @@ function _verifyRequest(request, opts) {
|
|
|
543
545
|
requestsSignatureCertificate: requestsSignatureCertificate,
|
|
544
546
|
reason: reasons.length ? _join(reasons, "; ") : undefined,
|
|
545
547
|
});
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
+
}
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
);
|
|
548
552
|
}
|
|
549
553
|
|
|
550
554
|
/** @internal Whether the signature certificate's key may make this signature at all. A certificate confined
|
|
551
555
|
* to `keyAgreement` does not authorize a signature over a certification request, and accepting one lets a
|
|
552
556
|
* key never issued for signing authorize issuance. The accepted set is `pki.cms`'s: a signature over
|
|
553
|
-
* content is authorized by digitalSignature
|
|
554
|
-
|
|
557
|
+
* content is authorized by digitalSignature or nonRepudiation alike. The decoder emits the
|
|
558
|
+
* RFC 5280 sec. 4.2.1.3 bit name `nonRepudiation`, never the X.509 spelling `contentCommitment`,
|
|
559
|
+
* so naming the latter here asked for a name nothing defines. */
|
|
560
|
+
var _maySign = pkix.keyUsagePermits(NS, ["digitalSignature", "nonRepudiation"]);
|
|
555
561
|
|
|
556
562
|
/** @internal The subjectAltName a request asks for, or null when it asks for none. */
|
|
557
563
|
function _requestedSan(parsedCsr) {
|
package/lib/related-cert.js
CHANGED
|
@@ -99,6 +99,33 @@ function _certIdParts(der) {
|
|
|
99
99
|
var DIGEST_LENGTHS = pkix.digestLengths();
|
|
100
100
|
var SIG_OID_DIGEST = pkix.signatureDigestNames(oid);
|
|
101
101
|
|
|
102
|
+
/** @internal Whether the related certificate's key may make this one signature. RFC 5280 sec. 4.2.1.3
|
|
103
|
+
* confines a key to the purposes a present keyUsage asserts, so a certificate issued only to sign
|
|
104
|
+
* certificates or CRLs does not authorize a proof of possession over a certification request.
|
|
105
|
+
*
|
|
106
|
+
* The set is NOT `pki.possession`'s. RFC 9763 sec. 3.1 states that "if the related certificate is a
|
|
107
|
+
* key establishment certificate (e.g., using RSA key transport or Elliptic Curve Cryptography (ECC)
|
|
108
|
+
* key agreement), the private key is used to sign one time for proof of possession (POP)", so
|
|
109
|
+
* keyEncipherment and keyAgreement authorize this signature where they authorize no other, and
|
|
110
|
+
* holding the proof to possession's signing-only set would refuse a request the document admits.
|
|
111
|
+
* What is left to refuse is a certificate confined to keyCertSign, cRLSign, or the two modifier bits
|
|
112
|
+
* that carry no meaning without keyAgreement. An absent keyUsage constrains nothing.
|
|
113
|
+
*
|
|
114
|
+
* Which establishment bit counts comes from the SUBJECT KEY, because the sentence admits a key
|
|
115
|
+
* establishment CERTIFICATE and a bit the key's own family cannot perform does not make one. The
|
|
116
|
+
* union of both bits admitted a certificate that is not an establishment certificate under any
|
|
117
|
+
* reading: measured, an EC certificate asserting only keyEncipherment, which RFC 8813 sec. 3 makes a
|
|
118
|
+
* MUST NOT for id-ecPublicKey, and an RSA certificate asserting only keyAgreement, which RFC 3279
|
|
119
|
+
* sec. 2.3.1 does not list, both reported signerMaySign and a valid proof. A family the shared table
|
|
120
|
+
* places in neither column admits neither bit, so a signature usage is what authorizes it. */
|
|
121
|
+
var SIGNATURE_USAGE_BITS = ["digitalSignature", "nonRepudiation"];
|
|
122
|
+
var _estabBit = pkix.keyEstablishmentBit(NS);
|
|
123
|
+
var _maySign = pkix.keyUsagePermits(NS, function (parsedCert) {
|
|
124
|
+
var spki = parsedCert && parsedCert.subjectPublicKeyInfo;
|
|
125
|
+
var bit = _estabBit(spki && spki.algorithm ? spki.algorithm.oid : null);
|
|
126
|
+
return bit === null ? SIGNATURE_USAGE_BITS : [bit, "digitalSignature", "nonRepudiation"];
|
|
127
|
+
});
|
|
128
|
+
|
|
102
129
|
/** @internal The digests this module will bind a certificate identity to. sha1 is registered and
|
|
103
130
|
* decodable, and is absent here: a chosen-prefix collision on a certificate is a demonstrated
|
|
104
131
|
* attack, so a value naming it is refused before anything is computed. A caller reading a hashValue a
|
|
@@ -392,7 +419,7 @@ function _parseCert(certDer, what) {
|
|
|
392
419
|
* @example
|
|
393
420
|
* var kp = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
|
|
394
421
|
* var spki = await pki.key.export(kp.publicKey, { format: "der" });
|
|
395
|
-
* var related = await pki.x509.sign({ subject: "
|
|
422
|
+
* var related = await pki.x509.sign({ subject: "Held", subjectPublicKey: spki, serialNumber: 42n,
|
|
396
423
|
* notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z") }, { key: kp.privateKey });
|
|
397
424
|
* pki.relatedCert.certificateHash(related).hashAlgorithm; // -> "sha256", from this certificate's signature OID
|
|
398
425
|
* pki.relatedCert.certificateHash(related, "sha512").hashValue.length; // -> 64
|
|
@@ -455,9 +482,9 @@ function digestValue(hashAlgorithm, hashValue) {
|
|
|
455
482
|
* @example
|
|
456
483
|
* var kp = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
|
|
457
484
|
* var spki = await pki.key.export(kp.publicKey, { format: "der" });
|
|
458
|
-
* var related = await pki.x509.sign({ subject: "
|
|
485
|
+
* var related = await pki.x509.sign({ subject: "Held", subjectPublicKey: spki, serialNumber: 42n,
|
|
459
486
|
* notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z") }, { key: kp.privateKey });
|
|
460
|
-
* var leaf = await pki.x509.sign({ subject: "
|
|
487
|
+
* var leaf = await pki.x509.sign({ subject: "New Key", subjectPublicKey: spki, serialNumber: 43n,
|
|
461
488
|
* notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z"),
|
|
462
489
|
* extensions: { relatedCertificate: { relatedCertificate: related } } }, { key: kp.privateKey });
|
|
463
490
|
* var ext = pki.schema.x509.parse(leaf).extensions
|
|
@@ -563,7 +590,7 @@ function _namedAlgorithm(value) {
|
|
|
563
590
|
|
|
564
591
|
/**
|
|
565
592
|
* @primitive pki.relatedCert.verifyRequest
|
|
566
|
-
* @signature pki.relatedCert.verifyRequest(requesterCertificate, certificate, opts?) -> Promise<
|
|
593
|
+
* @signature pki.relatedCert.verifyRequest(requesterCertificate, certificate, opts?) -> Promise<verdict>
|
|
567
594
|
* @since 0.8.42
|
|
568
595
|
* @status stable
|
|
569
596
|
* @spec RFC 9763 sec. 3.1
|
|
@@ -572,9 +599,18 @@ function _namedAlgorithm(value) {
|
|
|
572
599
|
*
|
|
573
600
|
* Verify the proof of possession in a `relatedCertRequest` CSR attribute. `requesterCertificate` is
|
|
574
601
|
* the value `pki.schema.csr.parse` carries on that attribute, and `certificate` is the DER of the
|
|
575
|
-
* certificate its `certID` names.
|
|
576
|
-
*
|
|
577
|
-
* `
|
|
602
|
+
* certificate its `certID` names.
|
|
603
|
+
*
|
|
604
|
+
* Returns `{ valid, verified, signerMaySign, signatureAlgorithm }`, plus `code` and `reason` when it
|
|
605
|
+
* did not pass. `verified` is the signature over the bytes
|
|
606
|
+
* `pki.relatedCert.requestSignedData` describes, checked under that certificate's subject key.
|
|
607
|
+
* `signerMaySign` is whether the certificate's keyUsage authorizes that signature at all, and
|
|
608
|
+
* `valid` is both together. They are separate because a key the certificate never authorized for
|
|
609
|
+
* signing and a signature that does not verify are different facts, and a caller acting on one as
|
|
610
|
+
* if it were the other acts on the wrong one. RFC 5280 sec. 4.2.1.3 confines a key to the purposes a
|
|
611
|
+
* present keyUsage asserts; a certificate carrying none is unconstrained. A key-establishment
|
|
612
|
+
* certificate still passes, because RFC 9763 sec. 3.1 has such a key "sign one time for proof of
|
|
613
|
+
* possession", so keyEncipherment and keyAgreement authorize this signature and no other.
|
|
578
614
|
*
|
|
579
615
|
* The certificate handed in must be the one `certID` names: an issuer and serial that do not match
|
|
580
616
|
* throw `relatedcert/cert-mismatch`, since verifying a proof against a certificate the request did
|
|
@@ -614,7 +650,7 @@ function _namedAlgorithm(value) {
|
|
|
614
650
|
* @example
|
|
615
651
|
* var kp = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
|
|
616
652
|
* var spki = await pki.key.export(kp.publicKey, { format: "der" });
|
|
617
|
-
* var held = await pki.x509.sign({ subject: "
|
|
653
|
+
* var held = await pki.x509.sign({ subject: "Held", subjectPublicKey: spki, serialNumber: 42n,
|
|
618
654
|
* notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z") }, { key: kp.privateKey });
|
|
619
655
|
* var parsed = pki.schema.x509.parse(held);
|
|
620
656
|
* var certID = { issuer: parsed.issuer.bytes, serialNumber: parsed.serialNumber };
|
|
@@ -627,7 +663,7 @@ function _namedAlgorithm(value) {
|
|
|
627
663
|
* pki.asn1.build.integer(BigInt("0x" + raw.subarray(0, half).toString("hex"))),
|
|
628
664
|
* pki.asn1.build.integer(BigInt("0x" + raw.subarray(half).toString("hex"))),
|
|
629
665
|
* ]);
|
|
630
|
-
* var csr = await pki.csr.sign({ subject: "
|
|
666
|
+
* var csr = await pki.csr.sign({ subject: "New Key", subjectPublicKey: spki,
|
|
631
667
|
* relatedCertRequest: { certID: certID, requestTime: 1800000000,
|
|
632
668
|
* locationInfo: ["https://certs.example/held.cer"], signature: proof } }, { key: kp.privateKey });
|
|
633
669
|
* var attr = pki.schema.csr.parse(csr).attributes
|
|
@@ -635,7 +671,7 @@ function _namedAlgorithm(value) {
|
|
|
635
671
|
* // The window is the certification authority's policy, so it is named: five minutes here, judged
|
|
636
672
|
* // against the instant the request was made, which is what a CA reading a recorded request does.
|
|
637
673
|
* await pki.relatedCert.verifyRequest(attr.relatedCertRequest, held,
|
|
638
|
-
* { maxAge: 300, at: new Date(1800000060 * 1000) }); // -> true
|
|
674
|
+
* { maxAge: 300, at: new Date(1800000060 * 1000) }); // -> { valid: true, verified: true, ... }
|
|
639
675
|
*/
|
|
640
676
|
function verifyRequest(requesterCertificate, certificate, opts) {
|
|
641
677
|
/** @internal Every refusal reaches the caller as a rejection, so a verb that returns a promise never
|
|
@@ -680,6 +716,8 @@ function _verifyRequest(requesterCertificate, certificate, opts) {
|
|
|
680
716
|
"the certificate given is not the one certID names (RFC 9763 sec. 3.2)");
|
|
681
717
|
}
|
|
682
718
|
|
|
719
|
+
var signerMaySign = _maySign(c.parsed);
|
|
720
|
+
|
|
683
721
|
var sigAlg = o.signatureAlgorithm !== undefined && o.signatureAlgorithm !== null
|
|
684
722
|
? _namedAlgorithm(o.signatureAlgorithm)
|
|
685
723
|
: _proofAlgorithm(c.parsed, o.digestAlgorithm);
|
|
@@ -694,7 +732,30 @@ function _verifyRequest(requesterCertificate, certificate, opts) {
|
|
|
694
732
|
}
|
|
695
733
|
if (sig.bytes.length === 0) throw _err("relatedcert/bad-input", "the proof signature must not be empty");
|
|
696
734
|
|
|
697
|
-
|
|
735
|
+
/** @internal The two facts are reported apart. A bare boolean could not say which check failed, so
|
|
736
|
+
* a key the certificate never authorized for signing and a signature that simply does not verify
|
|
737
|
+
* read the same, and a caller acting on one as if it were the other acts on the wrong fact. */
|
|
738
|
+
return intrinsic.chain(_seam.verify(sigAlg, sig, c.parsed.subjectPublicKeyInfo.bytes, preimage), function (verified) {
|
|
739
|
+
var ok = verified === true && signerMaySign;
|
|
740
|
+
/** @internal The verdict carries what this verb DETERMINED, not an echo of what it was handed.
|
|
741
|
+
* The caller already holds `certID` and `requestTime` on the request it passed in, and copying
|
|
742
|
+
* the decoded issuer onto the result would invite a byte comparison against a name this verb
|
|
743
|
+
* judged equal under the RFC 5280 sec. 7.1 folding: two names that compare equal here are
|
|
744
|
+
* routinely different bytes, and there is no public comparison a caller could use instead.
|
|
745
|
+
* `signatureAlgorithm` is kept because it is derived rather than supplied, so without it a
|
|
746
|
+
* caller cannot tell which digest the proof was actually checked under. */
|
|
747
|
+
return guard.verdict.of({
|
|
748
|
+
valid: ok,
|
|
749
|
+
verified: verified === true,
|
|
750
|
+
signerMaySign: signerMaySign,
|
|
751
|
+
signatureAlgorithm: sigAlg,
|
|
752
|
+
}, ok ? null : {
|
|
753
|
+
code: !signerMaySign ? "relatedcert/signer-may-not-sign" : "relatedcert/bad-signature",
|
|
754
|
+
reason: !signerMaySign
|
|
755
|
+
? "the related certificate's keyUsage does not authorize a proof-of-possession signature (RFC 5280 sec. 4.2.1.3)"
|
|
756
|
+
: "the proof signature does not verify under the key of the certificate certID names",
|
|
757
|
+
});
|
|
758
|
+
});
|
|
698
759
|
}
|
|
699
760
|
|
|
700
761
|
module.exports = {
|
package/lib/schema-attrcert.js
CHANGED
|
@@ -293,7 +293,7 @@ var ATTRIBUTE_CERTIFICATE = pkix.signedEnvelope(NS, ACINFO, {
|
|
|
293
293
|
* var aaCert = await pki.x509.sign({ subject: "Attribute Authority", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
294
294
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
295
295
|
* var der = await pki.attrcert.sign(
|
|
296
|
-
* { holder: { entityName: { directoryName: "
|
|
296
|
+
* { holder: { entityName: { directoryName: "Alice" } },
|
|
297
297
|
* notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
|
|
298
298
|
* attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
|
|
299
299
|
* { cert: aaCert, key: key });
|
|
@@ -350,7 +350,7 @@ function parseV1(input, callerOpts) {
|
|
|
350
350
|
* var aaCert = await pki.x509.sign({ subject: "Attribute Authority", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
351
351
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
352
352
|
* var pemText = await pki.attrcert.sign(
|
|
353
|
-
* { holder: { entityName: { directoryName: "
|
|
353
|
+
* { holder: { entityName: { directoryName: "Alice" } },
|
|
354
354
|
* notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
|
|
355
355
|
* attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
|
|
356
356
|
* { cert: aaCert, key: key }, { pem: true });
|
|
@@ -375,7 +375,7 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label === null ? n
|
|
|
375
375
|
* var aaCert = await pki.x509.sign({ subject: "Attribute Authority", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
376
376
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
377
377
|
* var der = await pki.attrcert.sign(
|
|
378
|
-
* { holder: { entityName: { directoryName: "
|
|
378
|
+
* { holder: { entityName: { directoryName: "Alice" } },
|
|
379
379
|
* notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
|
|
380
380
|
* attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
|
|
381
381
|
* { cert: aaCert, key: key });
|
|
@@ -700,7 +700,7 @@ var DOOR = pkix.extensionDoor({
|
|
|
700
700
|
* var key = await pki.key.export(pair.privateKey);
|
|
701
701
|
* var aaCert = await pki.x509.sign({ subject: "Example AA", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
702
702
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
703
|
-
* var acDer = await pki.attrcert.sign({ holder: { entityName: [{ directoryName: "
|
|
703
|
+
* var acDer = await pki.attrcert.sign({ holder: { entityName: [{ directoryName: "Holder" }] },
|
|
704
704
|
* attributes: { role: { roleName: "https://example.test/role/admin" } }, serialNumber: 1n,
|
|
705
705
|
* notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
|
|
706
706
|
* extensions: { noRevAvail: true } }, { cert: aaCert, key: key });
|
|
@@ -729,7 +729,7 @@ var decodeExtensions = DOOR.decodeExtensions;
|
|
|
729
729
|
* var key = await pki.key.export(pair.privateKey);
|
|
730
730
|
* var aaCert = await pki.x509.sign({ subject: "Example AA", subjectPublicKey: await pki.key.export(pair.publicKey),
|
|
731
731
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
732
|
-
* var acDer = await pki.attrcert.sign({ holder: { entityName: [{ directoryName: "
|
|
732
|
+
* var acDer = await pki.attrcert.sign({ holder: { entityName: [{ directoryName: "Holder" }] },
|
|
733
733
|
* attributes: { role: { roleName: "https://example.test/role/admin" } }, serialNumber: 1n,
|
|
734
734
|
* notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
|
|
735
735
|
* extensions: { noRevAvail: true } }, { cert: aaCert, key: key });
|
package/lib/schema-c509.js
CHANGED
|
@@ -1590,6 +1590,12 @@ function _reconstructDer(r, sigNode) {
|
|
|
1590
1590
|
* reached by an explicit call and is not auto-routed by pki.schema.parse. The type-2 signedData and the
|
|
1591
1591
|
* raw signature are surfaced as-is (a native verifier hashes them without re-serialization).
|
|
1592
1592
|
*
|
|
1593
|
+
* A type-3 ECDSA signature is a fixed-width `r || s` that names no curve, so the same pair padded to a
|
|
1594
|
+
* wider accepted width reconstructs byte-identical DER: 64, 96 and 132 are three spellings of one
|
|
1595
|
+
* certificate. `signatureValueMinimalWidth` reports the narrowest width that represents the value, and
|
|
1596
|
+
* does not fail the parse. A reader that deduplicates, caches or blocklists by encoded form compares it
|
|
1597
|
+
* against `signatureValue.length`, and re-narrows to it for a key that is the same for every spelling.
|
|
1598
|
+
*
|
|
1593
1599
|
* @example
|
|
1594
1600
|
* // the RFC 7925 profiled certificate from draft-ietf-cose-cbor-encoded-cert Appendix A.1 (type 3)
|
|
1595
1601
|
* var bytes = Buffer.from(
|
|
@@ -1703,7 +1709,10 @@ function _finishParse(f, root, nested, fields) {
|
|
|
1703
1709
|
result._fieldBytes = root.bytes.subarray(1, root.bytes.length - f[10].bytes.length);
|
|
1704
1710
|
if (type === 2) result.signedData = result._fieldBytes;
|
|
1705
1711
|
|
|
1706
|
-
if (type === 3)
|
|
1712
|
+
if (type === 3) {
|
|
1713
|
+
result.reconstructedDer = _reconstructDer(result, f[10]);
|
|
1714
|
+
result.signatureValueMinimalWidth = _minimalSigWidth(signatureValue);
|
|
1715
|
+
}
|
|
1707
1716
|
return result;
|
|
1708
1717
|
}
|
|
1709
1718
|
|
|
@@ -1960,11 +1969,37 @@ function _keyUsageBitsFromDer(extnValue, ctx) {
|
|
|
1960
1969
|
var _NO_EXPIRY = C.TIME.seconds(Number(NO_EXPIRY_SECONDS));
|
|
1961
1970
|
var NODE_TO_WEBCRYPTO = Object.assign(Object.create(null), { "prime256v1": "P-256", "secp384r1": "P-384", "secp521r1": "P-521" });
|
|
1962
1971
|
var WEBCRYPTO_FIELD_BYTES = Object.assign(Object.create(null), { "P-256": 32, "P-384": 48, "P-521": 66 });
|
|
1972
|
+
/** @internal The accepted fixed-width `r || s` lengths, derived from the field sizes above rather
|
|
1973
|
+
* than written a fourth time. */
|
|
1974
|
+
var EC_SIG_WIDTHS = Object.keys(WEBCRYPTO_FIELD_BYTES).map(function (n) { return WEBCRYPTO_FIELD_BYTES[n] * 2; });
|
|
1963
1975
|
var SIG_ALG_TO_CURVE = Object.assign(Object.create(null), { "ecdsaWithSHA256": "P-256", "ecdsaWithSHA384": "P-384", "ecdsaWithSHA512": "P-521" });
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1976
|
+
/** @internal The significant byte count of a big-endian magnitude in `buf[from, to)`: the leading
|
|
1977
|
+
* zero bytes do not change the value they encode. Taken as a range so the fixed-width halves of an
|
|
1978
|
+
* r||s signature and a decoded INTEGER's content are measured by the one scan. */
|
|
1979
|
+
function _magBytes(buf, from, to) {
|
|
1980
|
+
var i = from;
|
|
1981
|
+
while (i < to - 1 && buf[i] === 0x00) i++;
|
|
1982
|
+
return to - i;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
/** @internal The narrowest accepted r||s width that encodes the same (r, s) as `sig`. A fixed-width
|
|
1986
|
+
* r||s carries no curve, so padding each half of a 64-byte signature to 32 more bytes reconstructs
|
|
1987
|
+
* byte-identical DER: 64, 96 and 132 are three spellings of one certificate, which makes a C509 byte
|
|
1988
|
+
* string something other than a unique identifier for what it encodes. Neither the width nor the
|
|
1989
|
+
* issuer's curve is recoverable from the bytes, and `parse` takes no option to be told, so this is
|
|
1990
|
+
* reported and the caller judges. The widths come from the field-size table rather than a fourth
|
|
1991
|
+
* copy of 32/48/66. */
|
|
1992
|
+
function _minimalSigWidth(sig) {
|
|
1993
|
+
var half = sig.length / 2;
|
|
1994
|
+
var need = _magBytes(sig, 0, half);
|
|
1995
|
+
var other = _magBytes(sig, half, sig.length);
|
|
1996
|
+
if (other > need) need = other;
|
|
1997
|
+
var narrowest = sig.length;
|
|
1998
|
+
for (var i = 0; i < EC_SIG_WIDTHS.length; i++) {
|
|
1999
|
+
var width = EC_SIG_WIDTHS[i];
|
|
2000
|
+
if (width >= need * 2 && width <= sig.length && width < narrowest) narrowest = width;
|
|
2001
|
+
}
|
|
2002
|
+
return narrowest;
|
|
1968
2003
|
}
|
|
1969
2004
|
function _sigMagWidth(derSig) {
|
|
1970
2005
|
var n;
|
|
@@ -1975,7 +2010,8 @@ function _sigMagWidth(derSig) {
|
|
|
1975
2010
|
var ch = n.children[i];
|
|
1976
2011
|
if (ch.tagNumber !== asn1.TAGS.INTEGER || ch.tagClass !== "universal" || ch.constructed) throw _err("c509/bad-signature", "the ECDSA issuer signature r and s must be universal INTEGERs");
|
|
1977
2012
|
}
|
|
1978
|
-
|
|
2013
|
+
var r0 = n.children[0].content, s0 = n.children[1].content;
|
|
2014
|
+
return Math.max(_magBytes(r0, 0, r0.length), _magBytes(s0, 0, s0.length));
|
|
1979
2015
|
}
|
|
1980
2016
|
var CURVE_ORDER = ["P-256", "P-384", "P-521"];
|
|
1981
2017
|
function _minCurveForMag(mag) {
|
package/lib/schema-cmp.js
CHANGED
|
@@ -803,7 +803,7 @@ var readName = _valueParser("Name", pkix.name(NS));
|
|
|
803
803
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
804
804
|
* var csrDer = await pki.csr.sign({ subject: "client", subjectPublicKey: await pki.key.export(pair.publicKey) }, { key: key });
|
|
805
805
|
* var der = await pki.cmp.build(
|
|
806
|
-
* { header: { sender: { directoryName: "
|
|
806
|
+
* { header: { sender: { directoryName: "client" }, recipient: { directoryName: "CA" } },
|
|
807
807
|
* body: { p10cr: csrDer } }, { key: key, cert: cert });
|
|
808
808
|
* var m = pki.schema.cmp.parse(der);
|
|
809
809
|
* m.body.arm; // "ir", "ip", "error", ...
|
|
@@ -837,7 +837,7 @@ var parse = pkix.makeParser({
|
|
|
837
837
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
838
838
|
* var csrDer = await pki.csr.sign({ subject: "client", subjectPublicKey: await pki.key.export(pair.publicKey) }, { key: key });
|
|
839
839
|
* var msg = await pki.cmp.build(
|
|
840
|
-
* { header: { sender: { directoryName: "
|
|
840
|
+
* { header: { sender: { directoryName: "client" }, recipient: { directoryName: "CA" } },
|
|
841
841
|
* body: { p10cr: csrDer } }, { key: key, cert: cert });
|
|
842
842
|
* var pemText = pki.schema.cmp.pemEncode(msg);
|
|
843
843
|
* var der = pki.schema.cmp.pemDecode(pemText);
|
|
@@ -861,7 +861,7 @@ function pemDecode(text, label) { return pkix.pemDecode(text, label || "CMP", Pe
|
|
|
861
861
|
* notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
|
|
862
862
|
* var csrDer = await pki.csr.sign({ subject: "client", subjectPublicKey: await pki.key.export(pair.publicKey) }, { key: key });
|
|
863
863
|
* var der = await pki.cmp.build(
|
|
864
|
-
* { header: { sender: { directoryName: "
|
|
864
|
+
* { header: { sender: { directoryName: "client" }, recipient: { directoryName: "CA" } },
|
|
865
865
|
* body: { p10cr: csrDer } }, { key: key, cert: cert });
|
|
866
866
|
* var pem = pki.schema.cmp.pemEncode(der);
|
|
867
867
|
*/
|