@blamejs/pki 0.8.31 → 0.8.49
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 +401 -0
- package/README.md +66 -15
- package/bin/pki.js +716 -30
- package/examples/issue-a-certificate.js +90 -0
- package/examples/post-quantum.js +66 -0
- package/examples/read-a-certificate.js +56 -0
- package/examples/revoke-and-check.js +86 -0
- package/examples/sign-and-verify-cms.js +63 -0
- package/index.js +18 -5
- package/lib/acme.js +223 -74
- package/lib/alt-sig.js +441 -0
- package/lib/asn1-der.js +48 -25
- package/lib/attrcert-sign.js +27 -19
- package/lib/cbor-det.js +24 -14
- package/lib/cmc-build.js +9 -2
- package/lib/cmc-verify.js +14 -3
- package/lib/cmp-build.js +17 -5
- package/lib/cmp-session.js +24 -11
- package/lib/cmp-verify.js +28 -19
- package/lib/cms-compress.js +3 -1
- package/lib/cms-decrypt.js +102 -7
- package/lib/cms-encrypt.js +59 -7
- package/lib/cms-sign.js +31 -9
- package/lib/cms-verify.js +22 -18
- package/lib/composite-kem.js +30 -18
- package/lib/composite-sig.js +29 -14
- package/lib/constants.js +26 -0
- package/lib/crl-sign.js +111 -19
- package/lib/crmf-sign.js +167 -15
- package/lib/csr-sign.js +117 -6
- package/lib/ct.js +1008 -58
- package/lib/est.js +29 -16
- package/lib/framework-error.js +12 -0
- package/lib/guard-async.js +1 -1
- package/lib/guard-bytes.js +103 -20
- package/lib/guard-crypto.js +205 -3
- package/lib/guard-encoding.js +6 -1
- package/lib/guard-identifier.js +55 -8
- package/lib/guard-intrinsic.js +43 -0
- package/lib/guard-json.js +122 -9
- package/lib/guard-limits.js +12 -1
- package/lib/guard-list.js +63 -8
- package/lib/guard-name.js +167 -20
- package/lib/guard-range.js +43 -5
- package/lib/guard-text.js +41 -0
- package/lib/guard-verdict.js +39 -8
- package/lib/hpke.js +688 -42
- package/lib/http-transport.js +156 -4
- package/lib/identity-match.js +4 -4
- package/lib/inspect.js +33 -6
- package/lib/jose.js +93 -25
- package/lib/key.js +18 -13
- package/lib/lint.js +165 -1
- package/lib/merkle.js +233 -27
- package/lib/ocsp.js +27 -14
- package/lib/oid.js +14 -4
- package/lib/path-validate.js +143 -61
- package/lib/pkcs12-build.js +7 -2
- package/lib/pki-build.js +31 -7
- package/lib/possession.js +575 -0
- package/lib/related-cert.js +708 -0
- package/lib/revocation-fetch.js +6 -6
- package/lib/scep.js +15 -5
- package/lib/schema-c509.js +5 -2
- package/lib/schema-cmc.js +1 -1
- package/lib/schema-cmp.js +8 -8
- package/lib/schema-cms.js +2 -15
- package/lib/schema-csr.js +69 -0
- package/lib/schema-engine.js +10 -3
- package/lib/schema-pkix.js +228 -2
- package/lib/schema-tsp.js +7 -6
- package/lib/sign-scheme.js +22 -9
- package/lib/sigstore.js +684 -168
- package/lib/sleep.js +8 -1
- package/lib/smime.js +22 -1
- package/lib/tlog.js +1235 -0
- package/lib/tls-cert-compress.js +25 -8
- package/lib/tsp-sign.js +120 -35
- package/lib/tuf.js +1395 -0
- package/lib/validator-tpm.js +4 -1
- package/lib/verify-seam.js +7 -1
- package/lib/webauthn-mds.js +41 -4
- package/lib/webauthn.js +43 -25
- package/lib/webcrypto.js +48 -46
- package/lib/x509-sign.js +225 -19
- package/package.json +2 -1
- package/sbom.cdx.json +6 -6
package/lib/alt-sig.js
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
/**
|
|
5
|
+
* @module pki.altSig
|
|
6
|
+
* @nav Signing
|
|
7
|
+
* @title Alternative signatures
|
|
8
|
+
* @order 237
|
|
9
|
+
* @slug alt-sig
|
|
10
|
+
* @fullname Alternative signatures: one certificate carrying two, for an algorithm migration
|
|
11
|
+
*
|
|
12
|
+
* @intro
|
|
13
|
+
* One certificate, two signatures. A PKI moving from one set of algorithms to
|
|
14
|
+
* another cannot reissue to every relying party at once, so ITU-T X.509 (2019)
|
|
15
|
+
* clause 9.8 lets a certificate carry a second public key, a second signature
|
|
16
|
+
* algorithm and a second signature in extensions. A relying party that has not
|
|
17
|
+
* migrated reads the native signature and ignores the rest; one that has reads
|
|
18
|
+
* the alternative signature instead. The shape is widely called Catalyst.
|
|
19
|
+
*
|
|
20
|
+
* `signedData` builds the bytes that alternative signature covers. They are the
|
|
21
|
+
* `PreTBSCertificate`: the `tbsCertificate` without its `signature`
|
|
22
|
+
* AlgorithmIdentifier and without the `altSignatureValue` extension. A CRL has
|
|
23
|
+
* the same shape in `PreTBSCertList`, where `signature` is the second element
|
|
24
|
+
* rather than the third. Those bytes never appear on the wire, so a verifier
|
|
25
|
+
* rebuilds them. This is the one place the toolkit re-encodes what it parsed, so
|
|
26
|
+
* it does it by keeping the original bytes of every component it retains and
|
|
27
|
+
* recomputing only the two SEQUENCE headers whose lengths change. Nothing is
|
|
28
|
+
* re-serialized from a decoded model, because a model that normalized any byte
|
|
29
|
+
* would either fail every verification or accept an encoding the issuer never
|
|
30
|
+
* signed.
|
|
31
|
+
*
|
|
32
|
+
* `verify` checks that signature under the alternative public key of the ISSUER,
|
|
33
|
+
* which clause 9.8.4 requires, through the same engine certification-path
|
|
34
|
+
* validation uses. The algorithm is not derived here: the structure states it in
|
|
35
|
+
* `altSignatureAlgorithm`, and the engine binds that stated algorithm to the key
|
|
36
|
+
* it was handed, so an algorithm that does not match the key is refused rather
|
|
37
|
+
* than reported as a bad signature.
|
|
38
|
+
*
|
|
39
|
+
* `subjectAltPublicKey` reads a certificate's own alternative public key, which is
|
|
40
|
+
* what a verifier needs to check the certificate BELOW it. `SubjectAltPublicKeyInfo`
|
|
41
|
+
* carries the same two components in the same order as `SubjectPublicKeyInfo`, and a
|
|
42
|
+
* field name is not encoded, so the extension value is an SPKI on the wire and an
|
|
43
|
+
* importer takes it unchanged.
|
|
44
|
+
*
|
|
45
|
+
* Clause 7.10.3 gives a CRL the same procedure with the same two extensions, and
|
|
46
|
+
* both verbs read a CRL as readily as a certificate. `subjectAltPublicKeyInfo` is
|
|
47
|
+
* certificate-only, which clause 7.2.2 states in a NOTE.
|
|
48
|
+
*
|
|
49
|
+
* `pki.x509.sign` and `pki.crl.sign` produce all of this: an `altKey` beside the
|
|
50
|
+
* issuing key makes the two-pass signature the clause describes, so the ordering
|
|
51
|
+
* it requires is not the caller's to get right.
|
|
52
|
+
*
|
|
53
|
+
* @card Build and verify the second signature an X.509 (2019) clause 9.8 certificate
|
|
54
|
+
* or CRL carries, for a PKI migrating between algorithm sets.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
var asn1 = require("./asn1-der");
|
|
58
|
+
var oid = require("./oid");
|
|
59
|
+
var pkix = require("./schema-pkix");
|
|
60
|
+
var schema = require("./schema-engine");
|
|
61
|
+
var schemaX509 = require("./schema-x509");
|
|
62
|
+
var schemaCrl = require("./schema-crl");
|
|
63
|
+
var frameworkError = require("./framework-error");
|
|
64
|
+
var guard = require("./guard-all");
|
|
65
|
+
var intrinsic = require("./guard-intrinsic");
|
|
66
|
+
var seam = require("./verify-seam");
|
|
67
|
+
|
|
68
|
+
var AltSigError = frameworkError.AltSigError;
|
|
69
|
+
var b = asn1.build;
|
|
70
|
+
var _create = intrinsic.create;
|
|
71
|
+
var _assign = intrinsic.assign;
|
|
72
|
+
var _forEach = intrinsic.forEach;
|
|
73
|
+
var _map = intrinsic.map;
|
|
74
|
+
var _bufferFrom = intrinsic.bufferFrom;
|
|
75
|
+
var _bufferConcat = intrinsic.bufferConcat;
|
|
76
|
+
var _push = intrinsic.push;
|
|
77
|
+
|
|
78
|
+
function _err(code, message, cause) { return new AltSigError(code, message, cause); }
|
|
79
|
+
|
|
80
|
+
var _seam = seam.makeSeam("alt-sig", AltSigError, "altsig/bad-input");
|
|
81
|
+
|
|
82
|
+
var OID_SAPKI = oid.byName("subjectAltPublicKeyInfo");
|
|
83
|
+
var OID_ALT_ALG = oid.byName("altSignatureAlgorithm");
|
|
84
|
+
var OID_ALT_VAL = oid.byName("altSignatureValue");
|
|
85
|
+
_forEach([OID_SAPKI, OID_ALT_ALG, OID_ALT_VAL], function (o) {
|
|
86
|
+
if (!o) throw new Error("alt-sig: the clause 9.8 extension OIDs are not in the registry");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
var KNOWN_VERIFY_OPTS = _create(null);
|
|
90
|
+
|
|
91
|
+
/** @internal Which structure this is, and where its extensions sit. A certificate carries them in
|
|
92
|
+
* `[3] EXPLICIT` at the end of the tbsCertificate; a CRL in `[0] EXPLICIT` at the end of the
|
|
93
|
+
* tbsCertList. The two shapes are told apart by the format detectors the parsers already publish,
|
|
94
|
+
* which are proven mutually exclusive, rather than by a tag probe written here. */
|
|
95
|
+
/** @internal `preName` is the type the preimage is an instance of, and `signatureIndex` finds the
|
|
96
|
+
* `signature` AlgorithmIdentifier the preimage drops. The field sits at a different element in the two
|
|
97
|
+
* structures because the optional `version` before it is encoded differently: a certificate's is
|
|
98
|
+
* `[0] EXPLICIT`, so `signature` is the third element, while a CRL's is a bare INTEGER, so it is the
|
|
99
|
+
* second. Each is located by the tag of the element before it rather than by a fixed offset. */
|
|
100
|
+
/** @internal `carriesSubjectAltKey` is which kinds may hold a `subjectAltPublicKeyInfo` extension:
|
|
101
|
+
* certificates only (ITU-T X.509 (2019) clause 7.2.2, NOTE). It is a row on the kind rather than a
|
|
102
|
+
* position in this array, so adding a third kind states its own answer and reordering the array cannot
|
|
103
|
+
* silently invert the rule. */
|
|
104
|
+
var KINDS = [
|
|
105
|
+
{ name: "certificate", carriesSubjectAltKey: true, preName: "PreTBSCertificate", matches: schemaX509.matches, parse: schemaX509.parse, extTag: 3,
|
|
106
|
+
signatureIndex: function (tbs) {
|
|
107
|
+
var first = tbs.children[0];
|
|
108
|
+
return (first.tagClass === "context" && first.tagNumber === 0) ? 2 : 1;
|
|
109
|
+
} },
|
|
110
|
+
{ name: "CRL", carriesSubjectAltKey: false, preName: "PreTBSCertList", matches: schemaCrl.matches, parse: schemaCrl.parse, extTag: 0,
|
|
111
|
+
signatureIndex: function (tbs) {
|
|
112
|
+
var first = tbs.children[0];
|
|
113
|
+
return (first.tagClass === "universal" && first.tagNumber === asn1.TAGS.INTEGER) ? 1 : 0;
|
|
114
|
+
} },
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
function _classify(root) {
|
|
118
|
+
for (var i = 0; i < KINDS.length; i++) {
|
|
119
|
+
if (KINDS[i].matches(root) === true) return KINDS[i];
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** @internal The decoded root of a signed structure this module reads, with the kind it is. */
|
|
125
|
+
function _open(input, what) {
|
|
126
|
+
var der = guard.bytes.snapshot(input, _err, "altsig/bad-input", what);
|
|
127
|
+
var root;
|
|
128
|
+
try { root = asn1.decode(der); }
|
|
129
|
+
catch (e) { throw _err("altsig/bad-input", what + " must be DER", e); }
|
|
130
|
+
var kind = _classify(root);
|
|
131
|
+
if (kind === null) {
|
|
132
|
+
throw _err("altsig/bad-input", what + " must be an X.509 certificate or a CRL");
|
|
133
|
+
}
|
|
134
|
+
if (root.children.length < 3) {
|
|
135
|
+
throw _err("altsig/bad-input", "a signed " + kind.name + " carries a toBeSigned, an algorithm and a signature");
|
|
136
|
+
}
|
|
137
|
+
/** @internal The structure is PARSED, not merely detected. `matches` answers which format the bytes are,
|
|
138
|
+
* which is not the same as their being a well-formed one of it: a certificate carrying the
|
|
139
|
+
* altSignatureValue extension TWICE passed the detector, `_findExt` took the first signature and
|
|
140
|
+
* `_preimage` removed both, so an ambiguous certificate the shared parser refuses with
|
|
141
|
+
* `x509/duplicate-extension` received a successful verdict here. Running the parser is what holds every
|
|
142
|
+
* structural rule this module then relies on, rather than the handful its own walk happens to check. */
|
|
143
|
+
try { kind.parse(der); }
|
|
144
|
+
catch (e) {
|
|
145
|
+
throw _err("altsig/bad-input", what + " is not a well-formed " + kind.name +
|
|
146
|
+
", so the extensions this verb reads from it cannot be trusted to be the ones it carries", e);
|
|
147
|
+
}
|
|
148
|
+
return { der: der, root: root, kind: kind };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** @internal The extensions SEQUENCE node of a certificate or CRL, or null when the field is absent.
|
|
152
|
+
* The tagged field is the last child of the toBeSigned in both structures, and it is located by its
|
|
153
|
+
* tag rather than by position, since the optional fields before it may or may not be present. */
|
|
154
|
+
function _extensionsNode(tbs, extTag) {
|
|
155
|
+
var kids = tbs.children || [];
|
|
156
|
+
for (var i = kids.length - 1; i >= 0; i--) {
|
|
157
|
+
var k = kids[i];
|
|
158
|
+
if (k.tagClass === "context" && k.tagNumber === extTag && k.children && k.children.length === 1 &&
|
|
159
|
+
k.children[0].tagClass === "universal" && k.children[0].tagNumber === asn1.TAGS.SEQUENCE) {
|
|
160
|
+
return { index: i, wrapper: k, list: k.children[0] };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function _extOid(node) {
|
|
167
|
+
if (!node.children || node.children.length < 2) return null;
|
|
168
|
+
try { return asn1.read.oid(node.children[0]); }
|
|
169
|
+
catch (_e) { return null; }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** @internal Every extension of the structure, as `{ oid, node }`, in the order encoded. */
|
|
173
|
+
function _extensionList(opened) {
|
|
174
|
+
var found = _extensionsNode(opened.root.children[0], opened.kind.extTag);
|
|
175
|
+
if (found === null) return { block: null, items: [] };
|
|
176
|
+
return {
|
|
177
|
+
block: found,
|
|
178
|
+
items: _map(found.list.children || [], function (n) { return { oid: _extOid(n), node: n }; }),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function _findExt(items, wanted) {
|
|
183
|
+
for (var i = 0; i < items.length; i++) { if (items[i].oid === wanted) return items[i]; }
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @primitive pki.altSig.signedData
|
|
189
|
+
* @signature pki.altSig.signedData(structure) -> Buffer
|
|
190
|
+
* @since 0.8.43
|
|
191
|
+
* @status stable
|
|
192
|
+
* @spec ITU-T X.509 (2019) clause 7.2.2 / 7.10.3 / 9.8
|
|
193
|
+
* @defends alternative-signature-scope-confusion (CWE-347)
|
|
194
|
+
* @related pki.altSig.verify, pki.x509.sign, pki.crl.sign
|
|
195
|
+
*
|
|
196
|
+
* The bytes an alternative signature covers, for a certificate or a CRL. ITU-T X.509 (2019) clause
|
|
197
|
+
* 7.2.2 says to "exclude the signature component and the altSignatureValue extension from the public-key
|
|
198
|
+
* certificate, and generate the digital signature over the remaining DER encoded public-key
|
|
199
|
+
* certificate", and clause 7.10.3 says the same of a CRL. Two members are named `signature`, so
|
|
200
|
+
* `draft-truskovsky-lamps-pq-hybrid-x509` gives the excluded one a type of its own: the
|
|
201
|
+
* `PreTBSCertificate` of section 4 is the `tbsCertificate` without "the signature field (the third
|
|
202
|
+
* element in the TBSCertificate sequence)", and the `PreTBSCertList` of section 5 is the `tbsCertList`
|
|
203
|
+
* without its second element. The `altSignatureValue` extension is removed from the extension list of
|
|
204
|
+
* each, and the relative order of the extensions that remain is unchanged. What comes back is that
|
|
205
|
+
* structure: the toBeSigned alone, carrying one member fewer.
|
|
206
|
+
*
|
|
207
|
+
* These are the bytes Bouncy Castle emits and checks, so an alternative signature this toolkit makes
|
|
208
|
+
* verifies there, and one made there verifies here.
|
|
209
|
+
*
|
|
210
|
+
* A verifier has to rebuild the encoding, since it never appears on the wire. Every component retained
|
|
211
|
+
* keeps the bytes it had, and only the SEQUENCE headers whose lengths change are recomputed; nothing is
|
|
212
|
+
* re-serialized from a decoded model.
|
|
213
|
+
*
|
|
214
|
+
* A structure carrying no `altSignatureAlgorithm` extension throws `altsig/absent`: there is no stated
|
|
215
|
+
* algorithm, so there is no alternative signature to be checked and no question these bytes answer.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* var native = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
|
|
219
|
+
* var alt = await pki.key.generate({ name: "ML-DSA-65" });
|
|
220
|
+
* var nativeSpki = await pki.key.export(native.publicKey, { format: "der" });
|
|
221
|
+
* var altSpki = await pki.key.export(alt.publicKey, { format: "der" });
|
|
222
|
+
* var certDer = await pki.x509.sign({
|
|
223
|
+
* subject: "CN=Catalyst", subjectPublicKey: nativeSpki, serialNumber: 1n,
|
|
224
|
+
* notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z"),
|
|
225
|
+
* extensions: { subjectAltPublicKeyInfo: altSpki },
|
|
226
|
+
* }, { key: native.privateKey, altKey: alt.privateKey, altPublicKey: altSpki });
|
|
227
|
+
* pki.altSig.signedData(certDer).length > 0; // -> true
|
|
228
|
+
*/
|
|
229
|
+
function signedData(structure) {
|
|
230
|
+
var opened = _open(structure, "the signed structure");
|
|
231
|
+
return _preimage(opened);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function _preimage(opened) {
|
|
235
|
+
var exts = _extensionList(opened);
|
|
236
|
+
if (_findExt(exts.items, OID_ALT_ALG) === null) {
|
|
237
|
+
throw _err("altsig/absent",
|
|
238
|
+
"the " + opened.kind.name + " carries no altSignatureAlgorithm extension, so it states no alternative signature (ITU-T X.509 (2019) clause 9.8.3)");
|
|
239
|
+
}
|
|
240
|
+
var tbs = opened.root.children[0];
|
|
241
|
+
var sigIndex = opened.kind.signatureIndex(tbs);
|
|
242
|
+
var sigField = tbs.children[sigIndex];
|
|
243
|
+
/** @internal The element dropped is held to being an AlgorithmIdentifier before it is dropped, so a
|
|
244
|
+
* structure whose fields do not sit where the type places them is refused rather than silently
|
|
245
|
+
* signed over with a different element missing. The parser `_open` ran already holds the layout;
|
|
246
|
+
* this keeps a future structure whose layout differs from failing quietly. */
|
|
247
|
+
if (!sigField || sigField.tagClass !== "universal" || sigField.tagNumber !== asn1.TAGS.SEQUENCE) {
|
|
248
|
+
throw _err("altsig/bad-input",
|
|
249
|
+
"the " + opened.kind.name + " does not carry its signature AlgorithmIdentifier where the type places it, so the " +
|
|
250
|
+
opened.kind.preName + " cannot be built from it");
|
|
251
|
+
}
|
|
252
|
+
var kept = [];
|
|
253
|
+
_forEach(tbs.children || [], function (k, i) {
|
|
254
|
+
if (i === sigIndex) return;
|
|
255
|
+
if (exts.block !== null && i === exts.block.index) return;
|
|
256
|
+
_push(kept, k.bytes);
|
|
257
|
+
});
|
|
258
|
+
if (exts.block !== null) {
|
|
259
|
+
var keptExts = [];
|
|
260
|
+
_forEach(exts.items, function (it) { if (it.oid !== OID_ALT_VAL) _push(keptExts, it.node.bytes); });
|
|
261
|
+
if (keptExts.length === 0) {
|
|
262
|
+
throw _err("altsig/bad-input",
|
|
263
|
+
"removing the altSignatureValue extension would leave an empty Extensions SEQUENCE, which the type does not allow");
|
|
264
|
+
}
|
|
265
|
+
_push(kept, b.explicit(opened.kind.extTag, b.sequence([b.raw(_bufferConcat(keptExts))])));
|
|
266
|
+
}
|
|
267
|
+
/** @internal Two headers are recomputed and no leaf is: the Extensions SEQUENCE and its tagged
|
|
268
|
+
* wrapper above, and the toBeSigned SEQUENCE here.
|
|
269
|
+
* WHAT IS EXCLUDED. ITU-T X.509 (2019) clause 7.2.2 says to "exclude the signature
|
|
270
|
+
* component and the altSignatureValue extension from the public-key certificate, and generate the
|
|
271
|
+
* digital signature over the remaining DER encoded public-key certificate". Two structures have a member
|
|
272
|
+
* named `signature`: the outer SEQUENCE, through `COMPONENTS OF SIGNATURE`, whose `signature` is the BIT
|
|
273
|
+
* STRING and whose `algorithmIdentifier` is a separately named component; and `TBSCertificate`, whose
|
|
274
|
+
* `signature` is an AlgorithmIdentifier. The clause names neither, and both can be built.
|
|
275
|
+
* draft-truskovsky-lamps-pq-hybrid-x509 settles it by defining the preimage as a type of its own.
|
|
276
|
+
* Section 4's `PreTBSCertificate` is the TBSCertificate without "the signature field (the third
|
|
277
|
+
* element in the TBSCertificate sequence)", and section 5's `PreTBSCertList` is the TBSCertList
|
|
278
|
+
* without its second element; the extension list in each drops altSignatureValue. The draft gives the
|
|
279
|
+
* reason the toBeSigned's own field is the one excluded: the signature AlgorithmIdentifier "might not
|
|
280
|
+
* be known at the time that the alternative signature is calculated", and being "associated with the
|
|
281
|
+
* final signatureValue field" it "does not need to be protected by the alternative signature". Both
|
|
282
|
+
* creation procedures bear that out, adding the field in a step after the alternative signature is
|
|
283
|
+
* computed. These are the bytes Bouncy Castle emits and checks, so an alternative signature this
|
|
284
|
+
* toolkit makes is one another implementation verifies, and the reverse. */
|
|
285
|
+
return b.sequence([b.raw(_bufferConcat(kept))]);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** @internal One decoded extension value of this structure, through the shared certificate-extension
|
|
289
|
+
* decoders so the value is held to its type before anything acts on it. */
|
|
290
|
+
var NS = pkix.makeNS("altsig", AltSigError, oid);
|
|
291
|
+
var EXT_DECODERS = pkix.certExtensionDecoders(NS).byOid;
|
|
292
|
+
function _decodeExt(item, label) {
|
|
293
|
+
var dec = EXT_DECODERS[item.oid];
|
|
294
|
+
var value;
|
|
295
|
+
try { value = asn1.read.octetString(item.node.children[item.node.children.length - 1]); }
|
|
296
|
+
catch (e) { throw _err("altsig/bad-input", "the " + label + " extension value must be an OCTET STRING", e); }
|
|
297
|
+
try { return dec(value); }
|
|
298
|
+
catch (e) {
|
|
299
|
+
if (e instanceof AltSigError) throw e;
|
|
300
|
+
throw _err("altsig/bad-input", "the " + label + " extension value is malformed", e);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* @primitive pki.altSig.subjectAltPublicKey
|
|
306
|
+
* @signature pki.altSig.subjectAltPublicKey(certificate) -> Buffer
|
|
307
|
+
* @since 0.8.43
|
|
308
|
+
* @status stable
|
|
309
|
+
* @spec ITU-T X.509 (2019) clause 9.8.2
|
|
310
|
+
* @defends alternative-key-substitution (CWE-347)
|
|
311
|
+
* @related pki.altSig.verify, pki.x509.sign
|
|
312
|
+
*
|
|
313
|
+
* A certificate's own alternative public key, as the SPKI DER an importer takes. This is the key that
|
|
314
|
+
* verifies the alternative signature on a certificate this one ISSUED, which is how the alternative
|
|
315
|
+
* chain is followed: clause 9.8.4 requires the alternative signature to be "verified using the
|
|
316
|
+
* alternative public key of the issuer".
|
|
317
|
+
*
|
|
318
|
+
* `SubjectAltPublicKeyInfo` carries the same two components in the same order as
|
|
319
|
+
* `SubjectPublicKeyInfo`, and a field name is not encoded, so the extension value is already an SPKI
|
|
320
|
+
* and is returned unchanged rather than rebuilt from its parts.
|
|
321
|
+
*
|
|
322
|
+
* A certificate with no such extension throws `altsig/absent`, as does a CRL, the extension being
|
|
323
|
+
* certificate-only (clause 7.2.2, NOTE).
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* var native = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
|
|
327
|
+
* var alt = await pki.key.generate({ name: "ML-DSA-65" });
|
|
328
|
+
* var nativeSpki = await pki.key.export(native.publicKey, { format: "der" });
|
|
329
|
+
* var altSpki = await pki.key.export(alt.publicKey, { format: "der" });
|
|
330
|
+
* var caDer = await pki.x509.sign({
|
|
331
|
+
* subject: "CN=Catalyst CA", subjectPublicKey: nativeSpki, serialNumber: 1n,
|
|
332
|
+
* notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z"),
|
|
333
|
+
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign"], subjectAltPublicKeyInfo: altSpki },
|
|
334
|
+
* }, { key: native.privateKey, altKey: alt.privateKey, altPublicKey: altSpki });
|
|
335
|
+
* // The same bytes the CA certified, ready to verify a certificate this CA issued.
|
|
336
|
+
* pki.altSig.subjectAltPublicKey(caDer).length === altSpki.length; // -> true
|
|
337
|
+
*/
|
|
338
|
+
function subjectAltPublicKey(certificate) {
|
|
339
|
+
var opened = _open(certificate, "the certificate");
|
|
340
|
+
/** @internal The KIND decides before any extension is read. `subjectAltPublicKeyInfo` is
|
|
341
|
+
* certificate-only (clause 7.2.2, NOTE) and this verb documents that a CRL is refused, but the refusal
|
|
342
|
+
* came from the extension being absent rather than from the structure being the wrong one: a CRL
|
|
343
|
+
* carrying the extension, which the pre-encoded extensions array admits under `profile: "none"`, had it
|
|
344
|
+
* decoded with the certificate decoder and its key handed back. A key is an identity, so where it was
|
|
345
|
+
* read from is the question. */
|
|
346
|
+
if (opened.kind.carriesSubjectAltKey !== true) {
|
|
347
|
+
throw _err("altsig/absent",
|
|
348
|
+
"a " + opened.kind.name + " carries no subjectAltPublicKeyInfo: the extension is certificate-only " +
|
|
349
|
+
"(ITU-T X.509 (2019) clause 7.2.2, NOTE)");
|
|
350
|
+
}
|
|
351
|
+
var exts = _extensionList(opened);
|
|
352
|
+
var item = _findExt(exts.items, OID_SAPKI);
|
|
353
|
+
if (item === null) {
|
|
354
|
+
throw _err("altsig/absent",
|
|
355
|
+
"the " + opened.kind.name + " carries no subjectAltPublicKeyInfo extension (ITU-T X.509 (2019) clause 9.8.2)");
|
|
356
|
+
}
|
|
357
|
+
return _decodeExt(item, "subjectAltPublicKeyInfo").bytes;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* @primitive pki.altSig.verify
|
|
362
|
+
* @signature pki.altSig.verify(structure, issuerAltPublicKey, opts?) -> Promise<boolean>
|
|
363
|
+
* @since 0.8.43
|
|
364
|
+
* @status stable
|
|
365
|
+
* @spec ITU-T X.509 (2019) clause 7.2.2 / 7.10.3 / 9.8.4
|
|
366
|
+
* @defends alternative-signature-forgery (CWE-347)
|
|
367
|
+
* @related pki.altSig.signedData, pki.altSig.subjectAltPublicKey, pki.path.validate
|
|
368
|
+
*
|
|
369
|
+
* Verify the alternative signature on a certificate or a CRL. `issuerAltPublicKey` is the issuer's
|
|
370
|
+
* alternative public key as SPKI DER, which clause 9.8.4 requires: "it shall be verified using the
|
|
371
|
+
* alternative public key of the issuer". `pki.altSig.subjectAltPublicKey` reads that key out of the
|
|
372
|
+
* issuer's own certificate. Resolves `true` when the signature over the bytes
|
|
373
|
+
* `pki.altSig.signedData` describes verifies, and `false` when a signature was checked and did not.
|
|
374
|
+
*
|
|
375
|
+
* The algorithm is not derived. The structure states it in its `altSignatureAlgorithm` extension, and
|
|
376
|
+
* that stated algorithm is bound to the key through the same engine certification-path validation
|
|
377
|
+
* uses, so a key that does not match the stated algorithm resolves `false` rather than being read under
|
|
378
|
+
* some other algorithm. Clause 9.8.3's NOTE 1 is why the algorithm is a separate extension: it sits
|
|
379
|
+
* inside the bytes the alternative signature covers, so it cannot be changed without breaking it.
|
|
380
|
+
*
|
|
381
|
+
* A structure that states no alternative signature throws, since there is nothing to check and `false`
|
|
382
|
+
* would read as a signature that failed.
|
|
383
|
+
*
|
|
384
|
+
* This verb answers one question: whether the issuer's alternative key signed this structure. It does
|
|
385
|
+
* not validate a path, check revocation, or read the native signature. `pki.path.validate` answers
|
|
386
|
+
* those, over the native signature.
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* var native = await pki.key.generate({ name: "ECDSA", namedCurve: "P-256" });
|
|
390
|
+
* var alt = await pki.key.generate({ name: "ML-DSA-65" });
|
|
391
|
+
* var nativeSpki = await pki.key.export(native.publicKey, { format: "der" });
|
|
392
|
+
* var altSpki = await pki.key.export(alt.publicKey, { format: "der" });
|
|
393
|
+
* var caDer = await pki.x509.sign({
|
|
394
|
+
* subject: "CN=Catalyst CA", subjectPublicKey: nativeSpki, serialNumber: 1n,
|
|
395
|
+
* notBefore: new Date("2027-01-01T00:00:00Z"), notAfter: new Date("2028-01-01T00:00:00Z"),
|
|
396
|
+
* extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign"], subjectAltPublicKeyInfo: altSpki },
|
|
397
|
+
* }, { key: native.privateKey, altKey: alt.privateKey, altPublicKey: altSpki });
|
|
398
|
+
* // The CA's own alternative key verifies the alternative signature it made.
|
|
399
|
+
* await pki.altSig.verify(caDer, pki.altSig.subjectAltPublicKey(caDer)); // -> true
|
|
400
|
+
*/
|
|
401
|
+
function verify(structure, issuerAltPublicKey, opts) {
|
|
402
|
+
return guard.async.deferred(function () { return _verify(structure, issuerAltPublicKey, opts); });
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function _verify(structure, issuerAltPublicKey, opts) {
|
|
406
|
+
guard.identifier.assertKnownKeys(opts, KNOWN_VERIFY_OPTS, _err, "altsig/bad-input",
|
|
407
|
+
"pki.altSig.verify has an unknown option: ");
|
|
408
|
+
var opened = _open(structure, "the signed structure");
|
|
409
|
+
var exts = _extensionList(opened);
|
|
410
|
+
var algItem = _findExt(exts.items, OID_ALT_ALG);
|
|
411
|
+
var valItem = _findExt(exts.items, OID_ALT_VAL);
|
|
412
|
+
if (algItem === null || valItem === null) {
|
|
413
|
+
throw _err("altsig/absent",
|
|
414
|
+
"the " + opened.kind.name + " carries no alternative signature: clause 9.8.3 requires the altSignatureAlgorithm and altSignatureValue extensions together");
|
|
415
|
+
}
|
|
416
|
+
var sigAlg = _decodeExt(algItem, "altSignatureAlgorithm");
|
|
417
|
+
var sigValue = _decodeExt(valItem, "altSignatureValue");
|
|
418
|
+
var spki = guard.bytes.snapshot(issuerAltPublicKey, _err, "altsig/bad-input", "the issuer's alternative public key");
|
|
419
|
+
_assertSpki(spki);
|
|
420
|
+
return _seam.verify(sigAlg, sigValue, spki, _preimage(opened));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/** @internal The alternative key is handed to an importer, so it is held to being a
|
|
424
|
+
* SubjectPublicKeyInfo first: bytes that are not one would reach the engine as a key it reports no
|
|
425
|
+
* verdict for, and a caller cannot tell that from a signature that failed. */
|
|
426
|
+
var SPKI_SCHEMA = pkix.spki(NS);
|
|
427
|
+
function _assertSpki(der) {
|
|
428
|
+
try { schema.walk(SPKI_SCHEMA, asn1.decode(der), NS); }
|
|
429
|
+
catch (e) {
|
|
430
|
+
throw _err("altsig/bad-input", "the issuer's alternative public key must be a SubjectPublicKeyInfo in DER", e);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
module.exports = {
|
|
435
|
+
signedData: signedData,
|
|
436
|
+
verify: verify,
|
|
437
|
+
subjectAltPublicKey: subjectAltPublicKey,
|
|
438
|
+
/** @internal The extension OIDs and the preimage, for the builders that emit them. */
|
|
439
|
+
OID_SAPKI: OID_SAPKI, OID_ALT_ALG: OID_ALT_ALG, OID_ALT_VAL: OID_ALT_VAL,
|
|
440
|
+
setEngine: _seam.setEngine,
|
|
441
|
+
};
|
package/lib/asn1-der.js
CHANGED
|
@@ -41,6 +41,7 @@ var frameworkError = require("./framework-error");
|
|
|
41
41
|
var guard = require("./guard-all");
|
|
42
42
|
var intrinsic = require("./guard-intrinsic");
|
|
43
43
|
var _isArray = intrinsic.isArray;
|
|
44
|
+
var _unshift = intrinsic.unshift;
|
|
44
45
|
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
45
46
|
/** @internal A Date's fields are read through the prototype's own getters, captured at load, so
|
|
46
47
|
* a getter the instance carries or one replaced later cannot change what a time encodes to or
|
|
@@ -207,6 +208,19 @@ function _decodeTLV(buf, start, limit, depth, maxDepth, ber, strDepth, ctr) {
|
|
|
207
208
|
}
|
|
208
209
|
}
|
|
209
210
|
|
|
211
|
+
/** @internal UNIVERSAL tag 0 is reserved. X.690 sec. 8.1.5 gives the `00 00` octet pair one meaning,
|
|
212
|
+
* the terminator of an indefinite-length encoding, and nothing this decoder accepts uses indefinite
|
|
213
|
+
* length: the strict door refuses it outright, and the BER door consumes the pair as a terminator
|
|
214
|
+
* rather than as an element. So a universal tag-0 element is never part of a well-formed input on
|
|
215
|
+
* either door, and it was being handed back as an ordinary zero-length element. The rule is refused
|
|
216
|
+
* HERE, at the TLV, because `guard.der.element` already carried it and a reader that did not route
|
|
217
|
+
* through that guard inherited nothing. The class matters: a context-specific [0] is how most of
|
|
218
|
+
* X.509 and CMS write an optional field, so this is universal-class only. */
|
|
219
|
+
if (tagClassBits === CLASS_UNIVERSAL && tagNumber === 0) {
|
|
220
|
+
throw new Asn1Error("asn1/reserved-tag",
|
|
221
|
+
"universal tag 0 is the reserved end-of-contents encoding (X.690 sec. 8.1.5) and is not an element");
|
|
222
|
+
}
|
|
223
|
+
|
|
210
224
|
if (tagClassBits === CLASS_UNIVERSAL && CONSTRUCTED_ONLY_UNIVERSAL_TAGS[tagNumber] && !constructed) {
|
|
211
225
|
throw new Asn1Error("asn1/bad-tlv", "a universal constructed-only type (SEQUENCE/SET/EXTERNAL/EMBEDDED PDV/CHARACTER STRING) must be constructed");
|
|
212
226
|
}
|
|
@@ -472,10 +486,18 @@ function readOid(node) {
|
|
|
472
486
|
}
|
|
473
487
|
|
|
474
488
|
function decodeOidContent(buf) {
|
|
475
|
-
|
|
489
|
+
/** @internal This verb is exported, so `buf` is a caller's object. `guard.bytes.view` refuses anything
|
|
490
|
+
* whose indexed elements are not its bytes, and `guard.bytes.lengthOf` reads the count through the
|
|
491
|
+
* captured TypedArray prototype getter, so the count and the bytes come from the same view. A Buffer
|
|
492
|
+
* accepts an own `length` property that differs from its real size, and read from `.length` a buffer
|
|
493
|
+
* whose bytes encode 1.2.840.3 and whose `length` says 3 decoded as 1.2.840. An OID selects a signature
|
|
494
|
+
* algorithm and an extension decoder, so the bytes have to decide. */
|
|
495
|
+
buf = guard.bytes.view(buf, OidError, "oid/bad-input", "OBJECT IDENTIFIER content");
|
|
496
|
+
var len = guard.bytes.lengthOf(buf);
|
|
497
|
+
if (len === 0) throw new OidError("oid/empty", "OBJECT IDENTIFIER content is empty");
|
|
476
498
|
var arcs = [];
|
|
477
499
|
var arcStart = 0;
|
|
478
|
-
for (var i = 0; i <
|
|
500
|
+
for (var i = 0; i < len; i++) {
|
|
479
501
|
var b = buf[i];
|
|
480
502
|
if (i === arcStart && b === 0x80) throw new OidError("oid/non-minimal", "non-minimal sub-identifier (leading 0x80)");
|
|
481
503
|
if (i - arcStart >= constants.LIMITS.OID_MAX_SUBIDENTIFIER_BYTES) {
|
|
@@ -493,7 +515,7 @@ function decodeOidContent(buf) {
|
|
|
493
515
|
arcStart = i + 1;
|
|
494
516
|
}
|
|
495
517
|
}
|
|
496
|
-
if (arcStart !==
|
|
518
|
+
if (arcStart !== len) throw new OidError("oid/truncated", "OBJECT IDENTIFIER ends mid sub-identifier");
|
|
497
519
|
var first = arcs[0];
|
|
498
520
|
var a1, a2;
|
|
499
521
|
if (first < 40n) { a1 = 0n; a2 = first; }
|
|
@@ -540,17 +562,9 @@ function readNumericString(node) {
|
|
|
540
562
|
* so the bytes written would not carry the string the caller handed in, and `_decodeUtf8Strict`
|
|
541
563
|
* refuses exactly those bytes on the way back. The builder answers the same way rather than
|
|
542
564
|
* silently changing the text, which is the rule `_decodeUtf16be` already applies to BMPString. */
|
|
565
|
+
/** @internal @guard-via guard\.text\.assertWellFormedUtf16\( */
|
|
543
566
|
function _assertWellFormedUtf16(s) {
|
|
544
|
-
|
|
545
|
-
var c = _charCodeAt(s, i);
|
|
546
|
-
if (c >= 0xD800 && c <= 0xDBFF) {
|
|
547
|
-
var n = (i + 1) < s.length ? _charCodeAt(s, i + 1) : -1;
|
|
548
|
-
if (!(n >= 0xDC00 && n <= 0xDFFF)) throw new Asn1Error("asn1/bad-utf8-string", "UTF8String has an unpaired high surrogate");
|
|
549
|
-
i++;
|
|
550
|
-
} else if (c >= 0xDC00 && c <= 0xDFFF) {
|
|
551
|
-
throw new Asn1Error("asn1/bad-utf8-string", "UTF8String has an unpaired low surrogate");
|
|
552
|
-
}
|
|
553
|
-
}
|
|
567
|
+
return guard.text.assertWellFormedUtf16(s, Asn1Error, "asn1/bad-utf8-string", "UTF8String");
|
|
554
568
|
}
|
|
555
569
|
|
|
556
570
|
function _decodeUtf8Strict(buf) {
|
|
@@ -688,7 +702,7 @@ function encodeLength(n) {
|
|
|
688
702
|
if (n < 0x80) return Buffer.from([n]);
|
|
689
703
|
var bytes = [];
|
|
690
704
|
var v = n;
|
|
691
|
-
while (v > 0) { bytes
|
|
705
|
+
while (v > 0) { _unshift(bytes, v & 0xff); v = Math.floor(v / 256); }
|
|
692
706
|
if (bytes.length > 126) throw new Asn1Error("asn1/length-too-large", "length needs more than 126 octets");
|
|
693
707
|
return Buffer.concat([Buffer.from([0x80 | bytes.length]), Buffer.from(bytes)]);
|
|
694
708
|
}
|
|
@@ -728,7 +742,7 @@ function encodeIdentifier(classBits, constructed, tagNumber) {
|
|
|
728
742
|
if (tagNumber < 0x1f) return Buffer.from([lead | tagNumber]);
|
|
729
743
|
var body = [];
|
|
730
744
|
var v = tagNumber;
|
|
731
|
-
do { body
|
|
745
|
+
do { _unshift(body, v & 0x7f); v = Math.floor(v / 128); } while (v > 0);
|
|
732
746
|
if (body.length > 4) throw new Asn1Error("asn1/tag-too-large", "high-tag-number too large");
|
|
733
747
|
for (var i = 0; i < body.length - 1; i++) body[i] |= 0x80;
|
|
734
748
|
return Buffer.concat([Buffer.from([lead | 0x1f]), Buffer.from(body)]);
|
|
@@ -816,8 +830,8 @@ function intToDer(v) {
|
|
|
816
830
|
var bytes = [];
|
|
817
831
|
if (v > 0n) {
|
|
818
832
|
var t = v;
|
|
819
|
-
while (t > 0n) { bytes
|
|
820
|
-
if (bytes[0] & 0x80) bytes
|
|
833
|
+
while (t > 0n) { _unshift(bytes, Number(t & 0xffn)); t >>= 8n; }
|
|
834
|
+
if (bytes[0] & 0x80) _unshift(bytes, 0x00);
|
|
821
835
|
} else {
|
|
822
836
|
var len = 1;
|
|
823
837
|
while (v < -(1n << BigInt(8 * len - 1))) len += 1;
|
|
@@ -831,12 +845,15 @@ function encodeOidContent(dotted) {
|
|
|
831
845
|
guard.identifier.assertCanonicalOid(dotted, _oidErr, "oid/bad-input", "OID", "oid/bad-arc", "oid/too-many-subidentifiers");
|
|
832
846
|
var arcs = dotted.split(".").map(function (p) { return BigInt(p); });
|
|
833
847
|
var a1 = arcs[0], a2 = arcs[1];
|
|
834
|
-
|
|
848
|
+
/** @internal The sub-identifier list through the capture: assembled by a live
|
|
849
|
+
* `Array.prototype.concat`, the arcs this encodes were that method's to choose, and the result is
|
|
850
|
+
* the OID a signature or an algorithm is named by. */
|
|
851
|
+
var subids = intrinsic.concat([a1 * 40n + a2], arcs.slice(2));
|
|
835
852
|
var out = [];
|
|
836
853
|
for (var s = 0; s < subids.length; s++) {
|
|
837
854
|
var body = [];
|
|
838
855
|
var v = subids[s];
|
|
839
|
-
do { body
|
|
856
|
+
do { _unshift(body, Number(v & 0x7fn)); v >>= 7n; } while (v > 0n);
|
|
840
857
|
if (body.length > constants.LIMITS.OID_MAX_SUBIDENTIFIER_BYTES) {
|
|
841
858
|
throw new OidError("oid/subidentifier-too-large",
|
|
842
859
|
"OID sub-identifier exceeds " + constants.LIMITS.OID_MAX_SUBIDENTIFIER_BYTES + " octets");
|
|
@@ -987,15 +1004,21 @@ var build = intrinsic.assign(intrinsic.create(null), {
|
|
|
987
1004
|
sequence: function (children) {
|
|
988
1005
|
return encodeConstructed(CLASS_UNIVERSAL, TAGS.SEQUENCE, _asBufferArray(children, "build.sequence"));
|
|
989
1006
|
},
|
|
1007
|
+
/** @internal The DER SET ordering goes through the captured copy-and-sort. Read off
|
|
1008
|
+
* `Array.prototype`, `slice` and `sort` decide the bytes every SET OF in the toolkit emits, and these
|
|
1009
|
+
* two builders are where nearly all of them are built: a replacement reorders or substitutes members
|
|
1010
|
+
* after whatever validated them, on content a caller goes on to sign. */
|
|
1011
|
+
/** @internal The COMPARATOR goes through the captured operation, as the copy and the sort beside it
|
|
1012
|
+
* do. A DER SET OF is ordered by its members' encoded bytes, so whatever decides "before" decides
|
|
1013
|
+
* the bytes emitted: a replacement answering a fixed order sets the order of every SET and SET OF
|
|
1014
|
+
* this toolkit builds, after whatever validated the members and on bytes a verb then signs. */
|
|
990
1015
|
set: function (children) {
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
return encodeConstructed(CLASS_UNIVERSAL, TAGS.SET, arr);
|
|
1016
|
+
return encodeConstructed(CLASS_UNIVERSAL, TAGS.SET,
|
|
1017
|
+
guard.list.sortedCopy(_asBufferArray(children, "build.set"), intrinsic.compare));
|
|
994
1018
|
},
|
|
995
1019
|
setOf: function (children) {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
return encodeConstructed(CLASS_UNIVERSAL, TAGS.SET, arr);
|
|
1020
|
+
return encodeConstructed(CLASS_UNIVERSAL, TAGS.SET,
|
|
1021
|
+
guard.list.sortedCopy(_asBufferArray(children, "build.setOf"), intrinsic.compare));
|
|
999
1022
|
},
|
|
1000
1023
|
explicit: function (tagNumber, inner) { return encodeTLV(CLASS_CONTEXT, true, tagNumber, _asBuffer(inner, "build.explicit")); },
|
|
1001
1024
|
contextPrimitive: function (tagNumber, content) { return encodeTLV(CLASS_CONTEXT, false, tagNumber, _asBuffer(content, "build.contextPrimitive")); },
|