@atlasauth/pca-aggsig 0.3.0 → 0.4.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/README.md +28 -8
- package/dist/bls.d.ts +39 -68
- package/dist/bls.js +122 -144
- package/dist/bls.js.map +1 -1
- package/dist/chain.d.ts +12 -2
- package/dist/chain.js +35 -4
- package/dist/chain.js.map +1 -1
- package/dist/core.d.ts +95 -0
- package/dist/core.js +243 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/witness.d.ts +6 -4
- package/dist/witness.js +11 -7
- package/dist/witness.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Depends on `@atlasauth/pca`, `@noble/curves` and `@noble/hashes` (installed tran
|
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
16
|
import {
|
|
17
|
-
keyGen, blsPublicKey, mintBlsRoot, blsDelegate,
|
|
17
|
+
keyGen, blsPublicKey, popProve, KeyRegistry, registerKey, mintBlsRoot, blsDelegate,
|
|
18
18
|
aggregateChainSignatures, verifyAggregatedChain,
|
|
19
19
|
cosignTreeHead, aggregateWitnessCosignatures, verifyAggregatedWitnessCosignatures,
|
|
20
20
|
} from '@atlasauth/pca-aggsig';
|
|
@@ -31,25 +31,45 @@ const root = mintBlsRoot({
|
|
|
31
31
|
});
|
|
32
32
|
const hop1 = blsDelegate(root, blsPublicKey(bob.secretKey), [{ type: 'max_amount', limit: 100 }], alice.secretKey);
|
|
33
33
|
|
|
34
|
+
// Every signing key must be admitted with a proof of possession before it can count in an aggregate.
|
|
35
|
+
const registry = new KeyRegistry();
|
|
36
|
+
for (const k of [principal, alice]) registry.register(k.publicKey, popProve(k.secretKey)); // { ok: true, key }
|
|
37
|
+
|
|
34
38
|
const chain = [root, hop1];
|
|
35
39
|
const aggSig = aggregateChainSignatures(chain); // one 96-byte signature
|
|
36
|
-
verifyAggregatedChain(chain, aggSig, principalPub);
|
|
40
|
+
verifyAggregatedChain(chain, aggSig, registry, principalPub); // { ok: true }
|
|
37
41
|
|
|
38
42
|
// A witness quorum cosigning one signed tree head.
|
|
39
43
|
const statement = { size: 1024, root: 'cm9vdEA=', prev_root: 'cHJldg==', guardian_epoch: 3 };
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
44
|
+
const witnesses = [keyGen(), keyGen(), keyGen()];
|
|
45
|
+
const quorum = witnesses.map((w) => {
|
|
46
|
+
const r = registerKey(w.publicKey, popProve(w.secretKey));
|
|
47
|
+
if (!r.ok) throw new Error(r.reason);
|
|
48
|
+
return r.key; // a RegisteredKey
|
|
49
|
+
});
|
|
50
|
+
const agg = aggregateWitnessCosignatures(witnesses.map((w) => cosignTreeHead(statement, w.secretKey)));
|
|
51
|
+
verifyAggregatedWitnessCosignatures(statement, quorum, agg); // true
|
|
43
52
|
```
|
|
44
53
|
|
|
45
54
|
`verifyAggregatedChain` also checks structure: depth bound, parent hash-links, holder-to-issuer continuity and append-only caveats. It never throws and returns `{ ok: false, reason }` on failure.
|
|
46
55
|
|
|
47
56
|
## API
|
|
48
57
|
|
|
49
|
-
- Core BLS: `keyGen`, `publicKeyOf`, `keyValidate`, `sign`, `verify`, `aggregate`, `
|
|
50
|
-
-
|
|
58
|
+
- Core BLS: `keyGen`, `publicKeyOf`, `keyValidate`, `sign`, `verify`, `aggregate`, `popProve`.
|
|
59
|
+
- Rogue-key-safe aggregation: `registerKey(publicKey, pop)` -> `{ ok, key: RegisteredKey } | { ok: false, reason }`, `KeyRegistry`, `isRegisteredKey`, and `aggregateVerify` / `fastAggregateVerify`, which accept only `RegisteredKey`s.
|
|
60
|
+
- Capability chain (suite `bls12-381-pop`): `mintBlsRoot`, `blsAttenuate`, `blsDelegate`, `blsPublicKey`, `capHash`, `aggregateChainSignatures`, `verifyAggregatedChain`. `capHash` hashes everything EXCEPT `sig` (per-hop signatures are only checked in aggregate, so they must not feed identity); `verifyAggregatedChain` additionally requires every hop `sig` to be a canonical encoding and the hop sigs to sum to the verified aggregate, so a junk `sig` is refused rather than ignored (red-team RT-13; breaking: parent links computed by earlier versions do not match). Individual hop signatures are not separately verified, only their sum. `fastAggregateVerify` refuses a repeated key (one signer cannot count as N).
|
|
51
61
|
- Witness cosignatures: `cosignTreeHead`, `verifyWitnessCosignature`, `aggregateWitnessCosignatures`, `verifyAggregatedWitnessCosignatures`.
|
|
52
62
|
|
|
63
|
+
## Rogue-key safety is structural
|
|
64
|
+
|
|
65
|
+
Same-message aggregation is forgeable unless every key proved possession of its secret (a rogue key `g^x - sum(honest keys)` makes `sign_x(m)` a valid aggregate for the whole honest set). Here the safe path is the only path:
|
|
66
|
+
|
|
67
|
+
- `RegisteredKey` is an opaque class. The only producers are `registerKey` and `KeyRegistry.register`, which run KeyValidate and verify the key's proof of possession (a PoP-DST signature over the key). The constructor is token-guarded and instances are tracked in a module-private `WeakSet`, so a cast object, a prototype-forged object or a `structuredClone` is refused at runtime.
|
|
68
|
+
- `aggregateVerify` and `fastAggregateVerify` take `RegisteredKey[]`; there is no raw-key overload and no exported `aggregatePublicKeys` or `popVerify`. `verifyAggregatedChain` takes a `KeyRegistry` and refuses a chain whose issuer is not in it; `verifyAggregatedWitnessCosignatures` takes `RegisteredKey[]`.
|
|
69
|
+
- The raw verifiers exist only in the internal `core` module (used by the reference-vector tests), which is not exported from the index and blocked by `package.json` `exports`.
|
|
70
|
+
|
|
71
|
+
Residual: this proves possession at registration time. The application still decides which keys deserve a registry slot (who may register), and a `RegisteredKey` is only as trustworthy as the channel that carried the PoP. Breaking change (unreleased): `aggregatePublicKeys` and `popVerify` removed; `aggregateVerify`, `fastAggregateVerify`, `verifyAggregatedChain` (new `registry` argument before `expectedRootPrincipal`) and `verifyAggregatedWitnessCosignatures` (takes `RegisteredKey[]`) changed signature.
|
|
72
|
+
|
|
53
73
|
## Status
|
|
54
74
|
|
|
55
75
|
Part of [Proof-Carrying Authority](https://github.com/Atlas-Authorization/pca). **Experimental and unaudited.** BLS12-381 is not post-quantum.
|
|
@@ -58,7 +78,7 @@ What is validated (proof-of-possession scheme, public keys in G1, signatures in
|
|
|
58
78
|
|
|
59
79
|
- **Reference vectors**: the 90 community-standard vectors of `ethereum/bls12-381-tests` v0.1.2 for this exact ciphersuite (sign, verify, aggregate, fast-aggregate-verify, aggregate-verify, and G1/G2 point deserialization), vendored in `test-vectors/` with source, release hash and per-file SHA-256. All pass, including infinity-key, infinity-signature, tampered-signature, wrong-key, extra-key and malformed-encoding cases. The IETF draft itself publishes no test vectors (its test-vector appendix is still to be written), so these are the reference vectors for the scheme, not vectors from the draft text.
|
|
60
80
|
- **Independent implementation**: `py_ecc` 8.0.0. Its public keys, proofs of possession, signatures and aggregates are reproduced byte-for-byte, and artifacts produced here verify there (and tampered ones are rejected there).
|
|
61
|
-
- **Rogue-key behavior** is exercised: a key chosen as `a*G - pk_victim` lets an adversary forge a same-message aggregate, which is why `fastAggregateVerify`
|
|
81
|
+
- **Rogue-key behavior** is exercised: a key chosen as `a*G - pk_victim` lets an adversary forge a same-message aggregate, which is why the public `fastAggregateVerify` accepts only keys admitted through `registerKey` (proof of possession); the adversary cannot produce a valid proof of possession for such a key. Proofs of possession and message signatures are domain-separated.
|
|
62
82
|
|
|
63
83
|
What is not validated:
|
|
64
84
|
|
package/dist/bls.d.ts
CHANGED
|
@@ -1,77 +1,48 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* WHY PoP (not message-augmentation / basic): the whole point of this package is to aggregate
|
|
9
|
-
* signatures from DIFFERENT keys — a delegation chain's per-hop keys, and a quorum of transparency
|
|
10
|
-
* witnesses cosigning the SAME statement. Same-message aggregation is exactly where the rogue-key
|
|
11
|
-
* attack lives: an adversary who registers pk_adv = pk_victim^-1 · g^a can forge an aggregate that
|
|
12
|
-
* "verifies" against {pk_victim, pk_adv}. PoP closes this: every key must ship a proof-of-possession
|
|
13
|
-
* (a signature, under a DISTINCT domain, over its own public key) that only the true secret-key
|
|
14
|
-
* holder can produce, so a rogue key chosen as a function of other keys cannot pass {@link popVerify}.
|
|
15
|
-
* The signing and PoP ciphersuites therefore use the `_POP_` domain separation tags below.
|
|
16
|
-
*
|
|
17
|
-
* Every function is total: verification never throws, it returns `false` on any malformed input.
|
|
18
|
-
*/
|
|
19
|
-
/** Ciphersuite ID / hash-to-curve DST for SIGNATURES (PoP variant, signatures in G2). */
|
|
20
|
-
export declare const SIG_DST = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
|
|
21
|
-
/** DISTINCT DST for proofs-of-possession, so a PoP can never be replayed as a message signature. */
|
|
22
|
-
export declare const POP_DST = "BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
|
|
23
|
-
/** Compressed G1 public-key length in bytes. */
|
|
24
|
-
export declare const PUBLIC_KEY_LENGTH = 48;
|
|
25
|
-
/** Compressed G2 signature length in bytes. */
|
|
26
|
-
export declare const SIGNATURE_LENGTH = 96;
|
|
27
|
-
export interface BlsKeyPair {
|
|
28
|
-
/** 32-byte scalar (mod r). */
|
|
29
|
-
secretKey: Uint8Array;
|
|
30
|
-
/** 48-byte compressed G1 point. */
|
|
31
|
-
publicKey: Uint8Array;
|
|
1
|
+
/** A public key whose proof of possession has been verified. Construct only via {@link registerKey}. */
|
|
2
|
+
export declare class RegisteredKey {
|
|
3
|
+
#private;
|
|
4
|
+
/** @internal Use {@link registerKey}; direct construction throws. */
|
|
5
|
+
constructor(token: symbol, bytes: Uint8Array);
|
|
6
|
+
/** A copy of the 48-byte compressed G1 public key. */
|
|
7
|
+
get bytes(): Uint8Array;
|
|
32
8
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
9
|
+
export type RegisterResult = {
|
|
10
|
+
ok: true;
|
|
11
|
+
key: RegisteredKey;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
reason: string;
|
|
15
|
+
};
|
|
37
16
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* to an aggregate. Never throws.
|
|
17
|
+
* Admit a public key: it must pass KeyValidate and carry a valid PoP (a PoP-DST signature by the key over its
|
|
18
|
+
* own bytes). The ONLY way to obtain a {@link RegisteredKey}. Never throws.
|
|
41
19
|
*/
|
|
42
|
-
export declare function
|
|
43
|
-
/**
|
|
44
|
-
export declare function
|
|
45
|
-
/** CoreVerify of a single signature: e(pk, H(msg)) == e(g1, sig). Never throws. */
|
|
46
|
-
export declare function verify(publicKey: Uint8Array, msg: Uint8Array, sig: Uint8Array): boolean;
|
|
20
|
+
export declare function registerKey(publicKey: Uint8Array, pop: Uint8Array): RegisterResult;
|
|
21
|
+
/** True only for instances produced by {@link registerKey}. */
|
|
22
|
+
export declare function isRegisteredKey(x: unknown): x is RegisteredKey;
|
|
47
23
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
24
|
+
* A verifier's pinned set of admitted keys, indexed by key bytes. Use it where keys arrive as bytes/strings
|
|
25
|
+
* (a capability chain's issuers, a witness list) and each must already have been admitted.
|
|
50
26
|
*/
|
|
51
|
-
export declare
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
*/
|
|
63
|
-
export declare function aggregateVerify(publicKeys: Uint8Array[], msgs: Uint8Array[], aggSig: Uint8Array): boolean;
|
|
27
|
+
export declare class KeyRegistry {
|
|
28
|
+
#private;
|
|
29
|
+
/** Verify the PoP and admit the key. Re-registering the same key is idempotent. */
|
|
30
|
+
register(publicKey: Uint8Array, pop: Uint8Array): RegisterResult;
|
|
31
|
+
/** The registered key for these bytes, or `undefined` if it was never admitted. */
|
|
32
|
+
get(publicKey: Uint8Array): RegisteredKey | undefined;
|
|
33
|
+
has(publicKey: Uint8Array): boolean;
|
|
34
|
+
get size(): number;
|
|
35
|
+
}
|
|
36
|
+
/** Why `keys` cannot be used for aggregate verification, or `null` if every element is a genuine {@link RegisteredKey}. */
|
|
37
|
+
export declare function checkRegistered(keys: unknown): string | null;
|
|
64
38
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* quorum of transparency witnesses cosigning one tree head. KeyValidate'd; never throws.
|
|
39
|
+
* AggregateVerify (draft §3.1.1) over distinct messages, restricted to registered keys. Never throws; `false`
|
|
40
|
+
* on any unregistered key, length mismatch, repeated message, or failed pairing check.
|
|
68
41
|
*/
|
|
69
|
-
export declare function
|
|
42
|
+
export declare function aggregateVerify(keys: readonly RegisteredKey[], msgs: Uint8Array[], aggSig: Uint8Array): boolean;
|
|
70
43
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
44
|
+
* FastAggregateVerify (draft §3.3.4): one message, many signers, restricted to registered keys (so the
|
|
45
|
+
* rogue-key attack cannot be run through this API). Never throws.
|
|
73
46
|
*/
|
|
74
|
-
export declare function
|
|
75
|
-
|
|
76
|
-
* `publicKey`. A key must pass this before it is admitted to any aggregate. Never throws. */
|
|
77
|
-
export declare function popVerify(publicKey: Uint8Array, pop: Uint8Array): boolean;
|
|
47
|
+
export declare function fastAggregateVerify(keys: readonly RegisteredKey[], msg: Uint8Array, aggSig: Uint8Array): boolean;
|
|
48
|
+
export { SIG_DST, POP_DST, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH, type BlsKeyPair, keyGen, publicKeyOf, keyValidate, sign, verify, aggregate, popProve, } from './core';
|
package/dist/bls.js
CHANGED
|
@@ -1,176 +1,154 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SIGNATURE_LENGTH = exports.PUBLIC_KEY_LENGTH = exports.POP_DST = exports.SIG_DST = void 0;
|
|
4
|
-
exports.
|
|
5
|
-
exports.
|
|
6
|
-
exports.
|
|
7
|
-
exports.sign = sign;
|
|
8
|
-
exports.verify = verify;
|
|
9
|
-
exports.aggregate = aggregate;
|
|
10
|
-
exports.aggregatePublicKeys = aggregatePublicKeys;
|
|
3
|
+
exports.popProve = exports.aggregate = exports.verify = exports.sign = exports.keyValidate = exports.publicKeyOf = exports.keyGen = exports.SIGNATURE_LENGTH = exports.PUBLIC_KEY_LENGTH = exports.POP_DST = exports.SIG_DST = exports.KeyRegistry = exports.RegisteredKey = void 0;
|
|
4
|
+
exports.registerKey = registerKey;
|
|
5
|
+
exports.isRegisteredKey = isRegisteredKey;
|
|
6
|
+
exports.checkRegistered = checkRegistered;
|
|
11
7
|
exports.aggregateVerify = aggregateVerify;
|
|
12
8
|
exports.fastAggregateVerify = fastAggregateVerify;
|
|
13
|
-
|
|
14
|
-
exports.popVerify = popVerify;
|
|
15
|
-
const bls12_381_1 = require("@noble/curves/bls12-381");
|
|
16
|
-
const utils_1 = require("@noble/hashes/utils");
|
|
9
|
+
const core_1 = require("./core");
|
|
17
10
|
/**
|
|
18
|
-
*
|
|
11
|
+
* ROGUE-KEY-SAFE AGGREGATION, by construction.
|
|
19
12
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
13
|
+
* `fastAggregateVerify` (same message, many keys) is only sound if every key was admitted with a verified
|
|
14
|
+
* proof of possession (PoP): without it, an adversary registers pk_adv = g^x - sum(honest keys) and forges an
|
|
15
|
+
* aggregate that "verifies" for the whole honest set. A caller-discipline rule ("remember to popVerify") is
|
|
16
|
+
* how that bug ships, so here the safe path is the ONLY path:
|
|
23
17
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
18
|
+
* - {@link RegisteredKey} is an opaque class. Its constructor is guarded by a module-private token, and every
|
|
19
|
+
* instance is recorded in a module-private `WeakSet`. A cast object, a prototype-forged object, a
|
|
20
|
+
* `structuredClone` or JSON round trip is not in the set and is refused.
|
|
21
|
+
* - The only producers are {@link registerKey} and {@link KeyRegistry.register}, and both run `popVerify`
|
|
22
|
+
* (KeyValidate + PoP-DST signature by the key over itself) first.
|
|
23
|
+
* - {@link aggregateVerify} and {@link fastAggregateVerify} take `RegisteredKey[]`, never raw bytes. The raw
|
|
24
|
+
* verifiers live in the internal `./core` module, which is not exported from the package index and is
|
|
25
|
+
* blocked by `package.json` `exports`.
|
|
32
26
|
*
|
|
33
|
-
*
|
|
27
|
+
* The aggregation scheme itself is the draft-irtf-cfrg-bls-signature POP ciphersuite
|
|
28
|
+
* (`BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_`), the draft's rogue-key defence; this module adds the
|
|
29
|
+
* enforcement that the draft leaves to the application.
|
|
34
30
|
*/
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
31
|
+
const TOKEN = Symbol('atlas-pca-aggsig/registered-key');
|
|
32
|
+
const REGISTERED = new WeakSet();
|
|
33
|
+
/** A public key whose proof of possession has been verified. Construct only via {@link registerKey}. */
|
|
34
|
+
class RegisteredKey {
|
|
35
|
+
#bytes;
|
|
36
|
+
/** @internal Use {@link registerKey}; direct construction throws. */
|
|
37
|
+
constructor(token, bytes) {
|
|
38
|
+
if (token !== TOKEN)
|
|
39
|
+
throw new TypeError('RegisteredKey cannot be constructed directly; use registerKey(publicKey, pop)');
|
|
40
|
+
this.#bytes = Uint8Array.from(bytes);
|
|
41
|
+
REGISTERED.add(this);
|
|
42
|
+
Object.freeze(this);
|
|
43
|
+
}
|
|
44
|
+
/** A copy of the 48-byte compressed G1 public key. */
|
|
45
|
+
get bytes() {
|
|
46
|
+
return Uint8Array.from(this.#bytes);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.RegisteredKey = RegisteredKey;
|
|
50
|
+
/**
|
|
51
|
+
* Admit a public key: it must pass KeyValidate and carry a valid PoP (a PoP-DST signature by the key over its
|
|
52
|
+
* own bytes). The ONLY way to obtain a {@link RegisteredKey}. Never throws.
|
|
53
|
+
*/
|
|
54
|
+
function registerKey(publicKey, pop) {
|
|
55
|
+
if (!(publicKey instanceof Uint8Array) || !(pop instanceof Uint8Array))
|
|
56
|
+
return { ok: false, reason: 'public key and proof of possession must be Uint8Arrays' };
|
|
57
|
+
if (!(0, core_1.keyValidate)(publicKey))
|
|
58
|
+
return { ok: false, reason: 'public key fails KeyValidate (not a canonical, non-identity, prime-order G1 point)' };
|
|
59
|
+
if (pop.length !== core_1.SIGNATURE_LENGTH)
|
|
60
|
+
return { ok: false, reason: 'proof of possession is not a 96-byte compressed G2 signature' };
|
|
61
|
+
if (!(0, core_1.popVerify)(publicKey, pop))
|
|
62
|
+
return { ok: false, reason: 'proof of possession does not verify for this key' };
|
|
63
|
+
return { ok: true, key: new RegisteredKey(TOKEN, publicKey) };
|
|
49
64
|
}
|
|
50
|
-
/**
|
|
51
|
-
function
|
|
52
|
-
return
|
|
65
|
+
/** True only for instances produced by {@link registerKey}. */
|
|
66
|
+
function isRegisteredKey(x) {
|
|
67
|
+
return typeof x === 'object' && x !== null && REGISTERED.has(x);
|
|
53
68
|
}
|
|
54
69
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* to an aggregate. Never throws.
|
|
70
|
+
* A verifier's pinned set of admitted keys, indexed by key bytes. Use it where keys arrive as bytes/strings
|
|
71
|
+
* (a capability chain's issuers, a witness list) and each must already have been admitted.
|
|
58
72
|
*/
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
class KeyRegistry {
|
|
74
|
+
#keys = new Map();
|
|
75
|
+
/** Verify the PoP and admit the key. Re-registering the same key is idempotent. */
|
|
76
|
+
register(publicKey, pop) {
|
|
77
|
+
const r = registerKey(publicKey, pop);
|
|
78
|
+
if (r.ok)
|
|
79
|
+
this.#keys.set(hex(publicKey), r.key);
|
|
80
|
+
return r;
|
|
66
81
|
}
|
|
67
|
-
|
|
68
|
-
|
|
82
|
+
/** The registered key for these bytes, or `undefined` if it was never admitted. */
|
|
83
|
+
get(publicKey) {
|
|
84
|
+
return publicKey instanceof Uint8Array && publicKey.length === core_1.PUBLIC_KEY_LENGTH ? this.#keys.get(hex(publicKey)) : undefined;
|
|
69
85
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
function sign(secretKey, msg) {
|
|
73
|
-
return bls12_381_1.bls12_381.sign(msg, secretKey, SIG_OPTS);
|
|
74
|
-
}
|
|
75
|
-
/** CoreVerify of a single signature: e(pk, H(msg)) == e(g1, sig). Never throws. */
|
|
76
|
-
function verify(publicKey, msg, sig) {
|
|
77
|
-
try {
|
|
78
|
-
if (!keyValidate(publicKey))
|
|
79
|
-
return false;
|
|
80
|
-
return bls12_381_1.bls12_381.verify(sig, msg, publicKey, SIG_OPTS);
|
|
86
|
+
has(publicKey) {
|
|
87
|
+
return this.get(publicKey) !== undefined;
|
|
81
88
|
}
|
|
82
|
-
|
|
83
|
-
return
|
|
89
|
+
get size() {
|
|
90
|
+
return this.#keys.size;
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
throw new RangeError('aggregate: need at least one signature');
|
|
93
|
-
return bls12_381_1.bls12_381.aggregateSignatures(sigs);
|
|
94
|
-
}
|
|
95
|
-
/** Aggregate a set of G1 public keys into one 48-byte key (used by {@link fastAggregateVerify}). */
|
|
96
|
-
function aggregatePublicKeys(publicKeys) {
|
|
97
|
-
if (publicKeys.length === 0)
|
|
98
|
-
throw new RangeError('aggregatePublicKeys: need at least one key');
|
|
99
|
-
return bls12_381_1.bls12_381.aggregatePublicKeys(publicKeys);
|
|
93
|
+
exports.KeyRegistry = KeyRegistry;
|
|
94
|
+
function hex(b) {
|
|
95
|
+
let s = '';
|
|
96
|
+
for (const x of b)
|
|
97
|
+
s += x.toString(16).padStart(2, '0');
|
|
98
|
+
return s;
|
|
100
99
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
100
|
+
/** Why `keys` cannot be used for aggregate verification, or `null` if every element is a genuine {@link RegisteredKey}. */
|
|
101
|
+
function checkRegistered(keys) {
|
|
102
|
+
if (!Array.isArray(keys))
|
|
103
|
+
return 'keys must be an array of RegisteredKey';
|
|
104
|
+
if (keys.length === 0)
|
|
105
|
+
return 'no keys';
|
|
106
|
+
for (let i = 0; i < keys.length; i++) {
|
|
107
|
+
if (!isRegisteredKey(keys[i]))
|
|
108
|
+
return `key ${i} is not a registered key: it was not admitted through registerKey (proof of possession)`;
|
|
108
109
|
}
|
|
109
|
-
return
|
|
110
|
+
return null;
|
|
110
111
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
* signatures are present in the aggregate — e(g1, aggSig) == Π e(pk_i, H(msg_i)).
|
|
114
|
-
*
|
|
115
|
-
* The draft REQUIRES the messages be pairwise distinct (same-message aggregation across different keys
|
|
116
|
-
* must go through {@link fastAggregateVerify}); we enforce that as defence-in-depth even though PoP
|
|
117
|
-
* already defeats the rogue-key attack. Every public key is KeyValidate'd first. Never throws; returns
|
|
118
|
-
* `false` on length mismatch, a repeated message, a bad key, or a failed pairing check.
|
|
119
|
-
*/
|
|
120
|
-
function aggregateVerify(publicKeys, msgs, aggSig) {
|
|
121
|
-
try {
|
|
122
|
-
if (publicKeys.length === 0 || publicKeys.length !== msgs.length)
|
|
123
|
-
return false;
|
|
124
|
-
if (!hasDistinctMessages(msgs))
|
|
125
|
-
return false;
|
|
126
|
-
for (const pk of publicKeys) {
|
|
127
|
-
if (!keyValidate(pk))
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
return bls12_381_1.bls12_381.verifyBatch(aggSig, msgs, publicKeys, SIG_OPTS);
|
|
131
|
-
}
|
|
132
|
-
catch {
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
112
|
+
function rawOf(keys) {
|
|
113
|
+
return keys.map((k) => k.bytes);
|
|
135
114
|
}
|
|
136
115
|
/**
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* quorum of transparency witnesses cosigning one tree head. KeyValidate'd; never throws.
|
|
116
|
+
* AggregateVerify (draft §3.1.1) over distinct messages, restricted to registered keys. Never throws; `false`
|
|
117
|
+
* on any unregistered key, length mismatch, repeated message, or failed pairing check.
|
|
140
118
|
*/
|
|
141
|
-
function
|
|
142
|
-
|
|
143
|
-
if (publicKeys.length === 0)
|
|
144
|
-
return false;
|
|
145
|
-
for (const pk of publicKeys) {
|
|
146
|
-
if (!keyValidate(pk))
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
149
|
-
const aggPk = bls12_381_1.bls12_381.aggregatePublicKeys(publicKeys);
|
|
150
|
-
return bls12_381_1.bls12_381.verify(aggSig, msg, aggPk, SIG_OPTS);
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
119
|
+
function aggregateVerify(keys, msgs, aggSig) {
|
|
120
|
+
if (checkRegistered(keys) !== null)
|
|
153
121
|
return false;
|
|
154
|
-
|
|
122
|
+
return (0, core_1.aggregateVerify)(rawOf(keys), msgs, aggSig);
|
|
155
123
|
}
|
|
156
124
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
125
|
+
* FastAggregateVerify (draft §3.3.4): one message, many signers, restricted to registered keys (so the
|
|
126
|
+
* rogue-key attack cannot be run through this API). Never throws.
|
|
159
127
|
*/
|
|
160
|
-
function
|
|
161
|
-
|
|
162
|
-
return bls12_381_1.bls12_381.sign(pk, secretKey, POP_OPTS);
|
|
163
|
-
}
|
|
164
|
-
/** PopVerify (draft §3.3.3): the proof-of-possession is a valid PoP-DST signature by `publicKey` over
|
|
165
|
-
* `publicKey`. A key must pass this before it is admitted to any aggregate. Never throws. */
|
|
166
|
-
function popVerify(publicKey, pop) {
|
|
167
|
-
try {
|
|
168
|
-
if (!keyValidate(publicKey))
|
|
169
|
-
return false;
|
|
170
|
-
return bls12_381_1.bls12_381.verify(pop, publicKey, publicKey, POP_OPTS);
|
|
171
|
-
}
|
|
172
|
-
catch {
|
|
128
|
+
function fastAggregateVerify(keys, msg, aggSig) {
|
|
129
|
+
if (checkRegistered(keys) !== null)
|
|
173
130
|
return false;
|
|
131
|
+
// One signer may not be counted twice (RT-13): s+s+s over [k,k,k] would pass as "3 signers".
|
|
132
|
+
const seen = new Set();
|
|
133
|
+
for (const k of keys) {
|
|
134
|
+
const h = hex(k.bytes);
|
|
135
|
+
if (seen.has(h))
|
|
136
|
+
return false;
|
|
137
|
+
seen.add(h);
|
|
174
138
|
}
|
|
139
|
+
return (0, core_1.fastAggregateVerify)(rawOf(keys), msg, aggSig);
|
|
175
140
|
}
|
|
141
|
+
// Re-exports of the primitives that are safe on their own (single-key checks, signature-only aggregation).
|
|
142
|
+
var core_2 = require("./core");
|
|
143
|
+
Object.defineProperty(exports, "SIG_DST", { enumerable: true, get: function () { return core_2.SIG_DST; } });
|
|
144
|
+
Object.defineProperty(exports, "POP_DST", { enumerable: true, get: function () { return core_2.POP_DST; } });
|
|
145
|
+
Object.defineProperty(exports, "PUBLIC_KEY_LENGTH", { enumerable: true, get: function () { return core_2.PUBLIC_KEY_LENGTH; } });
|
|
146
|
+
Object.defineProperty(exports, "SIGNATURE_LENGTH", { enumerable: true, get: function () { return core_2.SIGNATURE_LENGTH; } });
|
|
147
|
+
Object.defineProperty(exports, "keyGen", { enumerable: true, get: function () { return core_2.keyGen; } });
|
|
148
|
+
Object.defineProperty(exports, "publicKeyOf", { enumerable: true, get: function () { return core_2.publicKeyOf; } });
|
|
149
|
+
Object.defineProperty(exports, "keyValidate", { enumerable: true, get: function () { return core_2.keyValidate; } });
|
|
150
|
+
Object.defineProperty(exports, "sign", { enumerable: true, get: function () { return core_2.sign; } });
|
|
151
|
+
Object.defineProperty(exports, "verify", { enumerable: true, get: function () { return core_2.verify; } });
|
|
152
|
+
Object.defineProperty(exports, "aggregate", { enumerable: true, get: function () { return core_2.aggregate; } });
|
|
153
|
+
Object.defineProperty(exports, "popProve", { enumerable: true, get: function () { return core_2.popProve; } });
|
|
176
154
|
//# sourceMappingURL=bls.js.map
|
package/dist/bls.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bls.js","sourceRoot":"","sources":["../src/bls.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"bls.js","sourceRoot":"","sources":["../src/bls.ts"],"names":[],"mappings":"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"}
|
package/dist/chain.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Caveat, type ChainResult } from '@atlasauth/pca';
|
|
2
|
+
import { type KeyRegistry } from './bls';
|
|
2
3
|
/** The signature suite tag carried on every BLS hop. */
|
|
3
4
|
export declare const BLS_SUITE: "bls12-381-pop";
|
|
4
5
|
export interface BlsCapability {
|
|
@@ -19,7 +20,12 @@ export interface BlsCapability {
|
|
|
19
20
|
export type BlsCapabilityChain = BlsCapability[];
|
|
20
21
|
/** Longest delegation chain accepted (bounds verification cost; mirrors the core chain's limit). */
|
|
21
22
|
export declare const MAX_CHAIN_DEPTH = 16;
|
|
22
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Identity of a capability: the hash of everything EXCEPT `sig` (red-team RT-13). Per-hop signatures are
|
|
25
|
+
* only checked in aggregate, so a value that no individual check pins must not influence the identity
|
|
26
|
+
* (the parent link, dedup, revocation-by-hash): otherwise a free `sig` string mints unlimited distinct
|
|
27
|
+
* "identities" for one verified hop. The signed content already fixes issuer, holder, caveats, parent.
|
|
28
|
+
*/
|
|
23
29
|
export declare function capHash(c: BlsCapability): string;
|
|
24
30
|
/** b64u BLS G1 public key for a secret scalar — the identity a chain is rooted at / delegated to. */
|
|
25
31
|
export declare function blsPublicKey(secretKey: Uint8Array): string;
|
|
@@ -49,5 +55,9 @@ export declare function aggregateChainSignatures(chain: BlsCapabilityChain): Uin
|
|
|
49
55
|
* no parent, optional expected root principal, parent hash-links, holder→issuer continuity, and
|
|
50
56
|
* append-only caveat prefixes — then verifies all M hop signatures at once with a single
|
|
51
57
|
* distinct-message AggregateVerify over each hop's (issuer key, signed body) pair. Never throws.
|
|
58
|
+
*
|
|
59
|
+
* `registry` is the verifier's pinned set of keys admitted with a verified proof of possession. EVERY hop issuer
|
|
60
|
+
* must be in it: a chain signed by a key that never proved possession is refused, so a rogue-key chain cannot
|
|
61
|
+
* be presented. (Only issuers sign; the final holder need not be registered.)
|
|
52
62
|
*/
|
|
53
|
-
export declare function verifyAggregatedChain(chain: BlsCapabilityChain, aggSig: Uint8Array, expectedRootPrincipal?: string): ChainResult;
|
|
63
|
+
export declare function verifyAggregatedChain(chain: BlsCapabilityChain, aggSig: Uint8Array, registry: KeyRegistry, expectedRootPrincipal?: string): ChainResult;
|
package/dist/chain.js
CHANGED
|
@@ -43,9 +43,16 @@ function sigMessage(bodyDigest) {
|
|
|
43
43
|
m.set(d, p.length);
|
|
44
44
|
return m;
|
|
45
45
|
}
|
|
46
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Identity of a capability: the hash of everything EXCEPT `sig` (red-team RT-13). Per-hop signatures are
|
|
48
|
+
* only checked in aggregate, so a value that no individual check pins must not influence the identity
|
|
49
|
+
* (the parent link, dedup, revocation-by-hash): otherwise a free `sig` string mints unlimited distinct
|
|
50
|
+
* "identities" for one verified hop. The signed content already fixes issuer, holder, caveats, parent.
|
|
51
|
+
*/
|
|
47
52
|
function capHash(c) {
|
|
48
|
-
|
|
53
|
+
const { sig: _sig, ...rest } = c;
|
|
54
|
+
void _sig;
|
|
55
|
+
return (0, pca_1.hashCanonical)(rest);
|
|
49
56
|
}
|
|
50
57
|
function cloneCaveats(cs) {
|
|
51
58
|
const parsed = JSON.parse(new TextDecoder().decode((0, pca_1.canonicalBytes)(cs)));
|
|
@@ -119,8 +126,14 @@ function aggregateChainSignatures(chain) {
|
|
|
119
126
|
* no parent, optional expected root principal, parent hash-links, holder→issuer continuity, and
|
|
120
127
|
* append-only caveat prefixes — then verifies all M hop signatures at once with a single
|
|
121
128
|
* distinct-message AggregateVerify over each hop's (issuer key, signed body) pair. Never throws.
|
|
129
|
+
*
|
|
130
|
+
* `registry` is the verifier's pinned set of keys admitted with a verified proof of possession. EVERY hop issuer
|
|
131
|
+
* must be in it: a chain signed by a key that never proved possession is refused, so a rogue-key chain cannot
|
|
132
|
+
* be presented. (Only issuers sign; the final holder need not be registered.)
|
|
122
133
|
*/
|
|
123
|
-
function verifyAggregatedChain(chain, aggSig, expectedRootPrincipal) {
|
|
134
|
+
function verifyAggregatedChain(chain, aggSig, registry, expectedRootPrincipal) {
|
|
135
|
+
if (registry === null || typeof registry !== 'object' || typeof registry.get !== 'function')
|
|
136
|
+
return { ok: false, reason: 'a KeyRegistry of proof-of-possession-verified keys is required' };
|
|
124
137
|
if (!Array.isArray(chain) || chain.length === 0)
|
|
125
138
|
return { ok: false, reason: 'empty chain' };
|
|
126
139
|
if (chain.length > exports.MAX_CHAIN_DEPTH)
|
|
@@ -154,11 +167,13 @@ function verifyAggregatedChain(chain, aggSig, expectedRootPrincipal) {
|
|
|
154
167
|
return { ok: false, reason: `hop ${i}: body digest mismatch` };
|
|
155
168
|
let pk;
|
|
156
169
|
try {
|
|
157
|
-
pk = (0, pca_1.unb64u)(c.issuer);
|
|
170
|
+
pk = registry.get((0, pca_1.unb64u)(c.issuer));
|
|
158
171
|
}
|
|
159
172
|
catch {
|
|
160
173
|
return { ok: false, reason: `hop ${i}: malformed issuer key` };
|
|
161
174
|
}
|
|
175
|
+
if (pk === undefined)
|
|
176
|
+
return { ok: false, reason: `hop ${i}: issuer key has no verified proof of possession (not in the registry)` };
|
|
162
177
|
if (i > 0) {
|
|
163
178
|
const parent = chain[i - 1];
|
|
164
179
|
if (parent === undefined)
|
|
@@ -178,6 +193,22 @@ function verifyAggregatedChain(chain, aggSig, expectedRootPrincipal) {
|
|
|
178
193
|
pks.push(pk);
|
|
179
194
|
msgs.push(sigMessage(c.body_digest));
|
|
180
195
|
}
|
|
196
|
+
// Bind the per-hop `sig` fields: each must be a canonical encoding and together they must sum to the
|
|
197
|
+
// aggregate that was verified. A free-form or non-canonical `sig` is refused instead of ignored.
|
|
198
|
+
try {
|
|
199
|
+
for (let i = 0; i < chain.length; i++) {
|
|
200
|
+
const raw = (0, pca_1.unb64u)(chain[i].sig);
|
|
201
|
+
if ((0, pca_1.b64u)(raw) !== chain[i].sig)
|
|
202
|
+
return { ok: false, reason: `hop ${i}: non-canonical sig encoding` };
|
|
203
|
+
}
|
|
204
|
+
const sum = (0, bls_1.aggregate)(chain.map((c) => (0, pca_1.unb64u)(c.sig)));
|
|
205
|
+
if (sum.length !== aggSig.length || sum.some((b, k) => b !== aggSig[k])) {
|
|
206
|
+
return { ok: false, reason: 'hop sig fields do not sum to the aggregate signature' };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return { ok: false, reason: 'malformed hop sig' };
|
|
211
|
+
}
|
|
181
212
|
if (!(0, bls_1.aggregateVerify)(pks, msgs, aggSig)) {
|
|
182
213
|
return { ok: false, reason: 'aggregate signature does not verify over all hops' };
|
|
183
214
|
}
|
package/dist/chain.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chain.js","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"chain.js","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"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"}
|
package/dist/core.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BLS signatures on BLS12-381, aggregation-ready.
|
|
3
|
+
*
|
|
4
|
+
* SCHEME — Proof-of-Possession (PoP), per draft-irtf-cfrg-bls-signature-05 §3.3.
|
|
5
|
+
* Variant: "minimal-pubkey-size" — public keys live in G1 (48 bytes compressed), signatures in
|
|
6
|
+
* G2 (96 bytes compressed). This is the eth2 / Ethereum-consensus convention.
|
|
7
|
+
*
|
|
8
|
+
* WHY PoP (not message-augmentation / basic): the whole point of this package is to aggregate
|
|
9
|
+
* signatures from DIFFERENT keys — a delegation chain's per-hop keys, and a quorum of transparency
|
|
10
|
+
* witnesses cosigning the SAME statement. Same-message aggregation is exactly where the rogue-key
|
|
11
|
+
* attack lives: an adversary who registers pk_adv = pk_victim^-1 · g^a can forge an aggregate that
|
|
12
|
+
* "verifies" against {pk_victim, pk_adv}. PoP closes this: every key must ship a proof-of-possession
|
|
13
|
+
* (a signature, under a DISTINCT domain, over its own public key) that only the true secret-key
|
|
14
|
+
* holder can produce, so a rogue key chosen as a function of other keys cannot pass {@link popVerify}.
|
|
15
|
+
* The signing and PoP ciphersuites therefore use the `_POP_` domain separation tags below.
|
|
16
|
+
*
|
|
17
|
+
* Every function is total: verification never throws, it returns `false` on any malformed input.
|
|
18
|
+
*
|
|
19
|
+
* THIS MODULE IS INTERNAL. Its `aggregateVerify` / `fastAggregateVerify` / `aggregatePublicKeys` take RAW public
|
|
20
|
+
* keys and are only sound if the caller has already checked a proof of possession for every key. They are not
|
|
21
|
+
* exported from the package index (and `package.json` `exports` blocks deep imports); the public versions in
|
|
22
|
+
* `./bls` accept only {@link RegisteredKey}s, which can only be obtained from a verified PoP.
|
|
23
|
+
*/
|
|
24
|
+
/** Ciphersuite ID / hash-to-curve DST for SIGNATURES (PoP variant, signatures in G2). */
|
|
25
|
+
export declare const SIG_DST = "BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
|
|
26
|
+
/** DISTINCT DST for proofs-of-possession, so a PoP can never be replayed as a message signature. */
|
|
27
|
+
export declare const POP_DST = "BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
|
|
28
|
+
/** Compressed G1 public-key length in bytes. */
|
|
29
|
+
export declare const PUBLIC_KEY_LENGTH = 48;
|
|
30
|
+
/** Compressed G2 signature length in bytes. */
|
|
31
|
+
export declare const SIGNATURE_LENGTH = 96;
|
|
32
|
+
/**
|
|
33
|
+
* Strict ZCash/IETF compressed-encoding gate. The pinned `@noble/curves` decodes a 96-byte G2 value without
|
|
34
|
+
* insisting on the compression flag, so clearing the top bit of a valid signature still verified: every
|
|
35
|
+
* signature then had two accepted encodings (malleable bytes, and different `capHash` values for one hop).
|
|
36
|
+
* Reject anything that is not a 96-byte (G2) / 48-byte (G1) value with the compression bit set.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isCompressed(bytes: unknown, len: number): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Canonical signature encoding: a 96-byte compressed G2 point that is EXACTLY the re-encoding of the point it decodes to.
|
|
41
|
+
* The pinned `@noble/curves` decodes without reducing coordinates, so `c0 + p` parses to the same point: every signature
|
|
42
|
+
* would have a second accepted byte string (malleable, and a different `capHash` for one hop). Never throws.
|
|
43
|
+
*/
|
|
44
|
+
export declare function isCanonicalSignature(sig: unknown): boolean;
|
|
45
|
+
export interface BlsKeyPair {
|
|
46
|
+
/** 32-byte scalar (mod r). */
|
|
47
|
+
secretKey: Uint8Array;
|
|
48
|
+
/** 48-byte compressed G1 point. */
|
|
49
|
+
publicKey: Uint8Array;
|
|
50
|
+
}
|
|
51
|
+
/** Fresh key pair: a random scalar mod r and its G1 public key. */
|
|
52
|
+
export declare function keyGen(): BlsKeyPair;
|
|
53
|
+
/** Public key (compressed G1) for a secret scalar. */
|
|
54
|
+
export declare function publicKeyOf(secretKey: Uint8Array): Uint8Array;
|
|
55
|
+
/**
|
|
56
|
+
* KeyValidate (draft §2.5): the public key decodes to a valid point, is NOT the identity, and lies
|
|
57
|
+
* in the prime-order subgroup. A key that fails this is rejected everywhere before it can contribute
|
|
58
|
+
* to an aggregate. Never throws.
|
|
59
|
+
*/
|
|
60
|
+
export declare function keyValidate(publicKey: Uint8Array): boolean;
|
|
61
|
+
/** CoreSign: H(msg) in G2 under the signature DST, multiplied by the secret scalar. */
|
|
62
|
+
export declare function sign(secretKey: Uint8Array, msg: Uint8Array): Uint8Array;
|
|
63
|
+
/** CoreVerify of a single signature: e(pk, H(msg)) == e(g1, sig). Never throws. */
|
|
64
|
+
export declare function verify(publicKey: Uint8Array, msg: Uint8Array, sig: Uint8Array): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Aggregate: sum the G2 signature points into one 96-byte aggregate. Order-independent. Requires at
|
|
67
|
+
* least one signature.
|
|
68
|
+
*/
|
|
69
|
+
export declare function aggregate(sigs: Uint8Array[]): Uint8Array;
|
|
70
|
+
/** Aggregate a set of G1 public keys into one 48-byte key (used by {@link fastAggregateVerify}). */
|
|
71
|
+
export declare function aggregatePublicKeys(publicKeys: Uint8Array[]): Uint8Array;
|
|
72
|
+
/**
|
|
73
|
+
* AggregateVerify (draft §3.1.1) for DISTINCT messages: one pairing-product check that all N hops'
|
|
74
|
+
* signatures are present in the aggregate — e(g1, aggSig) == Π e(pk_i, H(msg_i)).
|
|
75
|
+
*
|
|
76
|
+
* The draft REQUIRES the messages be pairwise distinct (same-message aggregation across different keys
|
|
77
|
+
* must go through {@link fastAggregateVerify}); we enforce that as defence-in-depth even though PoP
|
|
78
|
+
* already defeats the rogue-key attack. Every public key is KeyValidate'd first. Never throws; returns
|
|
79
|
+
* `false` on length mismatch, a repeated message, a bad key, or a failed pairing check.
|
|
80
|
+
*/
|
|
81
|
+
export declare function aggregateVerify(publicKeys: Uint8Array[], msgs: Uint8Array[], aggSig: Uint8Array): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* FastAggregateVerify (draft §3.3.4): all signers signed the SAME message. Aggregate the public keys
|
|
84
|
+
* into one, then a single CoreVerify — e(g1, aggSig) == e(Σ pk_i, H(msg)). This is the surface for a
|
|
85
|
+
* quorum of transparency witnesses cosigning one tree head. KeyValidate'd; never throws.
|
|
86
|
+
*/
|
|
87
|
+
export declare function fastAggregateVerify(publicKeys: Uint8Array[], msg: Uint8Array, aggSig: Uint8Array): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* PopProve (draft §3.3.2): sign one's OWN public key under the PoP DST. Only the holder of the secret
|
|
90
|
+
* scalar can produce this, so a rogue key defined as a function of other keys cannot.
|
|
91
|
+
*/
|
|
92
|
+
export declare function popProve(secretKey: Uint8Array): Uint8Array;
|
|
93
|
+
/** PopVerify (draft §3.3.3): the proof-of-possession is a valid PoP-DST signature by `publicKey` over
|
|
94
|
+
* `publicKey`. A key must pass this before it is admitted to any aggregate. Never throws. */
|
|
95
|
+
export declare function popVerify(publicKey: Uint8Array, pop: Uint8Array): boolean;
|
package/dist/core.js
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SIGNATURE_LENGTH = exports.PUBLIC_KEY_LENGTH = exports.POP_DST = exports.SIG_DST = void 0;
|
|
4
|
+
exports.isCompressed = isCompressed;
|
|
5
|
+
exports.isCanonicalSignature = isCanonicalSignature;
|
|
6
|
+
exports.keyGen = keyGen;
|
|
7
|
+
exports.publicKeyOf = publicKeyOf;
|
|
8
|
+
exports.keyValidate = keyValidate;
|
|
9
|
+
exports.sign = sign;
|
|
10
|
+
exports.verify = verify;
|
|
11
|
+
exports.aggregate = aggregate;
|
|
12
|
+
exports.aggregatePublicKeys = aggregatePublicKeys;
|
|
13
|
+
exports.aggregateVerify = aggregateVerify;
|
|
14
|
+
exports.fastAggregateVerify = fastAggregateVerify;
|
|
15
|
+
exports.popProve = popProve;
|
|
16
|
+
exports.popVerify = popVerify;
|
|
17
|
+
const bls12_381_1 = require("@noble/curves/bls12-381");
|
|
18
|
+
const utils_1 = require("@noble/hashes/utils");
|
|
19
|
+
/**
|
|
20
|
+
* BLS signatures on BLS12-381, aggregation-ready.
|
|
21
|
+
*
|
|
22
|
+
* SCHEME — Proof-of-Possession (PoP), per draft-irtf-cfrg-bls-signature-05 §3.3.
|
|
23
|
+
* Variant: "minimal-pubkey-size" — public keys live in G1 (48 bytes compressed), signatures in
|
|
24
|
+
* G2 (96 bytes compressed). This is the eth2 / Ethereum-consensus convention.
|
|
25
|
+
*
|
|
26
|
+
* WHY PoP (not message-augmentation / basic): the whole point of this package is to aggregate
|
|
27
|
+
* signatures from DIFFERENT keys — a delegation chain's per-hop keys, and a quorum of transparency
|
|
28
|
+
* witnesses cosigning the SAME statement. Same-message aggregation is exactly where the rogue-key
|
|
29
|
+
* attack lives: an adversary who registers pk_adv = pk_victim^-1 · g^a can forge an aggregate that
|
|
30
|
+
* "verifies" against {pk_victim, pk_adv}. PoP closes this: every key must ship a proof-of-possession
|
|
31
|
+
* (a signature, under a DISTINCT domain, over its own public key) that only the true secret-key
|
|
32
|
+
* holder can produce, so a rogue key chosen as a function of other keys cannot pass {@link popVerify}.
|
|
33
|
+
* The signing and PoP ciphersuites therefore use the `_POP_` domain separation tags below.
|
|
34
|
+
*
|
|
35
|
+
* Every function is total: verification never throws, it returns `false` on any malformed input.
|
|
36
|
+
*
|
|
37
|
+
* THIS MODULE IS INTERNAL. Its `aggregateVerify` / `fastAggregateVerify` / `aggregatePublicKeys` take RAW public
|
|
38
|
+
* keys and are only sound if the caller has already checked a proof of possession for every key. They are not
|
|
39
|
+
* exported from the package index (and `package.json` `exports` blocks deep imports); the public versions in
|
|
40
|
+
* `./bls` accept only {@link RegisteredKey}s, which can only be obtained from a verified PoP.
|
|
41
|
+
*/
|
|
42
|
+
/** Ciphersuite ID / hash-to-curve DST for SIGNATURES (PoP variant, signatures in G2). */
|
|
43
|
+
exports.SIG_DST = 'BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_';
|
|
44
|
+
/** DISTINCT DST for proofs-of-possession, so a PoP can never be replayed as a message signature. */
|
|
45
|
+
exports.POP_DST = 'BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_';
|
|
46
|
+
/** Compressed G1 public-key length in bytes. */
|
|
47
|
+
exports.PUBLIC_KEY_LENGTH = 48;
|
|
48
|
+
/** Compressed G2 signature length in bytes. */
|
|
49
|
+
exports.SIGNATURE_LENGTH = 96;
|
|
50
|
+
/**
|
|
51
|
+
* Strict ZCash/IETF compressed-encoding gate. The pinned `@noble/curves` decodes a 96-byte G2 value without
|
|
52
|
+
* insisting on the compression flag, so clearing the top bit of a valid signature still verified: every
|
|
53
|
+
* signature then had two accepted encodings (malleable bytes, and different `capHash` values for one hop).
|
|
54
|
+
* Reject anything that is not a 96-byte (G2) / 48-byte (G1) value with the compression bit set.
|
|
55
|
+
*/
|
|
56
|
+
function isCompressed(bytes, len) {
|
|
57
|
+
return bytes instanceof Uint8Array && bytes.length === len && ((bytes[0] ?? 0) & 0x80) !== 0;
|
|
58
|
+
}
|
|
59
|
+
function sameBytes(a, b) {
|
|
60
|
+
if (a.length !== b.length)
|
|
61
|
+
return false;
|
|
62
|
+
let d = 0;
|
|
63
|
+
for (let i = 0; i < a.length; i++)
|
|
64
|
+
d |= a[i] ^ b[i];
|
|
65
|
+
return d === 0;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Canonical signature encoding: a 96-byte compressed G2 point that is EXACTLY the re-encoding of the point it decodes to.
|
|
69
|
+
* The pinned `@noble/curves` decodes without reducing coordinates, so `c0 + p` parses to the same point: every signature
|
|
70
|
+
* would have a second accepted byte string (malleable, and a different `capHash` for one hop). Never throws.
|
|
71
|
+
*/
|
|
72
|
+
function isCanonicalSignature(sig) {
|
|
73
|
+
if (!isCompressed(sig, exports.SIGNATURE_LENGTH))
|
|
74
|
+
return false;
|
|
75
|
+
try {
|
|
76
|
+
return sameBytes(bls12_381_1.bls12_381.G2.ProjectivePoint.fromHex(sig).toRawBytes(true), sig);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const SIG_OPTS = { DST: exports.SIG_DST };
|
|
83
|
+
const POP_OPTS = { DST: exports.POP_DST };
|
|
84
|
+
/** Fresh key pair: a random scalar mod r and its G1 public key. */
|
|
85
|
+
function keyGen() {
|
|
86
|
+
const secretKey = bls12_381_1.bls12_381.utils.randomPrivateKey();
|
|
87
|
+
const publicKey = bls12_381_1.bls12_381.getPublicKey(secretKey);
|
|
88
|
+
// A stuck-at-zero entropy source maps to the scalar 1 (public key = the group generator): a publicly
|
|
89
|
+
// known "secret". Probability of this under a healthy RNG is ~2^-255.
|
|
90
|
+
if ((0, utils_1.bytesToHex)(publicKey) === (0, utils_1.bytesToHex)(bls12_381_1.bls12_381.getPublicKey(Uint8Array.from([...new Array(31).fill(0), 1])))) {
|
|
91
|
+
throw new Error('keyGen: entropy source produced the degenerate key 1 (RNG failure); refusing to continue');
|
|
92
|
+
}
|
|
93
|
+
return { secretKey, publicKey };
|
|
94
|
+
}
|
|
95
|
+
/** Public key (compressed G1) for a secret scalar. */
|
|
96
|
+
function publicKeyOf(secretKey) {
|
|
97
|
+
return bls12_381_1.bls12_381.getPublicKey(secretKey);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* KeyValidate (draft §2.5): the public key decodes to a valid point, is NOT the identity, and lies
|
|
101
|
+
* in the prime-order subgroup. A key that fails this is rejected everywhere before it can contribute
|
|
102
|
+
* to an aggregate. Never throws.
|
|
103
|
+
*/
|
|
104
|
+
function keyValidate(publicKey) {
|
|
105
|
+
if (!isCompressed(publicKey, exports.PUBLIC_KEY_LENGTH))
|
|
106
|
+
return false;
|
|
107
|
+
try {
|
|
108
|
+
const P = bls12_381_1.bls12_381.G1.ProjectivePoint.fromHex(publicKey);
|
|
109
|
+
if (P.equals(bls12_381_1.bls12_381.G1.ProjectivePoint.ZERO))
|
|
110
|
+
return false;
|
|
111
|
+
P.assertValidity();
|
|
112
|
+
// noble does not reduce coordinates: x + p decodes to the same point. A second byte string for one signer would
|
|
113
|
+
// defeat byte-keyed distinct-signer counting, so only the canonical encoding is a key.
|
|
114
|
+
if (!sameBytes(P.toRawBytes(true), publicKey))
|
|
115
|
+
return false;
|
|
116
|
+
return P.isTorsionFree();
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** CoreSign: H(msg) in G2 under the signature DST, multiplied by the secret scalar. */
|
|
123
|
+
function sign(secretKey, msg) {
|
|
124
|
+
return bls12_381_1.bls12_381.sign(msg, secretKey, SIG_OPTS);
|
|
125
|
+
}
|
|
126
|
+
/** CoreVerify of a single signature: e(pk, H(msg)) == e(g1, sig). Never throws. */
|
|
127
|
+
function verify(publicKey, msg, sig) {
|
|
128
|
+
try {
|
|
129
|
+
if (!isCanonicalSignature(sig))
|
|
130
|
+
return false;
|
|
131
|
+
if (!keyValidate(publicKey))
|
|
132
|
+
return false;
|
|
133
|
+
return bls12_381_1.bls12_381.verify(sig, msg, publicKey, SIG_OPTS);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Aggregate: sum the G2 signature points into one 96-byte aggregate. Order-independent. Requires at
|
|
141
|
+
* least one signature.
|
|
142
|
+
*/
|
|
143
|
+
function aggregate(sigs) {
|
|
144
|
+
if (sigs.length === 0)
|
|
145
|
+
throw new RangeError('aggregate: need at least one signature');
|
|
146
|
+
for (const sg of sigs)
|
|
147
|
+
if (!isCanonicalSignature(sg))
|
|
148
|
+
throw new RangeError('aggregate: every signature must be a 96-byte compressed G2 point');
|
|
149
|
+
return bls12_381_1.bls12_381.aggregateSignatures(sigs);
|
|
150
|
+
}
|
|
151
|
+
/** Aggregate a set of G1 public keys into one 48-byte key (used by {@link fastAggregateVerify}). */
|
|
152
|
+
function aggregatePublicKeys(publicKeys) {
|
|
153
|
+
if (publicKeys.length === 0)
|
|
154
|
+
throw new RangeError('aggregatePublicKeys: need at least one key');
|
|
155
|
+
return bls12_381_1.bls12_381.aggregatePublicKeys(publicKeys);
|
|
156
|
+
}
|
|
157
|
+
function hasDistinctMessages(msgs) {
|
|
158
|
+
const seen = new Set();
|
|
159
|
+
for (const m of msgs) {
|
|
160
|
+
const h = (0, utils_1.bytesToHex)(m);
|
|
161
|
+
if (seen.has(h))
|
|
162
|
+
return false;
|
|
163
|
+
seen.add(h);
|
|
164
|
+
}
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* AggregateVerify (draft §3.1.1) for DISTINCT messages: one pairing-product check that all N hops'
|
|
169
|
+
* signatures are present in the aggregate — e(g1, aggSig) == Π e(pk_i, H(msg_i)).
|
|
170
|
+
*
|
|
171
|
+
* The draft REQUIRES the messages be pairwise distinct (same-message aggregation across different keys
|
|
172
|
+
* must go through {@link fastAggregateVerify}); we enforce that as defence-in-depth even though PoP
|
|
173
|
+
* already defeats the rogue-key attack. Every public key is KeyValidate'd first. Never throws; returns
|
|
174
|
+
* `false` on length mismatch, a repeated message, a bad key, or a failed pairing check.
|
|
175
|
+
*/
|
|
176
|
+
function aggregateVerify(publicKeys, msgs, aggSig) {
|
|
177
|
+
try {
|
|
178
|
+
if (!isCanonicalSignature(aggSig))
|
|
179
|
+
return false;
|
|
180
|
+
if (publicKeys.length === 0 || publicKeys.length !== msgs.length)
|
|
181
|
+
return false;
|
|
182
|
+
if (!hasDistinctMessages(msgs))
|
|
183
|
+
return false;
|
|
184
|
+
for (const pk of publicKeys) {
|
|
185
|
+
if (!keyValidate(pk))
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
return bls12_381_1.bls12_381.verifyBatch(aggSig, msgs, publicKeys, SIG_OPTS);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* FastAggregateVerify (draft §3.3.4): all signers signed the SAME message. Aggregate the public keys
|
|
196
|
+
* into one, then a single CoreVerify — e(g1, aggSig) == e(Σ pk_i, H(msg)). This is the surface for a
|
|
197
|
+
* quorum of transparency witnesses cosigning one tree head. KeyValidate'd; never throws.
|
|
198
|
+
*/
|
|
199
|
+
function fastAggregateVerify(publicKeys, msg, aggSig) {
|
|
200
|
+
try {
|
|
201
|
+
if (!isCanonicalSignature(aggSig))
|
|
202
|
+
return false;
|
|
203
|
+
if (publicKeys.length === 0)
|
|
204
|
+
return false;
|
|
205
|
+
for (const pk of publicKeys) {
|
|
206
|
+
if (!keyValidate(pk))
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
const aggPk = bls12_381_1.bls12_381.aggregatePublicKeys(publicKeys);
|
|
210
|
+
// KeyValidate the AGGREGATE too (draft §3.3.4 -> CoreVerify): keys {P, -P} each pass KeyValidate but
|
|
211
|
+
// sum to the identity, for which the identity "signature" would verify over ANY message. (The pinned
|
|
212
|
+
// noble-curves currently throws on that pairing; this makes the rejection explicit and version-proof.)
|
|
213
|
+
if (!keyValidate(aggPk))
|
|
214
|
+
return false;
|
|
215
|
+
return bls12_381_1.bls12_381.verify(aggSig, msg, aggPk, SIG_OPTS);
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* PopProve (draft §3.3.2): sign one's OWN public key under the PoP DST. Only the holder of the secret
|
|
223
|
+
* scalar can produce this, so a rogue key defined as a function of other keys cannot.
|
|
224
|
+
*/
|
|
225
|
+
function popProve(secretKey) {
|
|
226
|
+
const pk = bls12_381_1.bls12_381.getPublicKey(secretKey);
|
|
227
|
+
return bls12_381_1.bls12_381.sign(pk, secretKey, POP_OPTS);
|
|
228
|
+
}
|
|
229
|
+
/** PopVerify (draft §3.3.3): the proof-of-possession is a valid PoP-DST signature by `publicKey` over
|
|
230
|
+
* `publicKey`. A key must pass this before it is admitted to any aggregate. Never throws. */
|
|
231
|
+
function popVerify(publicKey, pop) {
|
|
232
|
+
try {
|
|
233
|
+
if (!isCanonicalSignature(pop))
|
|
234
|
+
return false;
|
|
235
|
+
if (!keyValidate(publicKey))
|
|
236
|
+
return false;
|
|
237
|
+
return bls12_381_1.bls12_381.verify(pop, publicKey, publicKey, POP_OPTS);
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":";;;AA2CA,oCAEC;AAcD,oDAOC;AAaD,wBASC;AAGD,kCAEC;AAOD,kCAaC;AAGD,oBAEC;AAGD,wBAQC;AAMD,8BAIC;AAGD,kDAGC;AAqBD,0CAYC;AAOD,kDAgBC;AAMD,4BAGC;AAID,8BAQC;AA9ND,uDAAoD;AACpD,+CAAiD;AAEjD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,yFAAyF;AAC5E,QAAA,OAAO,GAAG,6CAA6C,CAAC;AACrE,oGAAoG;AACvF,QAAA,OAAO,GAAG,6CAA6C,CAAC;AAErE,gDAAgD;AACnC,QAAA,iBAAiB,GAAG,EAAE,CAAC;AACpC,+CAA+C;AAClC,QAAA,gBAAgB,GAAG,EAAE,CAAC;AAEnC;;;;;GAKG;AACH,SAAgB,YAAY,CAAC,KAAc,EAAE,GAAW;IACtD,OAAO,KAAK,YAAY,UAAU,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;AAC/F,CAAC;AAED,SAAS,SAAS,CAAC,CAAa,EAAE,CAAa;IAC7C,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACtD,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,SAAgB,oBAAoB,CAAC,GAAY;IAC/C,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,wBAAgB,CAAC;QAAE,OAAO,KAAK,CAAC;IACvD,IAAI,CAAC;QACH,OAAO,SAAS,CAAC,qBAAS,CAAC,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,GAAiB,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,GAAiB,CAAC,CAAC;IAChH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,QAAQ,GAAG,EAAE,GAAG,EAAE,eAAO,EAAW,CAAC;AAC3C,MAAM,QAAQ,GAAG,EAAE,GAAG,EAAE,eAAO,EAAW,CAAC;AAS3C,mEAAmE;AACnE,SAAgB,MAAM;IACpB,MAAM,SAAS,GAAG,qBAAS,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC;IACrD,MAAM,SAAS,GAAG,qBAAS,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;IACpD,qGAAqG;IACrG,sEAAsE;IACtE,IAAI,IAAA,kBAAU,EAAC,SAAS,CAAC,KAAK,IAAA,kBAAU,EAAC,qBAAS,CAAC,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjH,MAAM,IAAI,KAAK,CAAC,0FAA0F,CAAC,CAAC;IAC9G,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;AAClC,CAAC;AAED,sDAAsD;AACtD,SAAgB,WAAW,CAAC,SAAqB;IAC/C,OAAO,qBAAS,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,SAAgB,WAAW,CAAC,SAAqB;IAC/C,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,yBAAiB,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9D,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,qBAAS,CAAC,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAC1D,IAAI,CAAC,CAAC,MAAM,CAAC,qBAAS,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QAC9D,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,gHAAgH;QAChH,uFAAuF;QACvF,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC;QAC5D,OAAO,CAAC,CAAC,aAAa,EAAE,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,uFAAuF;AACvF,SAAgB,IAAI,CAAC,SAAqB,EAAE,GAAe;IACzD,OAAO,qBAAS,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClD,CAAC;AAED,mFAAmF;AACnF,SAAgB,MAAM,CAAC,SAAqB,EAAE,GAAe,EAAE,GAAe;IAC5E,IAAI,CAAC;QACH,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAC7C,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC;QAC1C,OAAO,qBAAS,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,SAAS,CAAC,IAAkB;IAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,wCAAwC,CAAC,CAAC;IACtF,KAAK,MAAM,EAAE,IAAI,IAAI;QAAE,IAAI,CAAC,oBAAoB,CAAC,EAAE,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,kEAAkE,CAAC,CAAC;IAC/I,OAAO,qBAAS,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,oGAAoG;AACpG,SAAgB,mBAAmB,CAAC,UAAwB;IAC1D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,4CAA4C,CAAC,CAAC;IAChG,OAAO,qBAAS,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,mBAAmB,CAAC,IAAkB;IAC7C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,CAAC,GAAG,IAAA,kBAAU,EAAC,CAAC,CAAC,CAAC;QACxB,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QAC9B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,SAAgB,eAAe,CAAC,UAAwB,EAAE,IAAkB,EAAE,MAAkB;IAC9F,IAAI,CAAC;QACH,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;QAChD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC/E,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC;QAC7C,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;YAC5B,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBAAE,OAAO,KAAK,CAAC;QACrC,CAAC;QACD,OAAO,qBAAS,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAgB,mBAAmB,CAAC,UAAwB,EAAE,GAAe,EAAE,MAAkB;IAC/F,IAAI,CAAC;QACH,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;QAChD,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QAC1C,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE,CAAC;YAC5B,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBAAE,OAAO,KAAK,CAAC;QACrC,CAAC;QACD,MAAM,KAAK,GAAG,qBAAS,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC;QACxD,qGAAqG;QACrG,qGAAqG;QACrG,uGAAuG;QACvG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACtC,OAAO,qBAAS,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,QAAQ,CAAC,SAAqB;IAC5C,MAAM,EAAE,GAAG,qBAAS,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;IAC7C,OAAO,qBAAS,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AACjD,CAAC;AAED;8FAC8F;AAC9F,SAAgB,SAAS,CAAC,SAAqB,EAAE,GAAe;IAC9D,IAAI,CAAC;QACH,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAC7C,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC;YAAE,OAAO,KAAK,CAAC;QAC1C,OAAO,qBAAS,CAAC,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,6 @@
|
|
|
6
6
|
* check. Scheme: BLS (Boneh-Lynn-Shacham) with Proof-of-Possession (draft-irtf-cfrg-bls-signature),
|
|
7
7
|
* minimal-pubkey-size variant (G1 keys, G2 signatures). See {@link ./bls}.
|
|
8
8
|
*/
|
|
9
|
-
export { SIG_DST, POP_DST, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH, type BlsKeyPair, keyGen, publicKeyOf, keyValidate, sign, verify, aggregate,
|
|
9
|
+
export { SIG_DST, POP_DST, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH, type BlsKeyPair, keyGen, publicKeyOf, keyValidate, sign, verify, aggregate, aggregateVerify, fastAggregateVerify, popProve, RegisteredKey, type RegisterResult, registerKey, isRegisteredKey, KeyRegistry, } from './bls';
|
|
10
10
|
export { BLS_SUITE, MAX_CHAIN_DEPTH, type BlsCapability, type BlsCapabilityChain, capHash, blsPublicKey, mintBlsRoot, blsAttenuate, blsDelegate, aggregateChainSignatures, verifyAggregatedChain, } from './chain';
|
|
11
11
|
export { type SthStatement, type BlsWitnessCosignature, cosignTreeHead, verifyWitnessCosignature, aggregateWitnessCosignatures, verifyAggregatedWitnessCosignatures, } from './witness';
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* minimal-pubkey-size variant (G1 keys, G2 signatures). See {@link ./bls}.
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.verifyAggregatedWitnessCosignatures = exports.aggregateWitnessCosignatures = exports.verifyWitnessCosignature = exports.cosignTreeHead = exports.verifyAggregatedChain = exports.aggregateChainSignatures = exports.blsDelegate = exports.blsAttenuate = exports.mintBlsRoot = exports.blsPublicKey = exports.capHash = exports.MAX_CHAIN_DEPTH = exports.BLS_SUITE = exports.
|
|
11
|
+
exports.verifyAggregatedWitnessCosignatures = exports.aggregateWitnessCosignatures = exports.verifyWitnessCosignature = exports.cosignTreeHead = exports.verifyAggregatedChain = exports.aggregateChainSignatures = exports.blsDelegate = exports.blsAttenuate = exports.mintBlsRoot = exports.blsPublicKey = exports.capHash = exports.MAX_CHAIN_DEPTH = exports.BLS_SUITE = exports.KeyRegistry = exports.isRegisteredKey = exports.registerKey = exports.RegisteredKey = exports.popProve = exports.fastAggregateVerify = exports.aggregateVerify = exports.aggregate = exports.verify = exports.sign = exports.keyValidate = exports.publicKeyOf = exports.keyGen = exports.SIGNATURE_LENGTH = exports.PUBLIC_KEY_LENGTH = exports.POP_DST = exports.SIG_DST = void 0;
|
|
12
12
|
// Core BLS scheme + aggregation.
|
|
13
13
|
var bls_1 = require("./bls");
|
|
14
14
|
Object.defineProperty(exports, "SIG_DST", { enumerable: true, get: function () { return bls_1.SIG_DST; } });
|
|
@@ -21,11 +21,13 @@ Object.defineProperty(exports, "keyValidate", { enumerable: true, get: function
|
|
|
21
21
|
Object.defineProperty(exports, "sign", { enumerable: true, get: function () { return bls_1.sign; } });
|
|
22
22
|
Object.defineProperty(exports, "verify", { enumerable: true, get: function () { return bls_1.verify; } });
|
|
23
23
|
Object.defineProperty(exports, "aggregate", { enumerable: true, get: function () { return bls_1.aggregate; } });
|
|
24
|
-
Object.defineProperty(exports, "aggregatePublicKeys", { enumerable: true, get: function () { return bls_1.aggregatePublicKeys; } });
|
|
25
24
|
Object.defineProperty(exports, "aggregateVerify", { enumerable: true, get: function () { return bls_1.aggregateVerify; } });
|
|
26
25
|
Object.defineProperty(exports, "fastAggregateVerify", { enumerable: true, get: function () { return bls_1.fastAggregateVerify; } });
|
|
27
26
|
Object.defineProperty(exports, "popProve", { enumerable: true, get: function () { return bls_1.popProve; } });
|
|
28
|
-
Object.defineProperty(exports, "
|
|
27
|
+
Object.defineProperty(exports, "RegisteredKey", { enumerable: true, get: function () { return bls_1.RegisteredKey; } });
|
|
28
|
+
Object.defineProperty(exports, "registerKey", { enumerable: true, get: function () { return bls_1.registerKey; } });
|
|
29
|
+
Object.defineProperty(exports, "isRegisteredKey", { enumerable: true, get: function () { return bls_1.isRegisteredKey; } });
|
|
30
|
+
Object.defineProperty(exports, "KeyRegistry", { enumerable: true, get: function () { return bls_1.KeyRegistry; } });
|
|
29
31
|
// PCA BLS-suite capability chain.
|
|
30
32
|
var chain_1 = require("./chain");
|
|
31
33
|
Object.defineProperty(exports, "BLS_SUITE", { enumerable: true, get: function () { return chain_1.BLS_SUITE; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,iCAAiC;AACjC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,iCAAiC;AACjC,6BAoBe;AAnBb,8FAAA,OAAO,OAAA;AACP,8FAAA,OAAO,OAAA;AACP,wGAAA,iBAAiB,OAAA;AACjB,uGAAA,gBAAgB,OAAA;AAEhB,6FAAA,MAAM,OAAA;AACN,kGAAA,WAAW,OAAA;AACX,kGAAA,WAAW,OAAA;AACX,2FAAA,IAAI,OAAA;AACJ,6FAAA,MAAM,OAAA;AACN,gGAAA,SAAS,OAAA;AACT,sGAAA,eAAe,OAAA;AACf,0GAAA,mBAAmB,OAAA;AACnB,+FAAA,QAAQ,OAAA;AACR,oGAAA,aAAa,OAAA;AAEb,kGAAA,WAAW,OAAA;AACX,sGAAA,eAAe,OAAA;AACf,kGAAA,WAAW,OAAA;AAGb,kCAAkC;AAClC,iCAYiB;AAXf,kGAAA,SAAS,OAAA;AACT,wGAAA,eAAe,OAAA;AAGf,gGAAA,OAAO,OAAA;AACP,qGAAA,YAAY,OAAA;AACZ,oGAAA,WAAW,OAAA;AACX,qGAAA,YAAY,OAAA;AACZ,oGAAA,WAAW,OAAA;AACX,iHAAA,wBAAwB,OAAA;AACxB,8GAAA,qBAAqB,OAAA;AAGvB,uDAAuD;AACvD,qCAOmB;AAJjB,yGAAA,cAAc,OAAA;AACd,mHAAA,wBAAwB,OAAA;AACxB,uHAAA,4BAA4B,OAAA;AAC5B,8HAAA,mCAAmC,OAAA"}
|
package/dist/witness.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type SignedTreeHead } from '@atlasauth/pca';
|
|
2
|
+
import { type RegisteredKey } from './bls';
|
|
2
3
|
import { BLS_SUITE } from './chain';
|
|
3
4
|
/** The tree-head statement a witness binds to — the same fields the core ledger cosigns. */
|
|
4
5
|
export type SthStatement = Pick<SignedTreeHead, 'size' | 'root' | 'prev_root' | 'guardian_epoch'>;
|
|
@@ -21,8 +22,9 @@ export declare function verifyWitnessCosignature(statement: SthStatement, cosig:
|
|
|
21
22
|
export declare function aggregateWitnessCosignatures(cosigs: BlsWitnessCosignature[]): Uint8Array;
|
|
22
23
|
/**
|
|
23
24
|
* Verify an aggregate of witness cosignatures over ONE tree-head statement with a single
|
|
24
|
-
* FastAggregateVerify. `witnessKeys` are the
|
|
25
|
-
* caller supplies exactly the quorum it trusts
|
|
26
|
-
* `false` on an empty set, a
|
|
25
|
+
* FastAggregateVerify. `witnessKeys` are the {@link RegisteredKey}s (proof of possession verified) whose
|
|
26
|
+
* cosignatures were aggregated: the caller supplies exactly the quorum it trusts, and a key that never proved
|
|
27
|
+
* possession cannot be passed at all. Never throws; returns `false` on an empty set, a repeated witness, an
|
|
28
|
+
* unregistered key, or a failed pairing check.
|
|
27
29
|
*/
|
|
28
|
-
export declare function verifyAggregatedWitnessCosignatures(statement: SthStatement, witnessKeys:
|
|
30
|
+
export declare function verifyAggregatedWitnessCosignatures(statement: SthStatement, witnessKeys: readonly RegisteredKey[], aggSig: Uint8Array): boolean;
|
package/dist/witness.js
CHANGED
|
@@ -18,7 +18,7 @@ const chain_1 = require("./chain");
|
|
|
18
18
|
* {@link verifyAggregatedWitnessCosignatures} checks it with a single FastAggregateVerify (all
|
|
19
19
|
* witnesses signed the identical STH statement → same-message aggregation).
|
|
20
20
|
*/
|
|
21
|
-
/** Domain separator — distinct from the Ed25519 ledger's `atlas-pca/sth-witness/
|
|
21
|
+
/** Domain separator — distinct from the Ed25519 ledger's `atlas-pca/sth-witness/v2`, so a BLS
|
|
22
22
|
* cosignature can never be confused with (or replayed against) the core witness domain. */
|
|
23
23
|
const STH_WITNESS_BLS_DOMAIN = 'atlas-pca/sth-witness-bls/v1\0';
|
|
24
24
|
/** The exact bytes a witness cosigns: domain separator followed by the canonical STH statement. */
|
|
@@ -48,7 +48,7 @@ function verifyWitnessCosignature(statement, cosig) {
|
|
|
48
48
|
try {
|
|
49
49
|
if (cosig === null || typeof cosig !== 'object' || cosig.suite !== chain_1.BLS_SUITE)
|
|
50
50
|
return false;
|
|
51
|
-
return (0, bls_1.
|
|
51
|
+
return (0, bls_1.verify)((0, pca_1.unb64u)(cosig.key), witnessMessage(statement), (0, pca_1.unb64u)(cosig.sig));
|
|
52
52
|
}
|
|
53
53
|
catch {
|
|
54
54
|
return false;
|
|
@@ -65,16 +65,20 @@ function aggregateWitnessCosignatures(cosigs) {
|
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
67
|
* Verify an aggregate of witness cosignatures over ONE tree-head statement with a single
|
|
68
|
-
* FastAggregateVerify. `witnessKeys` are the
|
|
69
|
-
* caller supplies exactly the quorum it trusts
|
|
70
|
-
* `false` on an empty set, a
|
|
68
|
+
* FastAggregateVerify. `witnessKeys` are the {@link RegisteredKey}s (proof of possession verified) whose
|
|
69
|
+
* cosignatures were aggregated: the caller supplies exactly the quorum it trusts, and a key that never proved
|
|
70
|
+
* possession cannot be passed at all. Never throws; returns `false` on an empty set, a repeated witness, an
|
|
71
|
+
* unregistered key, or a failed pairing check.
|
|
71
72
|
*/
|
|
72
73
|
function verifyAggregatedWitnessCosignatures(statement, witnessKeys, aggSig) {
|
|
73
74
|
try {
|
|
74
75
|
if (!Array.isArray(witnessKeys) || witnessKeys.length === 0)
|
|
75
76
|
return false;
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
// A witness may appear once: otherwise one witness's cosignature, aggregated with itself, would satisfy
|
|
78
|
+
// "n distinct witnesses" for any caller that counts keys.
|
|
79
|
+
if (new Set(witnessKeys.map((k) => Buffer.from(k.bytes).toString('hex'))).size !== witnessKeys.length)
|
|
80
|
+
return false;
|
|
81
|
+
return (0, bls_1.fastAggregateVerify)(witnessKeys, witnessMessage(statement), aggSig);
|
|
78
82
|
}
|
|
79
83
|
catch {
|
|
80
84
|
return false;
|
package/dist/witness.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"witness.js","sourceRoot":"","sources":["../src/witness.ts"],"names":[],"mappings":";;AAgDA,wCAMC;AAGD,4DAOC;AAMD,oEAGC;
|
|
1
|
+
{"version":3,"file":"witness.js","sourceRoot":"","sources":["../src/witness.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlasauth/pca-aggsig",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "BLS signature aggregation for PCA: collapse a delegation chain per-hop signatures and transparency-ledger witness cosignatures into one compact BLS12-381 aggregate with aggregate-verify.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bls",
|
|
@@ -34,9 +34,10 @@
|
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@noble/curves": "^1.6.0",
|
|
36
36
|
"@noble/hashes": "^1.5.0",
|
|
37
|
-
"@atlasauth/pca": "0.
|
|
37
|
+
"@atlasauth/pca": "0.4.1"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
+
"fast-check": "^3.23.1",
|
|
40
41
|
"typescript": "^5.6.3",
|
|
41
42
|
"vitest": "^2.1.4"
|
|
42
43
|
},
|