@blamejs/pki 0.9.5 → 0.9.7
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 +66 -0
- package/README.md +7 -6
- package/bin/pki.js +110 -34
- package/lib/acme.js +91 -46
- package/lib/asn1-der.js +6 -5
- package/lib/attrcert-sign.js +4 -3
- package/lib/cmp-build.js +16 -6
- package/lib/cmp-session.js +26 -3
- package/lib/cmp-verify.js +24 -2
- package/lib/cms-compress.js +3 -3
- package/lib/cms-digest.js +3 -3
- package/lib/cms-verify.js +18 -8
- package/lib/compromised-keys.js +114 -0
- package/lib/constants.js +1 -0
- package/lib/crl-sign.js +3 -3
- package/lib/crmf-sign.js +2 -0
- package/lib/csr-sign.js +5 -1
- package/lib/ct.js +16 -3
- package/lib/est.js +44 -29
- package/lib/framework-error.js +20 -7
- package/lib/guard-all.js +2 -0
- package/lib/guard-distrust.js +344 -0
- package/lib/guard-json.js +1 -1
- package/lib/guard-verdict.js +16 -3
- package/lib/http-transport.js +412 -41
- package/lib/jose.js +1 -1
- package/lib/lint.js +49 -7
- package/lib/ocsp-verify.js +9 -3
- package/lib/ocsp.js +8 -4
- package/lib/oid.js +4 -0
- package/lib/path-validate.js +131 -16
- package/lib/possession.js +6 -2
- package/lib/related-cert.js +2 -2
- package/lib/revocation-fetch.js +39 -9
- package/lib/scep.js +28 -13
- package/lib/schema-pkix.js +3 -3
- package/lib/sigstore.js +11 -6
- package/lib/smime.js +18 -13
- package/lib/tls-cert-compress.js +6 -5
- package/lib/trust.js +159 -20
- package/lib/trustanchor-build.js +16 -1
- package/lib/tsp-sign.js +7 -1
- package/lib/webauthn-mds.js +30 -8
- package/lib/webauthn.js +46 -14
- package/lib/x509-sign.js +16 -5
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/lib/framework-error.js
CHANGED
|
@@ -78,10 +78,23 @@ function _isCodeShape(c) {
|
|
|
78
78
|
* if (e instanceof pki.errors.PkiError) console.error(e.code);
|
|
79
79
|
* }
|
|
80
80
|
*/
|
|
81
|
+
/** @internal A field of an error, defined as an own data property the way an assignment to a plain
|
|
82
|
+
* object creates one. An assignment runs a setter inherited from `Object.prototype`, and a setter
|
|
83
|
+
* that throws turned the construction of every error into that throw. The descriptor has no
|
|
84
|
+
* prototype, since `defineProperty` reads an inherited `get` or `set` off a literal one. */
|
|
85
|
+
function _own(target, key, value) {
|
|
86
|
+
var d = intrinsic.create(null);
|
|
87
|
+
d.value = value;
|
|
88
|
+
d.writable = true;
|
|
89
|
+
d.enumerable = true;
|
|
90
|
+
d.configurable = true;
|
|
91
|
+
_defineProperty(target, key, d);
|
|
92
|
+
}
|
|
93
|
+
|
|
81
94
|
class PkiError extends Error {
|
|
82
95
|
constructor(message, code) {
|
|
83
96
|
super(message);
|
|
84
|
-
this
|
|
97
|
+
_own(this, "name", "PkiError");
|
|
85
98
|
var c = code || "pki/invalid";
|
|
86
99
|
if (typeof c !== "string" || !_isCodeShape(c)) {
|
|
87
100
|
throw new TypeError(
|
|
@@ -89,9 +102,9 @@ class PkiError extends Error {
|
|
|
89
102
|
" (PkiError takes (message, code); defineClass subclasses take (code, message))"
|
|
90
103
|
);
|
|
91
104
|
}
|
|
92
|
-
this
|
|
93
|
-
this
|
|
94
|
-
this
|
|
105
|
+
_own(this, "code", c);
|
|
106
|
+
_own(this, "isPkiError", true);
|
|
107
|
+
_own(this, "permanent", !_TRANSIENT_REASONS[_reasonOf(c)]);
|
|
95
108
|
}
|
|
96
109
|
}
|
|
97
110
|
|
|
@@ -129,10 +142,10 @@ function defineClass(name, opts) {
|
|
|
129
142
|
var GeneratedError = class extends PkiError {
|
|
130
143
|
constructor(code, message, arg3) {
|
|
131
144
|
super(message, code);
|
|
132
|
-
this
|
|
133
|
-
this
|
|
145
|
+
_own(this, "name", name);
|
|
146
|
+
_own(this, flagKey, true);
|
|
134
147
|
if (withCause) {
|
|
135
|
-
if (arg3 !== undefined) this
|
|
148
|
+
if (arg3 !== undefined) _own(this, "cause", arg3);
|
|
136
149
|
} else if (arg3 !== undefined) {
|
|
137
150
|
throw new TypeError(name + " takes (code, message); to thread a cause, define the class with { withCause: true }");
|
|
138
151
|
}
|
package/lib/guard-all.js
CHANGED
|
@@ -22,6 +22,7 @@ var secret = require("./guard-secret");
|
|
|
22
22
|
var parsed = require("./guard-parsed");
|
|
23
23
|
var verdict = require("./guard-verdict");
|
|
24
24
|
var async_ = require("./guard-async");
|
|
25
|
+
var distrust = require("./guard-distrust");
|
|
25
26
|
|
|
26
27
|
module.exports = _freeze({
|
|
27
28
|
bytes: bytes,
|
|
@@ -42,4 +43,5 @@ module.exports = _freeze({
|
|
|
42
43
|
parsed: parsed,
|
|
43
44
|
verdict: verdict,
|
|
44
45
|
async: async_,
|
|
46
|
+
distrust: distrust,
|
|
45
47
|
});
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
// @internal
|
|
5
|
+
|
|
6
|
+
var util = require("util");
|
|
7
|
+
var intrinsic = require("./guard-intrinsic");
|
|
8
|
+
var constants = require("./constants");
|
|
9
|
+
var gBytes = require("./guard-bytes");
|
|
10
|
+
var gList = require("./guard-list");
|
|
11
|
+
var gCrypto = require("./guard-crypto");
|
|
12
|
+
var compromisedKeys = require("./compromised-keys");
|
|
13
|
+
|
|
14
|
+
var _freeze = intrinsic.freeze;
|
|
15
|
+
var _isArray = intrinsic.isArray;
|
|
16
|
+
var _create = intrinsic.create;
|
|
17
|
+
var _hasOwn = intrinsic.hasOwn;
|
|
18
|
+
var _getOwnPropertyDescriptor = intrinsic.getOwnPropertyDescriptor;
|
|
19
|
+
var _bufToString = intrinsic.bufToString;
|
|
20
|
+
var _charCodeAt = intrinsic.uncurry(String.prototype.charCodeAt);
|
|
21
|
+
var _fromCharCode = intrinsic.fromCharCode;
|
|
22
|
+
var _isProxy = util.types.isProxy;
|
|
23
|
+
|
|
24
|
+
var DIGEST_BYTES = 32;
|
|
25
|
+
var DIGEST_HEX = 64;
|
|
26
|
+
|
|
27
|
+
/** @internal One hexadecimal character, folded to lower case, or -1. The fold is arithmetic on the
|
|
28
|
+
* character code, so the normalized digest is not the answer of a `toLowerCase` read off the live
|
|
29
|
+
* prototype. */
|
|
30
|
+
function _lowerHexCode(c) {
|
|
31
|
+
if (c >= 48 && c <= 57) return c;
|
|
32
|
+
if (c >= 97 && c <= 102) return c;
|
|
33
|
+
if (c >= 65 && c <= 70) return c + 32;
|
|
34
|
+
return -1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function _hexEntry(s, E, code, where) {
|
|
38
|
+
if (s.length !== DIGEST_HEX) {
|
|
39
|
+
throw E(code, where + " must be a SHA-256 digest of a SubjectPublicKeyInfo: 64 hexadecimal characters or 32 bytes, got " + s.length + " characters");
|
|
40
|
+
}
|
|
41
|
+
var out = "";
|
|
42
|
+
for (var i = 0; i < DIGEST_HEX; i++) {
|
|
43
|
+
var c = _lowerHexCode(_charCodeAt(s, i));
|
|
44
|
+
if (c === -1) throw E(code, where + " must be 64 hexadecimal characters with no separators; character " + i + " is not a hexadecimal digit");
|
|
45
|
+
out += _fromCharCode(c);
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function _bytesEntry(v, E, code, where) {
|
|
51
|
+
var copy = gBytes.snapshotSource(v, E, code, where);
|
|
52
|
+
if (copy.length !== DIGEST_BYTES) {
|
|
53
|
+
throw E(code, where + " must be a SHA-256 digest of a SubjectPublicKeyInfo: 32 bytes or 64 hexadecimal characters, got " + copy.length + " bytes");
|
|
54
|
+
}
|
|
55
|
+
return _bufToString(copy, "hex");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The caller's list of distrusted keys, read once and returned as a frozen list of lower-case hex
|
|
59
|
+
* digests with duplicates removed, or `null` when the option is absent or `null`. Each entry is the
|
|
60
|
+
* SHA-256 digest of a certificate's DER SubjectPublicKeyInfo, given as 64 hexadecimal characters or
|
|
61
|
+
* as 32 bytes.
|
|
62
|
+
*
|
|
63
|
+
* Every slot is read through its descriptor, so a hole, an accessor and a Proxy are refused rather
|
|
64
|
+
* than read: a hole is answered by the array prototype and an accessor answers each read separately,
|
|
65
|
+
* and either one makes the list the check uses differ from the list the caller wrote. The list is a
|
|
66
|
+
* refusal list, so reading it as shorter than it is lets through a key the caller distrusted.
|
|
67
|
+
*
|
|
68
|
+
* @enforced-by behavioral -- the defect is a door that does not call this, which has no rename-proof
|
|
69
|
+
* code shape; the guard is the RED vector each verb carries, driving a listed key through the
|
|
70
|
+
* shipped verb and asserting the refusal.
|
|
71
|
+
*/
|
|
72
|
+
function keys(value, E, code, label) {
|
|
73
|
+
if (value === undefined || value === null) return null;
|
|
74
|
+
if (typeof value !== "object" || _isProxy(value) || !_isArray(value)) {
|
|
75
|
+
throw E(code, label + " must be an array of SHA-256 SubjectPublicKeyInfo digests");
|
|
76
|
+
}
|
|
77
|
+
var n = gList.count(value, "keys");
|
|
78
|
+
if (n > constants.LIMITS.DISTRUSTED_KEYS_MAX) {
|
|
79
|
+
throw E(code, label + " has " + n + " entries, more than the " + constants.LIMITS.DISTRUSTED_KEYS_MAX + " this toolkit reads");
|
|
80
|
+
}
|
|
81
|
+
var seen = _create(null);
|
|
82
|
+
var out = [];
|
|
83
|
+
for (var i = 0; i < n; i++) {
|
|
84
|
+
var where = label + "[" + i + "]";
|
|
85
|
+
var d = _getOwnPropertyDescriptor(value, i);
|
|
86
|
+
if (!d) throw E(code, where + " is a hole; every entry must be present");
|
|
87
|
+
if (d.get || d.set) throw E(code, where + " is an accessor; every entry must be a data property");
|
|
88
|
+
var v = d.value;
|
|
89
|
+
var hex;
|
|
90
|
+
if (typeof v === "string") hex = _hexEntry(v, E, code, where);
|
|
91
|
+
else if (gBytes.isByteSource(v)) hex = _bytesEntry(v, E, code, where);
|
|
92
|
+
else throw E(code, where + " must be 64 hexadecimal characters or 32 bytes");
|
|
93
|
+
if (_hasOwn(seen, hex)) continue;
|
|
94
|
+
seen[hex] = true;
|
|
95
|
+
gList.append(out, hex);
|
|
96
|
+
}
|
|
97
|
+
return _freeze(out);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** The lower-case hex SHA-256 of `spkiBytes`, the DER SubjectPublicKeyInfo exactly as a certificate
|
|
101
|
+
* or an anchor carries it.
|
|
102
|
+
*
|
|
103
|
+
* @enforced-by behavioral -- a digest has no rename-proof shape that separates this use from the
|
|
104
|
+
* others `guard-crypto` serves; the guard is the membership vector, which fails if the bytes hashed
|
|
105
|
+
* are not the ones listed.
|
|
106
|
+
*/
|
|
107
|
+
function digest(spkiBytes) {
|
|
108
|
+
return gCrypto.digest("sha256", spkiBytes, { output: "hex" });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** @internal The codec and the registry, reached through inline requires: both load this module
|
|
112
|
+
* through guard-all, so both are loaded by the time a digest is asked for. */
|
|
113
|
+
function _codec() {
|
|
114
|
+
return {
|
|
115
|
+
asn1: require("./asn1-der"), // allow:inline-require -- circular load: asn1-der -> guard-all -> this module
|
|
116
|
+
oid: require("./oid"), // allow:inline-require -- circular load: oid -> guard-all -> this module
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** @internal The SubjectPublicKeyInfo with an identifier that restricts the key's use replaced by the
|
|
121
|
+
* key's own: id-ecDH and id-ecMQV (RFC 5480 sec. 2.1.2) become ecPublicKey with the curve kept, and
|
|
122
|
+
* RSASSA-PSS and RSAES-OAEP (RFC 4055) become rsaEncryption with NULL. The subjectPublicKey bits are
|
|
123
|
+
* the same key in each pair. Any other identifier is returned unchanged. */
|
|
124
|
+
function _unrestricted(spkiBytes, c) {
|
|
125
|
+
var b = c.asn1.build;
|
|
126
|
+
var root = c.asn1.decode(spkiBytes);
|
|
127
|
+
var alg = root.children[0];
|
|
128
|
+
var name = c.oid.name(c.asn1.read.oid(alg.children[0]));
|
|
129
|
+
if (name === "ecDH" || name === "ecMQV") {
|
|
130
|
+
var params = alg.children.length > 1 ? [b.raw(alg.children[1].bytes)] : [];
|
|
131
|
+
return b.sequence([b.sequence(intrinsic.concatList([b.oid(c.oid.byName("ecPublicKey"))], params)), b.raw(root.children[1].bytes)]);
|
|
132
|
+
}
|
|
133
|
+
if (name === "rsassaPss" || name === "rsaesOaep") {
|
|
134
|
+
return b.sequence([b.sequence([b.oid(c.oid.byName("rsaEncryption")), b.nullValue()]), b.raw(root.children[1].bytes)]);
|
|
135
|
+
}
|
|
136
|
+
return spkiBytes;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** @internal The key in canonical form, or null when node does not read the bytes as a public key.
|
|
140
|
+
* An elliptic curve key is written with ecPublicKey, its named curve and an uncompressed point,
|
|
141
|
+
* whatever point form or parameter form it was read in. Any other key node writes as a JWK is
|
|
142
|
+
* written the way node writes a key it builds from one, which puts an RSA key under rsaEncryption
|
|
143
|
+
* with NULL. A key with neither form is re-exported as node read it. */
|
|
144
|
+
function _canonicalSpki(spkiBytes) {
|
|
145
|
+
try {
|
|
146
|
+
var c = _codec();
|
|
147
|
+
var spki = _unrestricted(spkiBytes, c);
|
|
148
|
+
var key = gCrypto.publicKey({ key: spki, format: "der", type: "spki" });
|
|
149
|
+
if (gCrypto.keyTypeOf(key) === "ec") {
|
|
150
|
+
var details = gCrypto.keyDetailsOf(key);
|
|
151
|
+
var curveOid = details && typeof details.namedCurve === "string" ? c.oid.byName(details.namedCurve) : undefined;
|
|
152
|
+
if (curveOid !== undefined) {
|
|
153
|
+
var b = c.asn1.build;
|
|
154
|
+
var point = c.asn1.read.bitString(c.asn1.decode(spki).children[1]).bytes;
|
|
155
|
+
var uncompressed = gCrypto.ecConvertPoint(point, details.namedCurve, "uncompressed");
|
|
156
|
+
return b.sequence([b.sequence([b.oid(c.oid.byName("ecPublicKey")), b.oid(curveOid)]), b.bitString(uncompressed, 0)]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
var jwk = null;
|
|
160
|
+
try { jwk = gCrypto.exportKey(key, { format: "jwk" }); }
|
|
161
|
+
catch (_je) {
|
|
162
|
+
// allow:swallow-unverified a key type with no JWK form is re-exported as node read it
|
|
163
|
+
jwk = null;
|
|
164
|
+
}
|
|
165
|
+
if (jwk !== null) key = gCrypto.publicKey({ key: jwk, format: "jwk" });
|
|
166
|
+
return gCrypto.exportKey(key, { type: "spki", format: "der" });
|
|
167
|
+
} catch (_e) {
|
|
168
|
+
// allow:swallow-unverified a key node does not read has no second encoding, and the raw digest still decides
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The digest a distrust entry names a key by: the lower-case hex SHA-256 of the key's
|
|
174
|
+
* SubjectPublicKeyInfo in canonical form, an uncompressed EC point with its named curve and an RSA
|
|
175
|
+
* key with NULL parameters. That is how nearly every certificate encodes a key, so for those it is
|
|
176
|
+
* the digest of the bytes the certificate carries. A key node does not read is digested as given.
|
|
177
|
+
*
|
|
178
|
+
* @enforced-by behavioral -- a digest has no rename-proof shape; the guard is the two-direction
|
|
179
|
+
* re-encoding vectors in guard-distrust.test.js.
|
|
180
|
+
*/
|
|
181
|
+
function keyDigest(spkiBytes) {
|
|
182
|
+
var canonical = _canonicalSpki(spkiBytes);
|
|
183
|
+
return digest(canonical !== null ? canonical : spkiBytes);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** @internal The built-in list, keyed by digest. */
|
|
187
|
+
var COMPROMISED = _create(null);
|
|
188
|
+
for (var _ci = 0; _ci < compromisedKeys.KEYS.length; _ci++) COMPROMISED[compromisedKeys.KEYS[_ci].spkiSha256] = compromisedKeys.KEYS[_ci];
|
|
189
|
+
|
|
190
|
+
/** @internal The curves the small-scalar table is built for, by JWK curve name, with the width of a
|
|
191
|
+
* coordinate in bytes, and the scalar bound. Each table holds the x-coordinates of d*G for d from 1
|
|
192
|
+
* to the bound minus one, built once per curve on first use (measured on Node 26: 4 ms for P-256,
|
|
193
|
+
* 138 ms for P-384, 321 ms for P-521). The points d*G and (n-d)*G share an x-coordinate, so one
|
|
194
|
+
* comparison covers both. */
|
|
195
|
+
var SMALL_SCALAR_CURVES = intrinsic.assign(_create(null), {
|
|
196
|
+
"P-256": { name: "prime256v1", width: 32 },
|
|
197
|
+
"P-384": { name: "secp384r1", width: 48 },
|
|
198
|
+
"P-521": { name: "secp521r1", width: 66 },
|
|
199
|
+
"secp256k1": { name: "secp256k1", width: 32 },
|
|
200
|
+
});
|
|
201
|
+
var SMALL_SCALAR_BOUND = 256;
|
|
202
|
+
var _smallScalarTables = _create(null);
|
|
203
|
+
function _smallScalarTable(curve) {
|
|
204
|
+
if (_smallScalarTables[curve.name]) return _smallScalarTables[curve.name];
|
|
205
|
+
var table = _create(null);
|
|
206
|
+
var scalar = intrinsic.bufferAlloc(curve.width);
|
|
207
|
+
for (var d = 1; d < SMALL_SCALAR_BOUND; d++) {
|
|
208
|
+
scalar[curve.width - 1] = d;
|
|
209
|
+
table[_bufToString(gCrypto.ecPointFromScalar(curve.name, scalar), "hex", 1, 1 + curve.width)] = true;
|
|
210
|
+
}
|
|
211
|
+
_smallScalarTables[curve.name] = table;
|
|
212
|
+
return table;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** The curve name of an elliptic curve key whose private scalar is below 256, or below 256 short of
|
|
216
|
+
* the group order, or `null` for any other key. Anyone can recover such a private key from the public
|
|
217
|
+
* key by trying each candidate. The key is read through node's own import after an id-ecDH or
|
|
218
|
+
* id-ecMQV identifier is read as ecPublicKey, so a named or explicit curve and an uncompressed,
|
|
219
|
+
* compressed or hybrid point are all judged; a point that is not on the curve is not a key and
|
|
220
|
+
* answers `null`. The covered curves are P-256, P-384, P-521 and secp256k1.
|
|
221
|
+
*
|
|
222
|
+
* @enforced-by behavioral -- the defect is a curve or an encoding the table does not reach, which has
|
|
223
|
+
* no code shape; the guard is the per-curve and per-encoding vectors in guard-distrust.test.js and
|
|
224
|
+
* lint-key-compromise.test.js.
|
|
225
|
+
*/
|
|
226
|
+
function smallEcScalar(spkiBytes) {
|
|
227
|
+
var jwk;
|
|
228
|
+
try {
|
|
229
|
+
var spki = _unrestricted(spkiBytes, _codec());
|
|
230
|
+
var key = gCrypto.publicKey({ key: spki, format: "der", type: "spki" });
|
|
231
|
+
if (gCrypto.keyTypeOf(key) !== "ec") return null;
|
|
232
|
+
jwk = gCrypto.exportKey(key, { format: "jwk" });
|
|
233
|
+
} catch (_e) {
|
|
234
|
+
// allow:swallow-unverified a key node does not import as an elliptic curve point has no scalar to judge
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
var curve = typeof jwk.crv === "string" && _hasOwn(SMALL_SCALAR_CURVES, jwk.crv) ? SMALL_SCALAR_CURVES[jwk.crv] : null;
|
|
238
|
+
if (curve === null || typeof jwk.x !== "string") return null;
|
|
239
|
+
return _smallScalarTable(curve)[_bufToString(intrinsic.bufferFrom(jwk.x, "base64url"), "hex")] === true ? curve.name : null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Why the private key of `spkiBytes` is public, as one sentence, or `null`. A key is compromised
|
|
243
|
+
* when its digest, as given or as `keyDigest` computes it, is on the list `pki.trust.knownCompromisedKeys()`
|
|
244
|
+
* returns, or when `smallEcScalar` answers a curve for it. Every verifier in this toolkit refuses such
|
|
245
|
+
* a key, with no option that turns the refusal off.
|
|
246
|
+
*
|
|
247
|
+
* @enforced-by behavioral -- the defect is a door that does not refuse, which has no rename-proof code
|
|
248
|
+
* shape; every door reaches this through `has`, and the guard is the compromised-key vector each verb
|
|
249
|
+
* carries.
|
|
250
|
+
*/
|
|
251
|
+
function compromised(spkiBytes) {
|
|
252
|
+
if (spkiBytes === null || spkiBytes === undefined) return null;
|
|
253
|
+
var entry = COMPROMISED[digest(spkiBytes)] || COMPROMISED[keyDigest(spkiBytes)];
|
|
254
|
+
if (entry) return "the key is the published key of " + entry.name + ": " + entry.reason;
|
|
255
|
+
var curve = smallEcScalar(spkiBytes);
|
|
256
|
+
if (curve !== null) return "the " + curve + " private key is an integer below " + SMALL_SCALAR_BOUND + ", so anyone can recover it from the public key";
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Throws `E(code, ...)` when `compromised(spkiBytes)` answers, naming the key by `label` and saying
|
|
261
|
+
* why its private key is public. A verb that certifies a key, signs with one or requests a
|
|
262
|
+
* certificate for one calls this on each key it is handed.
|
|
263
|
+
*
|
|
264
|
+
* @enforced-by behavioral -- the defect is a signing verb that does not call this, which has no
|
|
265
|
+
* rename-proof code shape; the guard is the compromised-key vector each signing verb carries.
|
|
266
|
+
*/
|
|
267
|
+
function refuseCompromised(spkiBytes, E, code, label) {
|
|
268
|
+
var why = compromised(spkiBytes);
|
|
269
|
+
if (why !== null) throw E(code, label + " is a compromised key: " + why);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Why the key `spkiBytes` names is refused, as the end of a sentence about the key, or `null` when
|
|
273
|
+
* `has(list, spkiBytes)` is false: "is a compromised key: " and the `compromised` reason, or "is
|
|
274
|
+
* listed in " and `label`, the option the caller's list came from.
|
|
275
|
+
*
|
|
276
|
+
* @enforced-by behavioral -- a message has no rename-proof code shape; the guard is the vectors that
|
|
277
|
+
* assert each door's message names a compromised key as one and a listed key as listed.
|
|
278
|
+
*/
|
|
279
|
+
function refusal(list, spkiBytes, label) {
|
|
280
|
+
var why = compromised(spkiBytes);
|
|
281
|
+
if (why !== null) return "is a compromised key: " + why;
|
|
282
|
+
return has(list, spkiBytes) ? "is listed in " + label : null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Whether the key `spkiBytes` names is refused: it is compromised (see `compromised`), or it is on
|
|
286
|
+
* `list`, a value `keys` returned. A `null` or empty list still refuses a compromised key. The digest
|
|
287
|
+
* of the bytes as given is compared with the list, and so is `keyDigest`, so an entry `keyDigest`
|
|
288
|
+
* computed matches the key however a certificate encodes it (a compressed EC point, RSA parameters
|
|
289
|
+
* without NULL), and an entry computed over raw bytes matches a certificate with the same bytes.
|
|
290
|
+
*
|
|
291
|
+
* @enforced-by behavioral -- membership has no rename-proof code shape; the RED vectors are the
|
|
292
|
+
* listed-key and compromised-key refusals at each verb, and the re-encodings in guard-distrust.test.js
|
|
293
|
+
* in both directions.
|
|
294
|
+
*/
|
|
295
|
+
function has(list, spkiBytes) {
|
|
296
|
+
if (spkiBytes === null || spkiBytes === undefined) return false;
|
|
297
|
+
if (compromised(spkiBytes) !== null) return true;
|
|
298
|
+
if (list === null || list === undefined || list.length === 0) return false;
|
|
299
|
+
if (gList.contains(list, digest(spkiBytes))) return true;
|
|
300
|
+
return gList.contains(list, keyDigest(spkiBytes));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Every entry of two lists that `keys` returned, once, as a frozen list; `null` when both are
|
|
304
|
+
* `null`. A transport default and a per-request list are combined this way, so a request cannot
|
|
305
|
+
* remove a key the default distrusts by naming a shorter list.
|
|
306
|
+
*
|
|
307
|
+
* @enforced-by behavioral -- a union has no rename-proof code shape; the RED vector is a request
|
|
308
|
+
* list that omits a default entry and the default entry still being refused.
|
|
309
|
+
*/
|
|
310
|
+
function union(a, b) {
|
|
311
|
+
if ((a === null || a === undefined) && (b === null || b === undefined)) return null;
|
|
312
|
+
var seen = _create(null);
|
|
313
|
+
var out = [];
|
|
314
|
+
var parts = [a || [], b || []];
|
|
315
|
+
for (var p = 0; p < 2; p++) {
|
|
316
|
+
var part = parts[p];
|
|
317
|
+
for (var i = 0, n = gList.count(part, "union"); i < n; i++) {
|
|
318
|
+
var hex = part[i];
|
|
319
|
+
if (_hasOwn(seen, hex)) continue;
|
|
320
|
+
seen[hex] = true;
|
|
321
|
+
gList.append(out, hex);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return _freeze(out);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/** A copy of the built-in list: one `{ spkiSha256, name, reason, sources }` record per key, each
|
|
328
|
+
* frozen, in a new array the caller may change.
|
|
329
|
+
*
|
|
330
|
+
* @enforced-by behavioral -- a copy has no rename-proof code shape; the guard is the vector that
|
|
331
|
+
* changes the returned array and reads the list again.
|
|
332
|
+
*/
|
|
333
|
+
function knownCompromised() {
|
|
334
|
+
var out = [];
|
|
335
|
+
for (var i = 0; i < compromisedKeys.KEYS.length; i++) gList.append(out, compromisedKeys.KEYS[i]);
|
|
336
|
+
return out;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
module.exports = _freeze({
|
|
340
|
+
keys: keys, digest: digest, keyDigest: keyDigest, has: has, union: union,
|
|
341
|
+
compromised: compromised, refuseCompromised: refuseCompromised, refusal: refusal,
|
|
342
|
+
smallEcScalar: smallEcScalar, knownCompromised: knownCompromised,
|
|
343
|
+
SMALL_SCALAR_BOUND: SMALL_SCALAR_BOUND,
|
|
344
|
+
});
|
package/lib/guard-json.js
CHANGED
|
@@ -24,7 +24,7 @@ var _hexVal = require("./guard-encoding").hexNibble;
|
|
|
24
24
|
// @enforced-by json-parse-not-via-guard
|
|
25
25
|
function parse(input, ErrorClass, spec) {
|
|
26
26
|
function E(code, message, cause) { return new ErrorClass(code, message, cause); }
|
|
27
|
-
if (spec.maxBytes
|
|
27
|
+
if (spec.maxBytes == null || spec.maxDepth == null) {
|
|
28
28
|
throw new TypeError("guard.json.parse: spec.maxBytes and spec.maxDepth are required");
|
|
29
29
|
}
|
|
30
30
|
var maxBytes = limits.cap(spec.maxBytes, "guard.json.parse spec.maxBytes", undefined);
|
package/lib/guard-verdict.js
CHANGED
|
@@ -9,6 +9,19 @@ var _defineProperty = require("./guard-intrinsic").defineProperty;
|
|
|
9
9
|
var _getOwnPropertyDescriptor = require("./guard-intrinsic").getOwnPropertyDescriptor;
|
|
10
10
|
var _hasOwn = require("./guard-intrinsic").hasOwn;
|
|
11
11
|
var _Object = require("./guard-intrinsic").Object;
|
|
12
|
+
var _create = require("./guard-intrinsic").create;
|
|
13
|
+
|
|
14
|
+
/** @internal A writable, configurable data descriptor with no prototype. A descriptor written as an
|
|
15
|
+
* object literal inherits from `Object.prototype`, and `defineProperty` reads `get` and `set` through
|
|
16
|
+
* that chain, so an inherited `get` beside the `value` made every define here throw. */
|
|
17
|
+
function _data(value, enumerable) {
|
|
18
|
+
var d = _create(null);
|
|
19
|
+
d.value = value;
|
|
20
|
+
d.enumerable = enumerable;
|
|
21
|
+
d.configurable = true;
|
|
22
|
+
d.writable = true;
|
|
23
|
+
return d;
|
|
24
|
+
}
|
|
12
25
|
|
|
13
26
|
function _copyOwn(out, src) {
|
|
14
27
|
var keys = _keys(src);
|
|
@@ -16,7 +29,7 @@ function _copyOwn(out, src) {
|
|
|
16
29
|
var d = _getOwnPropertyDescriptor(src, keys[i]);
|
|
17
30
|
if (!d) continue;
|
|
18
31
|
var value = _hasOwn(d, "value") ? d.value : src[keys[i]];
|
|
19
|
-
_defineProperty(out, keys[i],
|
|
32
|
+
_defineProperty(out, keys[i], _data(value, true));
|
|
20
33
|
}
|
|
21
34
|
}
|
|
22
35
|
|
|
@@ -49,7 +62,7 @@ function shield(target) {
|
|
|
49
62
|
* machinery looks, and reading it to decide would be the one read that does not count. */
|
|
50
63
|
if (d && _hasOwn(d, "value") && typeof d.value !== "function") return target;
|
|
51
64
|
}
|
|
52
|
-
_defineProperty(target, "then",
|
|
65
|
+
_defineProperty(target, "then", _data(undefined, false));
|
|
53
66
|
return target;
|
|
54
67
|
}
|
|
55
68
|
|
|
@@ -68,7 +81,7 @@ function carries(obj, key) {
|
|
|
68
81
|
|
|
69
82
|
// @enforced-by behavioral -- for a target that cannot be rebuilt, an Error carrying a field a
|
|
70
83
|
function set(target, key, value) {
|
|
71
|
-
_defineProperty(target, key,
|
|
84
|
+
_defineProperty(target, key, _data(value, true));
|
|
72
85
|
/** @internal The target leaves here carrying the sentinel as well. A field set on an object is the
|
|
73
86
|
* same kind of value a verdict is, and a target that reached a promise resolution without one would
|
|
74
87
|
* hand the lookup to Object.prototype however it was assembled. */
|