@blamejs/pki 0.8.0 → 0.8.2
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 +50 -0
- package/README.md +9 -9
- package/index.js +3 -1
- package/lib/asn1-der.js +1 -1
- package/lib/cbor-det.js +1 -1
- package/lib/cmc-build.js +1 -2
- package/lib/cms-decrypt.js +3 -1
- package/lib/cms-digest.js +191 -0
- package/lib/cms-verify.js +3 -2
- package/lib/guard-der.js +26 -1
- package/lib/guard-name.js +124 -1
- package/lib/guard-text.js +1 -1
- package/lib/oid.js +18 -6
- package/lib/path-validate.js +9 -4
- package/lib/pki-build.js +63 -6
- package/lib/schema-cms.js +28 -1
- package/lib/schema-pkix.js +5 -4
- package/lib/x509-sign.js +100 -1
- package/package.json +4 -2
- package/sbom.cdx.json +6 -6
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
/**
|
|
5
|
+
* @module pki.cms
|
|
6
|
+
* @nav Messaging
|
|
7
|
+
* @title CMS DigestedData
|
|
8
|
+
* @order 95
|
|
9
|
+
* @slug cms-digest
|
|
10
|
+
*
|
|
11
|
+
* @intro
|
|
12
|
+
* RFC 5652 sec. 7 DigestedData, the integrity-only CMS content type. It carries the
|
|
13
|
+
* content, the digest algorithm, and the digest over the content octets. There is no
|
|
14
|
+
* signer and no key, so it establishes that content matches a digest and nothing about
|
|
15
|
+
* who produced either: its worth is the worth of the channel the message and the digest
|
|
16
|
+
* traveled over. `pki.cms.sign` establishes origin, `pki.cms.authenticate` establishes
|
|
17
|
+
* origin under a shared key.
|
|
18
|
+
*
|
|
19
|
+
* The version follows the encapsulated type, 0 for `id-data` and 2 for anything else.
|
|
20
|
+
* The verifying half recomputes the digest and compares it in constant time, and its
|
|
21
|
+
* verdict carries no signer and no trust field, so it cannot be read as a signature.
|
|
22
|
+
*
|
|
23
|
+
* @spec RFC 5652 sec. 7
|
|
24
|
+
* @card
|
|
25
|
+
* Build and verify RFC 5652 DigestedData: SHA-256/384/512 over the content octets,
|
|
26
|
+
* attached or detached, constant-time comparison, fail-closed.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
var nodeCrypto = require("node:crypto");
|
|
30
|
+
var oid = require("./oid");
|
|
31
|
+
var schemaCms = require("./schema-cms");
|
|
32
|
+
var frameworkError = require("./framework-error");
|
|
33
|
+
var guard = require("./guard-all");
|
|
34
|
+
var intrinsic = require("./guard-intrinsic");
|
|
35
|
+
var asn1 = require("./asn1-der");
|
|
36
|
+
var b = asn1.build;
|
|
37
|
+
|
|
38
|
+
var CmsError = frameworkError.CmsError;
|
|
39
|
+
function _err(code, message, cause) { return new CmsError(code, message, cause); }
|
|
40
|
+
function O(name) { return oid.byName(name); }
|
|
41
|
+
|
|
42
|
+
var OID_DATA = O("data");
|
|
43
|
+
var OID_DIGESTED_DATA = O("digestedData");
|
|
44
|
+
|
|
45
|
+
var NODE_DIGEST = intrinsic.assign(intrinsic.create(null), {
|
|
46
|
+
sha256: "sha256", sha384: "sha384", sha512: "sha512",
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
var _DIGEST_OPTS = intrinsic.assign(intrinsic.create(null), {
|
|
50
|
+
digestAlgorithm: 1, contentType: 1, detached: 1, pem: 1,
|
|
51
|
+
});
|
|
52
|
+
var _VERIFY_DIGEST_OPTS = intrinsic.assign(intrinsic.create(null), { content: 1 });
|
|
53
|
+
|
|
54
|
+
function _digestName(opts) {
|
|
55
|
+
var name = opts.digestAlgorithm === undefined ? "sha256" : opts.digestAlgorithm;
|
|
56
|
+
var key = guard.text.keyOf(name);
|
|
57
|
+
if (!NODE_DIGEST[key]) {
|
|
58
|
+
throw _err("cms/unsupported-algorithm", "unsupported digest algorithm " + guard.text.showValue(name) +
|
|
59
|
+
" (sha256 / sha384 / sha512)");
|
|
60
|
+
}
|
|
61
|
+
return key;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @primitive pki.cms.digest
|
|
66
|
+
* @signature pki.cms.digest(content, opts?) -> Promise<Buffer|string>
|
|
67
|
+
* @since 0.8.1
|
|
68
|
+
* @status stable
|
|
69
|
+
* @spec RFC 5652 sec. 7
|
|
70
|
+
* @related pki.cms.verifyDigest, pki.cms.sign, pki.cms.authenticate
|
|
71
|
+
*
|
|
72
|
+
* Build an RFC 5652 `DigestedData`: the content, the digest algorithm, and the digest over
|
|
73
|
+
* the content octets. It carries no signer and no key, so it establishes that content
|
|
74
|
+
* matches a digest and nothing about who produced either. Use `pki.cms.sign` for origin,
|
|
75
|
+
* or `pki.cms.authenticate` for origin under a shared key.
|
|
76
|
+
*
|
|
77
|
+
* The version follows the encapsulated type, 0 for `id-data` and 2 for anything else.
|
|
78
|
+
*
|
|
79
|
+
* @opts
|
|
80
|
+
* - `digestAlgorithm` (string) -- `sha256` (default), `sha384` or `sha512`.
|
|
81
|
+
* - `contentType` (string) -- the encapsulated content type by OID name, default `data`.
|
|
82
|
+
* - `detached` (boolean) -- omit the content, leaving the digest to travel alone.
|
|
83
|
+
* - `pem` (boolean) -- return a PEM `CMS` block instead of DER.
|
|
84
|
+
* @example
|
|
85
|
+
* var der = await pki.cms.digest(Buffer.from("hello"));
|
|
86
|
+
* (await pki.cms.verifyDigest(der)).valid; // -> true
|
|
87
|
+
*/
|
|
88
|
+
function digest(content, opts) {
|
|
89
|
+
return guard.bytes.fixedCall(CmsError, "cms/bad-input", [
|
|
90
|
+
[content, "content"], [opts, "pki.cms.digest options"],
|
|
91
|
+
], _digest);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function _digest(content, opts) {
|
|
95
|
+
opts = guard.identifier.optionsObject(opts, _err, "cms/bad-input", "pki.cms.digest options");
|
|
96
|
+
guard.identifier.assertKnownKeys(opts, _DIGEST_OPTS, _err, "cms/bad-input",
|
|
97
|
+
"pki.cms.digest has an unknown option ");
|
|
98
|
+
var raw = guard.bytes.source(content, CmsError, "cms/bad-input", "content");
|
|
99
|
+
var alg = _digestName(opts);
|
|
100
|
+
var ctName = opts.contentType === undefined ? "data" : opts.contentType;
|
|
101
|
+
var ctOid = O(ctName);
|
|
102
|
+
if (!ctOid) throw _err("cms/bad-input", "opts.contentType is not a known OID name: " + guard.text.showValue(ctName));
|
|
103
|
+
|
|
104
|
+
var value = nodeCrypto.createHash(NODE_DIGEST[alg]).update(raw).digest();
|
|
105
|
+
var eciChildren = opts.detached === true
|
|
106
|
+
? [b.oid(ctOid)]
|
|
107
|
+
: [b.oid(ctOid), b.explicit(0, b.octetString(raw))];
|
|
108
|
+
var dd = b.sequence([
|
|
109
|
+
b.integer(ctOid === OID_DATA ? 0 : 2),
|
|
110
|
+
b.sequence([b.oid(O(alg))]),
|
|
111
|
+
b.sequence(eciChildren),
|
|
112
|
+
b.octetString(value),
|
|
113
|
+
]);
|
|
114
|
+
var ci = b.sequence([b.oid(OID_DIGESTED_DATA), b.explicit(0, dd)]);
|
|
115
|
+
return opts.pem === true ? schemaCms.pemEncode(ci, "CMS") : ci;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @primitive pki.cms.verifyDigest
|
|
120
|
+
* @signature pki.cms.verifyDigest(input, opts?) -> Promise<verdict>
|
|
121
|
+
* @since 0.8.1
|
|
122
|
+
* @status stable
|
|
123
|
+
* @spec RFC 5652 sec. 7
|
|
124
|
+
* @related pki.cms.digest, pki.cms.verify
|
|
125
|
+
*
|
|
126
|
+
* Recompute the digest of a `DigestedData`'s content and compare it with the digest the
|
|
127
|
+
* message carries, in constant time. Returns `{ valid, digestAlgorithm, content, contentType }`,
|
|
128
|
+
* with `code` set to `cms/digest-mismatch` when the content does not match.
|
|
129
|
+
*
|
|
130
|
+
* A DigestedData proves neither origin nor authority. A caller that needs either verifies a
|
|
131
|
+
* `SignedData` instead; this verdict carries no signer and no trust field, so it cannot be
|
|
132
|
+
* mistaken for one.
|
|
133
|
+
*
|
|
134
|
+
* @opts
|
|
135
|
+
* - `content` (BufferSource) -- the detached content the digest covers. Required when the
|
|
136
|
+
* message omits its own content, refused as redundant when it carries it.
|
|
137
|
+
* @example
|
|
138
|
+
* var der = await pki.cms.digest(Buffer.from("hello"));
|
|
139
|
+
* var v = await pki.cms.verifyDigest(der);
|
|
140
|
+
* v.valid; // -> true
|
|
141
|
+
* v.digestAlgorithm; // -> "sha256"
|
|
142
|
+
*/
|
|
143
|
+
function verifyDigest(input, opts) {
|
|
144
|
+
return guard.bytes.fixedCall(CmsError, "cms/bad-input", [
|
|
145
|
+
[input, "the CMS message"], [opts, "pki.cms.verifyDigest options"],
|
|
146
|
+
], _verifyDigest);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function _verifyDigest(input, opts) {
|
|
150
|
+
opts = guard.identifier.optionsObject(opts, _err, "cms/bad-input", "pki.cms.verifyDigest options");
|
|
151
|
+
guard.identifier.assertKnownKeys(opts, _VERIFY_DIGEST_OPTS, _err, "cms/bad-input",
|
|
152
|
+
"pki.cms.verifyDigest has an unknown option ");
|
|
153
|
+
var parsed = guard.parsed.acceptDerived(input, "cms", function (bytes) {
|
|
154
|
+
return schemaCms.parse(bytes);
|
|
155
|
+
}, _err, "cms/bad-input", "the DigestedData");
|
|
156
|
+
if (parsed.contentType !== OID_DIGESTED_DATA) {
|
|
157
|
+
throw _err("cms/not-a-digested-data", "pki.cms.verifyDigest reads a DigestedData (RFC 5652 sec. 7) and this is " +
|
|
158
|
+
(oid.name(parsed.contentType) || parsed.contentType));
|
|
159
|
+
}
|
|
160
|
+
var alg = guard.text.keyOf(parsed.digestAlgorithm.name || "");
|
|
161
|
+
if (!NODE_DIGEST[alg]) {
|
|
162
|
+
throw _err("cms/unsupported-algorithm", "unsupported digest algorithm " +
|
|
163
|
+
guard.text.showValue(parsed.digestAlgorithm.name || parsed.digestAlgorithm.oid));
|
|
164
|
+
}
|
|
165
|
+
guard.der.assertParams(parsed.digestAlgorithm.parameters, "absentOrNull", _err,
|
|
166
|
+
"cms/unsupported-algorithm", parsed.digestAlgorithm.name + " digest algorithm (RFC 5754 sec. 2)");
|
|
167
|
+
var embedded = parsed.encapContentInfo.eContent;
|
|
168
|
+
var supplied = opts.content === undefined ? null
|
|
169
|
+
: guard.bytes.snapshotSource(opts.content, CmsError, "cms/bad-input", "opts.content");
|
|
170
|
+
if (embedded != null && supplied != null) {
|
|
171
|
+
throw _err("cms/bad-input", "the DigestedData carries its own content, so opts.content is redundant");
|
|
172
|
+
}
|
|
173
|
+
if (embedded == null && supplied == null) {
|
|
174
|
+
throw _err("cms/bad-input", "the DigestedData is detached, so opts.content must supply the content it covers");
|
|
175
|
+
}
|
|
176
|
+
var covered = embedded != null
|
|
177
|
+
? guard.bytes.snapshotSource(embedded, CmsError, "cms/bad-input", "the encapsulated content")
|
|
178
|
+
: supplied;
|
|
179
|
+
var recomputed = nodeCrypto.createHash(NODE_DIGEST[alg]).update(covered).digest();
|
|
180
|
+
var matched = guard.crypto.constantTimeEqual(recomputed, parsed.digest);
|
|
181
|
+
return guard.verdict.of({
|
|
182
|
+
valid: matched,
|
|
183
|
+
code: matched ? undefined : "cms/digest-mismatch",
|
|
184
|
+
digestAlgorithm: alg,
|
|
185
|
+
contentType: parsed.encapContentInfo.eContentType,
|
|
186
|
+
contentTypeName: oid.name(parsed.encapContentInfo.eContentType) || null,
|
|
187
|
+
content: covered,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
module.exports = { digest: digest, verifyDigest: verifyDigest };
|
package/lib/cms-verify.js
CHANGED
|
@@ -38,6 +38,7 @@ var cmsSign = require("./cms-sign");
|
|
|
38
38
|
var cmsEncrypt = require("./cms-encrypt");
|
|
39
39
|
var cmsDecrypt = require("./cms-decrypt");
|
|
40
40
|
var cmsCompress = require("./cms-compress");
|
|
41
|
+
var cmsDigest = require("./cms-digest");
|
|
41
42
|
var signScheme = require("./sign-scheme");
|
|
42
43
|
var _mldsaDigestSuitable = signScheme.mldsaDigestSuitable;
|
|
43
44
|
var SLHDSA_BY_OID = signScheme.SLHDSA_BY_OID;
|
|
@@ -105,7 +106,7 @@ var SIG_SCHEME = intrinsic.assign(intrinsic.create(null), {
|
|
|
105
106
|
["sha2-128s", "sha2-128f", "sha2-192s", "sha2-192f", "sha2-256s", "sha2-256f",
|
|
106
107
|
"shake-128s", "shake-128f", "shake-192s", "shake-192f", "shake-256s", "shake-256f"
|
|
107
108
|
].forEach(function (s) { SIG_SCHEME["id-slh-dsa-" + s] = { kind: "slhdsa", name: "SLH-DSA-" + s.toUpperCase(), params: "absent", sameKeyOid: true, digest: SLHDSA_BY_OID[oid.byName("id-slh-dsa-" + s)].digest }; });
|
|
108
|
-
function _isDerNull(p) { return
|
|
109
|
+
function _isDerNull(p) { return guard.der.isNull(p); }
|
|
109
110
|
function _algParamsOk(shape, p) { return shape === "null" ? _isDerNull(p) : (p === null || p === undefined); }
|
|
110
111
|
var EC_CURVE = intrinsic.create(null);
|
|
111
112
|
EC_CURVE[oid.byName("prime256v1")] = { curve: "P-256", coordLen: 32 };
|
|
@@ -1120,4 +1121,4 @@ function isCertsOnly(input) {
|
|
|
1120
1121
|
}
|
|
1121
1122
|
|
|
1122
1123
|
// @internal
|
|
1123
|
-
module.exports = { verify: verify, sign: sign, countersign: countersign, encrypt: encrypt, authenticate: authenticate, decrypt: decrypt, compress: compress, decompress: decompress, certsOnly: certsOnly, parseCertsOnly: parseCertsOnly, isCertsOnly: isCertsOnly, setEngine: setEngine };
|
|
1124
|
+
module.exports = { verify: verify, sign: sign, countersign: countersign, encrypt: encrypt, authenticate: authenticate, decrypt: decrypt, compress: compress, decompress: decompress, digest: cmsDigest.digest, verifyDigest: cmsDigest.verifyDigest, certsOnly: certsOnly, parseCertsOnly: parseCertsOnly, isCertsOnly: isCertsOnly, setEngine: setEngine };
|
package/lib/guard-der.js
CHANGED
|
@@ -7,6 +7,7 @@ var _freeze = require("./guard-intrinsic").freeze;
|
|
|
7
7
|
var _forEach = require("./guard-intrinsic").forEach;
|
|
8
8
|
var _sizeOf = require("./guard-intrinsic").sizeOf;
|
|
9
9
|
var _create = require("./guard-intrinsic").create;
|
|
10
|
+
var _isBuffer = require("./guard-intrinsic").isBuffer;
|
|
10
11
|
|
|
11
12
|
var asn1 = null;
|
|
12
13
|
function _asn1() { if (asn1 === null) asn1 = require("./asn1-der"); return asn1; }
|
|
@@ -84,4 +85,28 @@ function tlv(content, E, code, label) {
|
|
|
84
85
|
return content;
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
// @enforced-by guard-shape-reinlined
|
|
89
|
+
// @guard-shape \[0\] === 0x05
|
|
90
|
+
function isNull(params) {
|
|
91
|
+
return _isBuffer(params) && _sizeOf(params) === 2 && params[0] === 0x05 && params[1] === 0x00;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// @enforced-by behavioral -- absence has no rename-proof shape; the RED vector per verb is the guard
|
|
95
|
+
function paramsAbsentOrNull(params) {
|
|
96
|
+
return params === null || params === undefined || isNull(params);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// @enforced-by behavioral -- see paramsAbsentOrNull.
|
|
100
|
+
function assertParams(params, shape, E, code, label) {
|
|
101
|
+
var ok = shape === "null" ? isNull(params) : (shape === "absent"
|
|
102
|
+
? (params === null || params === undefined)
|
|
103
|
+
: paramsAbsentOrNull(params));
|
|
104
|
+
if (ok) return params;
|
|
105
|
+
throw E(code, "the " + label + " parameters must be " + (shape === "null"
|
|
106
|
+
? "DER NULL" : (shape === "absent" ? "absent" : "absent or DER NULL")));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = _freeze({
|
|
110
|
+
element: element, tlv: tlv,
|
|
111
|
+
isNull: isNull, paramsAbsentOrNull: paramsAbsentOrNull, assertParams: assertParams,
|
|
112
|
+
});
|
package/lib/guard-name.js
CHANGED
|
@@ -21,6 +21,10 @@ var _numToString = intrinsic.uncurry(Number.prototype.toString);
|
|
|
21
21
|
var _create = intrinsic.create;
|
|
22
22
|
var _hasOwn = intrinsic.hasOwn;
|
|
23
23
|
var _fromCharCode = String.fromCharCode;
|
|
24
|
+
var _bufferFrom = intrinsic.bufferFrom;
|
|
25
|
+
var _TextDecoderCtor = TextDecoder;
|
|
26
|
+
var _utf8Fatal = new _TextDecoderCtor("utf-8", { fatal: true, ignoreBOM: true });
|
|
27
|
+
var _decodeUtf8 = intrinsic.uncurry(_TextDecoderCtor.prototype.decode);
|
|
24
28
|
var _String = String;
|
|
25
29
|
var _isArray = Array.isArray;
|
|
26
30
|
var _isBuffer = intrinsic.isBuffer;
|
|
@@ -159,6 +163,124 @@ function escapeControlBytes(str) {
|
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
var DN_SPECIAL = intrinsic.assign(intrinsic.create(null), { 0x2c: 1, 0x2b: 1, 0x22: 1, 0x5c: 1, 0x3c: 1, 0x3e: 1, 0x3b: 1 });
|
|
166
|
+
|
|
167
|
+
function _hexNibble(c) {
|
|
168
|
+
if (c >= 0x30 && c <= 0x39) return c - 0x30;
|
|
169
|
+
if (c >= 0x41 && c <= 0x46) return c - 0x41 + 10;
|
|
170
|
+
if (c >= 0x61 && c <= 0x66) return c - 0x61 + 10;
|
|
171
|
+
return -1;
|
|
172
|
+
}
|
|
173
|
+
function _isAttrTypeStart(c) { return (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a); }
|
|
174
|
+
function _isAttrTypeChar(c) { return _isAttrTypeStart(c) || (c >= 0x30 && c <= 0x39) || c === 0x2d; }
|
|
175
|
+
function _isDigit(c) { return c >= 0x30 && c <= 0x39; }
|
|
176
|
+
|
|
177
|
+
// @enforced-by behavioral -- the descriptor grammar has no rename-proof shape distinct
|
|
178
|
+
function isAttributeDescriptor(name) {
|
|
179
|
+
if (typeof name !== "string" || name.length === 0) return false;
|
|
180
|
+
if (!_isAttrTypeStart(_charCodeAt(name, 0))) return false;
|
|
181
|
+
for (var i = 1; i < name.length; i++) {
|
|
182
|
+
if (!_isAttrTypeChar(_charCodeAt(name, i))) return false;
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// @enforced-by behavioral -- the inverse of escapeDnValue has no rename-proof shape distinct
|
|
188
|
+
function parseDnString(str, E, code, label) {
|
|
189
|
+
if (typeof str !== "string") throw E(code, label + " must be a string");
|
|
190
|
+
var rdns = [], atvs = [], i = 0, n = str.length;
|
|
191
|
+
if (n === 0) return rdns;
|
|
192
|
+
for (;;) {
|
|
193
|
+
var typeStart = i;
|
|
194
|
+
var first = _charCodeAt(str, i);
|
|
195
|
+
if (_isDigit(first)) {
|
|
196
|
+
while (i < n && (_isDigit(_charCodeAt(str, i)) || _charCodeAt(str, i) === 0x2e)) i++;
|
|
197
|
+
} else if (_isAttrTypeStart(first)) {
|
|
198
|
+
while (i < n && _isAttrTypeChar(_charCodeAt(str, i))) i++;
|
|
199
|
+
}
|
|
200
|
+
if (i === typeStart) throw E(code, label + " has a component with no attribute type");
|
|
201
|
+
var type = _strSlice(str, typeStart, i);
|
|
202
|
+
if (i >= n || _charCodeAt(str, i) !== 0x3d) throw E(code, label + " component " + type + " has no '=' after its attribute type");
|
|
203
|
+
i++;
|
|
204
|
+
|
|
205
|
+
var value = "", hex = null, pending = [];
|
|
206
|
+
function flushPending() {
|
|
207
|
+
if (pending.length === 0) return;
|
|
208
|
+
var decoded;
|
|
209
|
+
try { decoded = _decodeUtf8(_utf8Fatal, _bufferFrom(pending)); }
|
|
210
|
+
catch (e) { throw E(code, label + " has a run of '\\' hex escapes that is not valid UTF-8 (RFC 4514 sec. 3)", e); }
|
|
211
|
+
value += decoded;
|
|
212
|
+
pending = [];
|
|
213
|
+
}
|
|
214
|
+
if (i < n && _charCodeAt(str, i) === 0x23) {
|
|
215
|
+
i++;
|
|
216
|
+
var hexStart = i;
|
|
217
|
+
while (i < n && _hexNibble(_charCodeAt(str, i)) >= 0) i++;
|
|
218
|
+
var span = i - hexStart;
|
|
219
|
+
if (span === 0 || (span & 1) === 1) throw E(code, label + " has a '#' value that is not an even number of hex digits");
|
|
220
|
+
hex = _strSlice(str, hexStart, i);
|
|
221
|
+
} else {
|
|
222
|
+
var valueStart = i, lastWasBareSpace = false;
|
|
223
|
+
for (; i < n; i++) {
|
|
224
|
+
var c = _charCodeAt(str, i);
|
|
225
|
+
if (c === 0x2c || c === 0x2b) break;
|
|
226
|
+
if (c !== 0x5c) {
|
|
227
|
+
if (c === 0x00 || c === 0x22 || c === 0x3b || c === 0x3c || c === 0x3e) {
|
|
228
|
+
throw E(code, label + " has an unescaped character RFC 4514 sec. 3 requires to be escaped");
|
|
229
|
+
}
|
|
230
|
+
if (c === 0x20 && i === valueStart) {
|
|
231
|
+
throw E(code, label + " has an unescaped leading space, which RFC 4514 sec. 3 requires to be escaped");
|
|
232
|
+
}
|
|
233
|
+
lastWasBareSpace = c === 0x20;
|
|
234
|
+
} else {
|
|
235
|
+
lastWasBareSpace = false;
|
|
236
|
+
}
|
|
237
|
+
if (c === 0x5c) {
|
|
238
|
+
i++;
|
|
239
|
+
if (i >= n) throw E(code, label + " ends with a trailing '\\' that escapes nothing");
|
|
240
|
+
var e1 = _charCodeAt(str, i);
|
|
241
|
+
var h1 = _hexNibble(e1);
|
|
242
|
+
if (h1 >= 0) {
|
|
243
|
+
if (i + 1 >= n) throw E(code, label + " has a '\\' hex escape with only one digit");
|
|
244
|
+
var h2 = _hexNibble(_charCodeAt(str, i + 1));
|
|
245
|
+
if (h2 < 0) throw E(code, label + " has a '\\' hex escape whose second digit is not hex");
|
|
246
|
+
_append(pending, h1 * 16 + h2);
|
|
247
|
+
i++;
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
if (e1 !== 0x20 && e1 !== 0x23 && e1 !== 0x3d && DN_SPECIAL[e1] !== 1) {
|
|
251
|
+
throw E(code, label + " escapes a character RFC 4514 sec. 2.4 does not give a meaning to");
|
|
252
|
+
}
|
|
253
|
+
flushPending();
|
|
254
|
+
value += _charAt(str, i);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
flushPending();
|
|
258
|
+
value += _charAt(str, i);
|
|
259
|
+
}
|
|
260
|
+
flushPending();
|
|
261
|
+
if (lastWasBareSpace) {
|
|
262
|
+
throw E(code, label + " has an unescaped trailing space, which RFC 4514 sec. 3 requires to be escaped");
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
_append(atvs, { type: type, value: hex === null ? value : null, hex: hex });
|
|
266
|
+
|
|
267
|
+
if (i >= n) { _append(rdns, atvs); return rdns; }
|
|
268
|
+
var sep = _charCodeAt(str, i);
|
|
269
|
+
if (sep !== 0x2c && sep !== 0x2b) {
|
|
270
|
+
throw E(code, label + " has a component followed by a character that is not a separator");
|
|
271
|
+
}
|
|
272
|
+
i++;
|
|
273
|
+
if (sep === 0x2b) {
|
|
274
|
+
if (i >= n) throw E(code, label + " ends with a '+' that joins nothing");
|
|
275
|
+
} else {
|
|
276
|
+
_append(rdns, atvs);
|
|
277
|
+
atvs = [];
|
|
278
|
+
if (i >= n) throw E(code, label + " ends with a separator that joins nothing");
|
|
279
|
+
while (i < n && _charCodeAt(str, i) === 0x20) i++;
|
|
280
|
+
if (i >= n) throw E(code, label + " ends with a separator that joins nothing");
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
162
284
|
// @enforced-by behavioral -- the escaping has no rename-proof code shape distinct
|
|
163
285
|
function escapeDnValue(v) {
|
|
164
286
|
var s = _String(v), out = "";
|
|
@@ -535,7 +657,8 @@ module.exports = intrinsic.freeze({
|
|
|
535
657
|
capturedRdns: capturedRdns, copyDnRdns: copyDnRdns,
|
|
536
658
|
assertNoControlBytes: assertNoControlBytes, assertPrintableIa5: assertPrintableIa5,
|
|
537
659
|
dnEqual: dnEqual, rdnEqual: rdnEqual, emailEqual: emailEqual, dpnCorresponds: dpnCorresponds,
|
|
538
|
-
escapeControlBytes: escapeControlBytes, escapeDnValue: escapeDnValue,
|
|
660
|
+
escapeControlBytes: escapeControlBytes, escapeDnValue: escapeDnValue, parseDnString: parseDnString,
|
|
661
|
+
isAttributeDescriptor: isAttributeDescriptor,
|
|
539
662
|
lowerAscii: lowerAscii, uriSchemeIs: uriSchemeIs,
|
|
540
663
|
stripTrailingDot: stripTrailingDot, isFqdnHost: isFqdnHost, splitMailbox: splitMailbox,
|
|
541
664
|
constraintBaseRefusal: constraintBaseRefusal,
|
package/lib/guard-text.js
CHANGED
|
@@ -24,7 +24,7 @@ function decode(input, maxBytes, ErrorClass, spec) {
|
|
|
24
24
|
input = bytes.source(input, ErrorClass, spec.badInput, spec.label);
|
|
25
25
|
if (bytes.lengthOf(input) > maxBytes) throw new ErrorClass(spec.tooLarge, spec.label + " exceeds the size cap");
|
|
26
26
|
if (spec.fatal) {
|
|
27
|
-
try { return _decode(new _TextDecoder(charset, { fatal: true }), input); }
|
|
27
|
+
try { return _decode(new _TextDecoder(charset, { fatal: true, ignoreBOM: true }), input); }
|
|
28
28
|
catch (e) { throw new ErrorClass(spec.badDecode, spec.label + " is not valid " + charset, e); }
|
|
29
29
|
}
|
|
30
30
|
return _toString(input, charset);
|
package/lib/oid.js
CHANGED
|
@@ -46,8 +46,13 @@ var FAMILIES = Object.assign(Object.create(null), {
|
|
|
46
46
|
attributeType: { base: [2, 5, 4], of: {
|
|
47
47
|
commonName: 3, surname: 4, serialNumber: 5, countryName: 6,
|
|
48
48
|
localityName: 7, stateOrProvinceName: 8, streetAddress: 9,
|
|
49
|
-
organizationName: 10, organizationalUnitName: 11, title: 12,
|
|
50
|
-
|
|
49
|
+
organizationName: 10, organizationalUnitName: 11, title: 12,
|
|
50
|
+
businessCategory: 15, postalCode: 17, name: 41, givenName: 42,
|
|
51
|
+
initials: 43, generationQualifier: 44, distinguishedNameQualifier: 46,
|
|
52
|
+
clearance: 55, pseudonym: 65, role: 72, organizationIdentifier: 97 } },
|
|
53
|
+
|
|
54
|
+
cabfJurisdiction: { base: [1, 3, 6, 1, 4, 1, 311, 60, 2, 1], of: {
|
|
55
|
+
jurisdictionLocalityName: 1, jurisdictionStateOrProvinceName: 2, jurisdictionCountryName: 3 } },
|
|
51
56
|
|
|
52
57
|
certExtension: { base: [2, 5, 29], of: {
|
|
53
58
|
subjectDirectoryAttributes: 9,
|
|
@@ -265,12 +270,19 @@ var FAMILIES = Object.assign(Object.create(null), {
|
|
|
265
270
|
asymmDecryptKeyID: 54, certificationRequestInfoTemplate: 61, extensionReqTemplate: 62 } },
|
|
266
271
|
|
|
267
272
|
ansiX962: { base: [1, 2, 840, 10045], of: {
|
|
268
|
-
ecPublicKey: [2, 1], prime256v1: [3, 1, 7],
|
|
273
|
+
ecPublicKey: [2, 1], prime192v1: [3, 1, 1], prime256v1: [3, 1, 7],
|
|
269
274
|
ecdsaWithSHA256: [4, 3, 2], ecdsaWithSHA384: [4, 3, 3], ecdsaWithSHA512: [4, 3, 4] } },
|
|
270
275
|
|
|
271
|
-
secg: { base: [1, 3, 132, 0], of: {
|
|
276
|
+
secg: { base: [1, 3, 132, 0], of: {
|
|
277
|
+
sect163k1: 1, sect163r1: 2, secp256k1: 10, sect163r2: 15, sect283k1: 16, sect283r1: 17,
|
|
278
|
+
sect233k1: 26, sect233r1: 27, secp192k1: 31, secp224k1: 32, secp224r1: 33,
|
|
279
|
+
secp384r1: 34, secp521r1: 35, sect409k1: 36, sect409r1: 37, sect571k1: 38, sect571r1: 39 } },
|
|
272
280
|
|
|
273
|
-
brainpool: { base: [1, 3, 36, 3, 3, 2, 8, 1, 1], of: {
|
|
281
|
+
brainpool: { base: [1, 3, 36, 3, 3, 2, 8, 1, 1], of: {
|
|
282
|
+
brainpoolP160r1: 1, brainpoolP160t1: 2, brainpoolP192r1: 3, brainpoolP192t1: 4,
|
|
283
|
+
brainpoolP224r1: 5, brainpoolP224t1: 6, brainpoolP256r1: 7, brainpoolP256t1: 8,
|
|
284
|
+
brainpoolP320r1: 9, brainpoolP320t1: 10, brainpoolP384r1: 11, brainpoolP384t1: 12,
|
|
285
|
+
brainpoolP512r1: 13, brainpoolP512t1: 14 } },
|
|
274
286
|
|
|
275
287
|
edwards: { base: [1, 3, 101], of: { X25519: 110, X448: 111, Ed25519: 112, Ed448: 113 } },
|
|
276
288
|
|
|
@@ -332,7 +344,7 @@ var FAMILIES = Object.assign(Object.create(null), {
|
|
|
332
344
|
nistKem: { base: [2, 16, 840, 1, 101, 3, 4, 4], of: {
|
|
333
345
|
"id-ml-kem-512": 1, "id-ml-kem-768": 2, "id-ml-kem-1024": 3 } },
|
|
334
346
|
|
|
335
|
-
datatype: { base: [0, 9, 2342, 19200300, 100, 1], of: { domainComponent: 25 } },
|
|
347
|
+
datatype: { base: [0, 9, 2342, 19200300, 100, 1], of: { userId: 1, domainComponent: 25 } },
|
|
336
348
|
|
|
337
349
|
fidoGenCe: { base: [1, 3, 6, 1, 4, 1, 45724, 1, 1], of: { idFidoGenCeAaguid: 4 } },
|
|
338
350
|
|
package/lib/path-validate.js
CHANGED
|
@@ -220,7 +220,7 @@ function resolveRsaPss(paramsBytes) {
|
|
|
220
220
|
return { verify: { name: "RSA-PSS", saltLength: saltLength }, imp: { name: "RSA-PSS", hash: hash } };
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
function isDerNull(p) { return
|
|
223
|
+
function isDerNull(p) { return guard.der.isNull(p); }
|
|
224
224
|
|
|
225
225
|
function resolveDescriptor(sigAlg) {
|
|
226
226
|
if (sigAlg.oid === OID_RSA_PSS) return resolveRsaPss(sigAlg.parameters);
|
|
@@ -827,7 +827,7 @@ function addChild(state, parent, validPolicy, qualifiers, expectedPolicySet, che
|
|
|
827
827
|
}
|
|
828
828
|
|
|
829
829
|
function isNullOrAbsentParams(p) {
|
|
830
|
-
return
|
|
830
|
+
return guard.der.paramsAbsentOrNull(p);
|
|
831
831
|
}
|
|
832
832
|
|
|
833
833
|
function spliceSpkiParameters(spki, algOid, paramsBytes) {
|
|
@@ -1017,8 +1017,13 @@ function validateCriticalExtensionStructure(cert) {
|
|
|
1017
1017
|
* optional `requiredEku` (key purposes the target's extendedKeyUsage must
|
|
1018
1018
|
* assert, given as registered OID names or dotted OID strings; an absent
|
|
1019
1019
|
* extension is unrestricted, RFC 5280 4.2.1.12), and an optional
|
|
1020
|
-
* `revocationChecker`.
|
|
1021
|
-
*
|
|
1020
|
+
* `revocationChecker`. An optional `verifier` replaces the built-in signature
|
|
1021
|
+
* check with `verifier.verify({ cert, workingPublicKey, workingPublicKeyAlgorithm,
|
|
1022
|
+
* workingPublicKeyParameters })`, which is how a chain on a curve the engine does
|
|
1023
|
+
* not carry, such as a brainpool one, is validated. It replaces the signature step
|
|
1024
|
+
* only: the key-strength floor and every other RFC 5280 check still apply.
|
|
1025
|
+
* The value-carrying options (`time`, `maxPathCerts`,
|
|
1026
|
+
* `maxPolicyNodes`, `minRsaModulusBits`, the subtree seeds, `userInitialPolicySet`, `requiredEku`)
|
|
1022
1027
|
* are validated at the entry point. A mis-shaped value throws `path/bad-input`,
|
|
1023
1028
|
* so it cannot silently go unapplied. Returns `{ valid, revocationChecked, anchorConstraints,
|
|
1024
1029
|
* path, results, workingPublicKey, workingPublicKeyAlgorithm,
|
package/lib/pki-build.js
CHANGED
|
@@ -265,6 +265,27 @@ function _isDottedOid(s) {
|
|
|
265
265
|
|
|
266
266
|
var b = asn1.build;
|
|
267
267
|
|
|
268
|
+
/** @internal The distinguished-name attributes RFC 5280 App. A.1 types as something other than
|
|
269
|
+
* DirectoryString, with the SIZE constraint the module states. Every other attribute is a
|
|
270
|
+
* DirectoryString and takes UTF8String. A row here, not a branch, so a newly registered
|
|
271
|
+
* attribute whose module entry names a type is a data addition. */
|
|
272
|
+
var ATV_TYPE = intrinsic.assign(intrinsic.create(null), {
|
|
273
|
+
countryName: { encode: b.printable, tag: asn1.TAGS.PRINTABLE_STRING, size: [2, 2], what: "X520countryName, PrintableString SIZE(2)" },
|
|
274
|
+
jurisdictionCountryName: { encode: b.printable, tag: asn1.TAGS.PRINTABLE_STRING, size: [2, 2], what: "a jurisdiction country code, PrintableString SIZE(2)" },
|
|
275
|
+
distinguishedNameQualifier: { encode: b.printable, tag: asn1.TAGS.PRINTABLE_STRING, what: "X520dnQualifier, PrintableString" },
|
|
276
|
+
serialNumber: { encode: b.printable, tag: asn1.TAGS.PRINTABLE_STRING, size: [1, 64], what: "X520SerialNumber, PrintableString" },
|
|
277
|
+
emailAddress: { encode: b.ia5, tag: asn1.TAGS.IA5_STRING, size: [1, 255], what: "EmailAddress, IA5String" },
|
|
278
|
+
domainComponent: { encode: b.ia5, tag: asn1.TAGS.IA5_STRING, what: "DomainComponent, IA5String (RFC 4519 sec. 2.4)" },
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
/** @internal The DirectoryString CHOICE of RFC 5280 App. A.1. Every attribute the toolkit
|
|
282
|
+
* publishes a short name for and does not give its own row above is valued as one, so a
|
|
283
|
+
* pre-encoded value for one of them must be a member. An attribute with no published short
|
|
284
|
+
* name has a syntax this toolkit does not claim to know, and its value stays opaque. */
|
|
285
|
+
var DIRECTORY_STRING_TAGS = intrinsic.assign(intrinsic.create(null), {});
|
|
286
|
+
[asn1.TAGS.TELETEX_STRING, asn1.TAGS.PRINTABLE_STRING, asn1.TAGS.UNIVERSAL_STRING,
|
|
287
|
+
asn1.TAGS.UTF8_STRING, asn1.TAGS.BMP_STRING].forEach(function (t) { DIRECTORY_STRING_TAGS[t] = 1; });
|
|
288
|
+
|
|
268
289
|
var KU_BIT = intrinsic.assign(intrinsic.create(null), {
|
|
269
290
|
digitalSignature: 0, nonRepudiation: 1, contentCommitment: 1, keyEncipherment: 2,
|
|
270
291
|
dataEncipherment: 3, keyAgreement: 4, keyCertSign: 5, cRLSign: 6, encipherOnly: 7, decipherOnly: 8,
|
|
@@ -360,19 +381,55 @@ function makeBuilder(ctx) {
|
|
|
360
381
|
return (y >= 1950 && y <= 2049) ? b.utcTime(date) : b.generalizedTime(date);
|
|
361
382
|
}
|
|
362
383
|
|
|
363
|
-
function
|
|
364
|
-
if (
|
|
365
|
-
|
|
366
|
-
|
|
384
|
+
function assertAtvValue(attrName, value) {
|
|
385
|
+
if (value == null || value === "") throw E("bad-name", "the " + attrName + " attribute value must be a non-empty string");
|
|
386
|
+
var row = ATV_TYPE[attrName];
|
|
387
|
+
if (!row || !row.size) return;
|
|
388
|
+
var len = _String(value).length;
|
|
389
|
+
if (len < row.size[0] || len > row.size[1]) {
|
|
390
|
+
throw E("bad-name", attrName + " must be " + (row.size[0] === row.size[1]
|
|
391
|
+
? "exactly " + row.size[0] + " characters" : row.size[0] + " to " + row.size[1] + " characters") +
|
|
392
|
+
" (" + row.what + ", RFC 5280 App. A.1)");
|
|
367
393
|
}
|
|
368
|
-
|
|
369
|
-
|
|
394
|
+
}
|
|
395
|
+
function atvString(attrName, value) {
|
|
396
|
+
assertAtvValue(attrName, value);
|
|
397
|
+
var row = ATV_TYPE[attrName];
|
|
398
|
+
if (!row) return b.utf8(value);
|
|
399
|
+
return row.encode(value);
|
|
370
400
|
}
|
|
371
401
|
function encodeAtv(attrName, value) {
|
|
372
402
|
if (value == null || value === "") throw E("bad-name", "the " + attrName + " attribute value must be a non-empty string");
|
|
373
403
|
var typeOid = O(attrName);
|
|
374
404
|
if (typeOid == null) throw E("bad-name", "unknown distinguished-name attribute " + guard.text.showValue(attrName));
|
|
375
405
|
var valueTlv;
|
|
406
|
+
if (typeof value === "string" && _charCodeAt(value, 0) === 0x23) {
|
|
407
|
+
var raw = guard.encoding.hex(_sliceStr(value, 1), null, rawErr, ctx.prefix + "/bad-name", "a #hex attribute value");
|
|
408
|
+
guard.der.tlv(raw, rawErr, ctx.prefix + "/bad-name", "a #hex attribute value");
|
|
409
|
+
var rawNode = asn1.decode(raw);
|
|
410
|
+
var typeRow = ATV_TYPE[attrName];
|
|
411
|
+
var universalPrimitive = rawNode.tagClass === "universal" && !rawNode.constructed;
|
|
412
|
+
if (typeRow && (!universalPrimitive || rawNode.tagNumber !== typeRow.tag)) {
|
|
413
|
+
throw E("bad-name", "the " + attrName + " #hex value must be encoded as " + typeRow.what);
|
|
414
|
+
}
|
|
415
|
+
if (!typeRow && intrinsic.hasOwn(C.NAMES.DN_SHORT, attrName) &&
|
|
416
|
+
(!universalPrimitive || !DIRECTORY_STRING_TAGS[rawNode.tagNumber])) {
|
|
417
|
+
throw E("bad-name", "the " + attrName + " #hex value must be a DirectoryString: a TeletexString, PrintableString, UniversalString, UTF8String or BMPString (RFC 5280 App. A.1)");
|
|
418
|
+
}
|
|
419
|
+
var rawText;
|
|
420
|
+
try {
|
|
421
|
+
rawText = asn1.read.string(rawNode);
|
|
422
|
+
} catch (_e) {
|
|
423
|
+
rawText = null;
|
|
424
|
+
}
|
|
425
|
+
if (rawText !== null) {
|
|
426
|
+
try { atvString(attrName, rawText); }
|
|
427
|
+
catch (e) { if (e instanceof ErrorClass) throw e; throw E("bad-name", "the " + attrName + " #hex value carries characters invalid for that attribute's string type", e); }
|
|
428
|
+
}
|
|
429
|
+
return b.sequence([b.oid(typeOid), raw]);
|
|
430
|
+
}
|
|
431
|
+
if (typeof value === "string" && _charCodeAt(value, 0) === 0x5c) value = _sliceStr(value, 1);
|
|
432
|
+
if (value === "") throw E("bad-name", "the " + attrName + " attribute value must be a non-empty string");
|
|
376
433
|
try { valueTlv = atvString(attrName, value); }
|
|
377
434
|
catch (e) { if (e instanceof ErrorClass) throw e; throw E("bad-name", "the " + attrName + " value has characters invalid for its string type", e); }
|
|
378
435
|
return b.sequence([b.oid(typeOid), valueTlv]);
|
package/lib/schema-cms.js
CHANGED
|
@@ -100,8 +100,8 @@ var AEAD_ALGS = Object.create(null);
|
|
|
100
100
|
|
|
101
101
|
var DEFERRED = new Set([
|
|
102
102
|
oid.byName("data"), oid.byName("signedAndEnvelopedData"),
|
|
103
|
-
oid.byName("digestedData"),
|
|
104
103
|
]);
|
|
104
|
+
var OID_DIGESTED_DATA = oid.byName("digestedData");
|
|
105
105
|
var OID_CONTENT_TYPE = oid.byName("contentType");
|
|
106
106
|
var OID_MESSAGE_DIGEST = oid.byName("messageDigest");
|
|
107
107
|
var OID_SIGNING_TIME = oid.byName("signingTime");
|
|
@@ -878,6 +878,32 @@ var COMPRESSED_DATA = schema.seq([
|
|
|
878
878
|
},
|
|
879
879
|
});
|
|
880
880
|
|
|
881
|
+
var DIGESTED_DATA_VERSION = pkix.versionReader(NS, { "0": 0, "2": 2 });
|
|
882
|
+
|
|
883
|
+
var DIGESTED_DATA = schema.seq([
|
|
884
|
+
schema.field("version", DIGESTED_DATA_VERSION),
|
|
885
|
+
schema.field("digestAlgorithm", ALGORITHM_IDENTIFIER),
|
|
886
|
+
schema.field("encapContentInfo", ENCAP_CONTENT_INFO),
|
|
887
|
+
schema.field("digest", schema.octetString()),
|
|
888
|
+
], {
|
|
889
|
+
assert: "sequence", arity: { exact: 4 }, code: "cms/bad-digested-data", what: "DigestedData",
|
|
890
|
+
build: function (m, ctx) {
|
|
891
|
+
var version = m.fields.version.value;
|
|
892
|
+
var eci = m.fields.encapContentInfo.value.result;
|
|
893
|
+
var expected = eci.eContentType === OID_DATA ? 0 : 2;
|
|
894
|
+
if (version !== expected) {
|
|
895
|
+
throw ctx.E("cms/bad-version", "a DigestedData over " + (ctx.oid.name(eci.eContentType) || eci.eContentType) +
|
|
896
|
+
" is version " + expected + " (RFC 5652 sec. 7)");
|
|
897
|
+
}
|
|
898
|
+
return {
|
|
899
|
+
version: version,
|
|
900
|
+
digestAlgorithm: m.fields.digestAlgorithm.value.result,
|
|
901
|
+
encapContentInfo: eci,
|
|
902
|
+
digest: m.fields.digest.value,
|
|
903
|
+
};
|
|
904
|
+
},
|
|
905
|
+
});
|
|
906
|
+
|
|
881
907
|
var CONTENT_INFO = schema.seq([
|
|
882
908
|
schema.field("contentType", schema.oidLeaf()),
|
|
883
909
|
schema.field("content", schema.explicit(0, schema.any(), { code: "cms/not-a-content-info" })),
|
|
@@ -892,6 +918,7 @@ var CONTENT_INFO = schema.seq([
|
|
|
892
918
|
else if (ct === OID_AUTH_DATA) inner = AUTHENTICATED_DATA;
|
|
893
919
|
else if (ct === OID_AUTH_ENVELOPED_DATA) inner = AUTH_ENVELOPED_DATA;
|
|
894
920
|
else if (ct === OID_COMPRESSED_DATA) inner = COMPRESSED_DATA;
|
|
921
|
+
else if (ct === OID_DIGESTED_DATA) inner = DIGESTED_DATA;
|
|
895
922
|
if (inner === null) {
|
|
896
923
|
if (DEFERRED.has(ct)) {
|
|
897
924
|
throw NS.E("cms/unsupported-content-type", (ctx.oid.name(ct) || ct) + " is recognized but not parsed by this build");
|