@bitspark/archon 0.6.2 → 0.8.1
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/dist/src/crypto.d.ts +6 -6
- package/dist/src/crypto.js +71 -13
- package/dist/src/crypto.js.map +1 -1
- package/package.json +1 -1
package/dist/src/crypto.d.ts
CHANGED
|
@@ -20,12 +20,12 @@ export declare function sign(message: Uint8Array, seed: Uint8Array): Uint8Array;
|
|
|
20
20
|
* @noble's `verify` instead throws on a wrong-length input, so we guard the lengths and
|
|
21
21
|
* wrap the call. (Issue #90: found by fast-check fuzzing.)
|
|
22
22
|
*
|
|
23
|
-
* `{ zip215: false }` selects
|
|
24
|
-
*
|
|
25
|
-
* `
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
23
|
+
* `{ zip215: false }` selects @noble's strict decoding (canonical encodings only, no
|
|
24
|
+
* small-order key), which issue #106 chose in the belief that Go's `crypto/ed25519` and
|
|
25
|
+
* `ed25519-dalek` reject small-order keys. They do not — ADR 0008 measured it — and the
|
|
26
|
+
* flag alone does not settle the accepted set either way, because @noble's equation is
|
|
27
|
+
* cofactored and theirs is not. The profile check above is what settles it; the flag is
|
|
28
|
+
* kept because it is the stricter decoder and costs nothing.
|
|
29
29
|
*/
|
|
30
30
|
export declare function verify(signature: Uint8Array, message: Uint8Array, pub: Uint8Array): boolean;
|
|
31
31
|
/**
|
package/dist/src/crypto.js
CHANGED
|
@@ -9,15 +9,49 @@
|
|
|
9
9
|
// - `signInDomain` / `verifyInDomain` — DOMAIN-SEPARATED: Ed25519ph with a context
|
|
10
10
|
// string (RFC 8032 §5.1), the domain, mixed into the hash. A signature made in one
|
|
11
11
|
// domain verifies in no other and never as a raw signature, and a raw signature
|
|
12
|
-
// verifies in no domain —
|
|
13
|
-
// protocols, no cross-talk: the default
|
|
14
|
-
// The domain is the caller's
|
|
15
|
-
// knows nor registers domains.
|
|
12
|
+
// verifies in no domain — for every key the profile admits, by the construction and
|
|
13
|
+
// not by any encoding convention. One key, many protocols, no cross-talk: the default
|
|
14
|
+
// for any protocol that has no bytes on disk yet. The domain is the caller's
|
|
15
|
+
// (`<repo>/<purpose>/v<n>` by convention); archon neither knows nor registers domains.
|
|
16
|
+
//
|
|
17
|
+
// Both verifies apply the verification profile (ADR 0008) before the equation: the
|
|
18
|
+
// public key and the signature's R must be canonical encodings of points of order
|
|
19
|
+
// exactly L — non-identity elements of the prime-order subgroup — and S must be in
|
|
20
|
+
// range. @noble with `{ zip215: false }` already refuses non-canonical encodings and
|
|
21
|
+
// small-order keys, but its equation is cofactored, so it accepts a mixed-order key for
|
|
22
|
+
// every message and a small-order R; Go and dalek, uncofactored, accept a mixed-order
|
|
23
|
+
// key for one message in eight and refuse a small-order R. The profile restricts the
|
|
24
|
+
// inputs to where the two equations agree, and is checked here explicitly so the
|
|
25
|
+
// accepted set is archon's, not the library's.
|
|
16
26
|
//
|
|
17
27
|
// Built on @noble/curves (which replaced @noble/ed25519 when domain separation landed:
|
|
18
28
|
// only the former exposes the Ed25519ph-with-context variant). Both are synchronous, so
|
|
19
29
|
// archon's LeafVerifier.verify stays synchronous, matching logos's sync SPI.
|
|
20
30
|
import { ed25519, ed25519ph } from "@noble/curves/ed25519.js";
|
|
31
|
+
/**
|
|
32
|
+
* ADR 0008: `bytes` is a canonical encoding of a point of order exactly L. False for a
|
|
33
|
+
* point not on the curve, a non-canonical spelling (y ≥ p, or x = 0 with the sign bit
|
|
34
|
+
* set — @noble's strict decoder refuses both), the identity, any small-order point and
|
|
35
|
+
* any mixed-order point. Point arithmetic is the library's; this is a check.
|
|
36
|
+
*/
|
|
37
|
+
function isPrimeOrderPoint(bytes) {
|
|
38
|
+
if (bytes.length !== PUBLIC_KEY_SIZE)
|
|
39
|
+
return false;
|
|
40
|
+
try {
|
|
41
|
+
const point = ed25519.Point.fromBytes(bytes, false);
|
|
42
|
+
return !point.is0() && point.isTorsionFree();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** The profile's shape conditions on a (key, signature) pair. S's range is @noble's. */
|
|
49
|
+
function inProfile(pub, signature) {
|
|
50
|
+
return (signature.length === SIGNATURE_SIZE &&
|
|
51
|
+
pub.length === PUBLIC_KEY_SIZE &&
|
|
52
|
+
isPrimeOrderPoint(pub) &&
|
|
53
|
+
isPrimeOrderPoint(signature.subarray(0, PUBLIC_KEY_SIZE)));
|
|
54
|
+
}
|
|
21
55
|
/** Length of an Ed25519 public key, in bytes. Mirrors rs `PUBLIC_KEY_SIZE` / go `PublicKeySize`. */
|
|
22
56
|
export const PUBLIC_KEY_SIZE = 32;
|
|
23
57
|
/** Length of an Ed25519 signature, in bytes. Mirrors rs `SIGNATURE_SIZE` / go `SignatureSize`. */
|
|
@@ -44,15 +78,15 @@ export function sign(message, seed) {
|
|
|
44
78
|
* @noble's `verify` instead throws on a wrong-length input, so we guard the lengths and
|
|
45
79
|
* wrap the call. (Issue #90: found by fast-check fuzzing.)
|
|
46
80
|
*
|
|
47
|
-
* `{ zip215: false }` selects
|
|
48
|
-
*
|
|
49
|
-
* `
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
81
|
+
* `{ zip215: false }` selects @noble's strict decoding (canonical encodings only, no
|
|
82
|
+
* small-order key), which issue #106 chose in the belief that Go's `crypto/ed25519` and
|
|
83
|
+
* `ed25519-dalek` reject small-order keys. They do not — ADR 0008 measured it — and the
|
|
84
|
+
* flag alone does not settle the accepted set either way, because @noble's equation is
|
|
85
|
+
* cofactored and theirs is not. The profile check above is what settles it; the flag is
|
|
86
|
+
* kept because it is the stricter decoder and costs nothing.
|
|
53
87
|
*/
|
|
54
88
|
export function verify(signature, message, pub) {
|
|
55
|
-
if (
|
|
89
|
+
if (!inProfile(pub, signature))
|
|
56
90
|
return false;
|
|
57
91
|
try {
|
|
58
92
|
return ed25519.verify(signature, message, pub, { zip215: false });
|
|
@@ -77,7 +111,7 @@ export function signInDomain(seed, domain, message) {
|
|
|
77
111
|
* here.
|
|
78
112
|
*/
|
|
79
113
|
export function verifyInDomain(pub, domain, message, signature) {
|
|
80
|
-
if (
|
|
114
|
+
if (!inProfile(pub, signature))
|
|
81
115
|
return false;
|
|
82
116
|
let context;
|
|
83
117
|
try {
|
|
@@ -95,9 +129,14 @@ export function verifyInDomain(pub, domain, message, signature) {
|
|
|
95
129
|
}
|
|
96
130
|
/**
|
|
97
131
|
* A domain is 1..=255 bytes of UTF-8 — the RFC 8032 context bound, with the empty
|
|
98
|
-
* context excluded on purpose (see `signInDomain`).
|
|
132
|
+
* context excluded on purpose (see `signInDomain`). A string that is not well-formed
|
|
133
|
+
* (a lone surrogate) is refused rather than encoded: `TextEncoder` would silently
|
|
134
|
+
* substitute U+FFFD, and a domain that changed on the way into the hash is not the
|
|
135
|
+
* domain the caller named.
|
|
99
136
|
*/
|
|
100
137
|
function domainBytes(domain) {
|
|
138
|
+
if (!isWellFormed(domain))
|
|
139
|
+
throw new Error("domain is not well-formed Unicode");
|
|
101
140
|
const bytes = new TextEncoder().encode(domain);
|
|
102
141
|
if (bytes.length === 0)
|
|
103
142
|
throw new Error("domain is empty");
|
|
@@ -106,4 +145,23 @@ function domainBytes(domain) {
|
|
|
106
145
|
}
|
|
107
146
|
return bytes;
|
|
108
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* `String.prototype.isWellFormed` without raising the compile target to ES2024: every
|
|
150
|
+
* high surrogate is followed by a low one, and no low surrogate stands alone.
|
|
151
|
+
*/
|
|
152
|
+
function isWellFormed(s) {
|
|
153
|
+
for (let i = 0; i < s.length; i++) {
|
|
154
|
+
const c = s.charCodeAt(i);
|
|
155
|
+
if (c >= 0xd800 && c <= 0xdbff) {
|
|
156
|
+
const d = s.charCodeAt(i + 1);
|
|
157
|
+
if (!(d >= 0xdc00 && d <= 0xdfff))
|
|
158
|
+
return false;
|
|
159
|
+
i++;
|
|
160
|
+
}
|
|
161
|
+
else if (c >= 0xdc00 && c <= 0xdfff) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
109
167
|
//# sourceMappingURL=crypto.js.map
|
package/dist/src/crypto.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,EAAE;AACF,kCAAkC;AAClC,EAAE;AACF,wFAAwF;AACxF,uFAAuF;AACvF,yFAAyF;AACzF,kBAAkB;AAClB,mFAAmF;AACnF,qFAAqF;AACrF,kFAAkF;AAClF,
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED