@did-btcr2/method 0.34.0 → 0.35.0
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/.tsbuildinfo +1 -1
- package/dist/browser.js +190 -112
- package/dist/browser.mjs +190 -112
- package/dist/cjs/index.js +130 -60
- package/dist/esm/core/aggregation/cohort.js +5 -1
- package/dist/esm/core/aggregation/cohort.js.map +1 -1
- package/dist/esm/core/aggregation/participant.js +25 -8
- package/dist/esm/core/aggregation/participant.js.map +1 -1
- package/dist/esm/core/aggregation/runner/participant-runner.js +9 -1
- package/dist/esm/core/aggregation/runner/participant-runner.js.map +1 -1
- package/dist/esm/core/aggregation/runner/service-runner.js +4 -1
- package/dist/esm/core/aggregation/runner/service-runner.js.map +1 -1
- package/dist/esm/core/aggregation/service.js +4 -4
- package/dist/esm/core/aggregation/service.js.map +1 -1
- package/dist/esm/core/aggregation/signer.js +33 -0
- package/dist/esm/core/aggregation/signer.js.map +1 -0
- package/dist/esm/core/aggregation/signing-session.js +34 -11
- package/dist/esm/core/aggregation/signing-session.js.map +1 -1
- package/dist/esm/core/aggregation/transport/in-memory.js +5 -2
- package/dist/esm/core/aggregation/transport/in-memory.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/types/core/aggregation/cohort.d.ts.map +1 -1
- package/dist/types/core/aggregation/participant.d.ts +17 -4
- package/dist/types/core/aggregation/participant.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/participant-runner.d.ts.map +1 -1
- package/dist/types/core/aggregation/runner/service-runner.d.ts.map +1 -1
- package/dist/types/core/aggregation/service.d.ts +10 -4
- package/dist/types/core/aggregation/service.d.ts.map +1 -1
- package/dist/types/core/aggregation/signer.d.ts +50 -0
- package/dist/types/core/aggregation/signer.d.ts.map +1 -0
- package/dist/types/core/aggregation/signing-session.d.ts +13 -5
- package/dist/types/core/aggregation/signing-session.d.ts.map +1 -1
- package/dist/types/core/aggregation/transport/in-memory.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/core/aggregation/cohort.ts +5 -1
- package/src/core/aggregation/participant.ts +38 -12
- package/src/core/aggregation/runner/participant-runner.ts +9 -1
- package/src/core/aggregation/runner/service-runner.ts +4 -1
- package/src/core/aggregation/service.ts +12 -6
- package/src/core/aggregation/signer.ts +67 -0
- package/src/core/aggregation/signing-session.ts +34 -11
- package/src/core/aggregation/transport/in-memory.ts +7 -3
- package/src/index.ts +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"participant-runner.d.ts","sourceRoot":"","sources":["../../../../../src/core/aggregation/runner/participant-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAUzD,OAAO,KAAK,EACV,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EACL,sBAAsB,EACvB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"participant-runner.d.ts","sourceRoot":"","sources":["../../../../../src/core/aggregation/runner/participant-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAUzD,OAAO,KAAK,EACV,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EACL,sBAAsB,EACvB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,yEAAyE;AACzE,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpE,wEAAwE;AACxE,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEjC,4DAA4D;AAC5D,MAAM,MAAM,cAAc,GAAG,CAAC,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAEzF,oDAAoD;AACpD,MAAM,MAAM,gBAAgB,GAAG,CAAC,IAAI,EAAE,qBAAqB,KAAK,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAE/F,MAAM,WAAW,mCAAmC;IAClD,4BAA4B;IAC5B,SAAS,EAAE,SAAS,CAAC;IAErB,4BAA4B;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,cAAc,CAAC;IAErB;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB;;;OAGG;IACH,eAAe,EAAE,eAAe,CAAC;IAEjC;;;OAGG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,qBAAa,4BAA6B,SAAQ,iBAAiB,CAAC,4BAA4B,CAAC;;IAC/F,sEAAsE;IACtE,QAAQ,CAAC,OAAO,EAAE,sBAAsB,CAAC;gBAY7B,OAAO,EAAE,mCAAmC;IAexD;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,8EAA8E;IAC9E,IAAI,IAAI,IAAI;IA4BZ;;;OAGG;WACU,SAAS,CACpB,OAAO,EAAE,mCAAmC,GAC3C,OAAO,CAAC;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;QACtB,UAAU,EAAE,MAAM,CAAC;QACnB,mEAAmE;QACnE,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACzC,0FAA0F;QAC1F,QAAQ,CAAC,EAAE,kBAAkB,CAAC;KAC/B,CAAC;CAwNH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service-runner.d.ts","sourceRoot":"","sources":["../../../../../src/core/aggregation/runner/service-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAUzD,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EAAC,MAAM,eAAe,CAAC;AACtC,OAAO,EACL,kBAAkB,EACnB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,kEAAkE;AAClE,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAEtF,6EAA6E;AAC7E,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB,KAAK,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAErC,mEAAmE;AACnE,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,UAAU,CAAC;CACzB,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAE7B,MAAM,WAAW,+BAA+B;IAC9C,kEAAkE;IAClE,SAAS,EAAE,SAAS,CAAC;IAErB,+BAA+B;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,cAAc,CAAC;IAErB,4BAA4B;IAC5B,MAAM,EAAE,YAAY,CAAC;IAErB;;;OAGG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAElC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAEtC;;;OAGG;IACH,eAAe,EAAE,eAAe,CAAC;IAEjC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;;;;;;OASG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,0FAA0F;AAC1F,eAAO,MAAM,iCAAiC,QAAS,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,wBAAyB,SAAQ,iBAAiB,CAAC,wBAAwB,CAAC;;IACvF,sEAAsE;IACtE,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;gBA8BzB,OAAO,EAAE,+BAA+B;
|
|
1
|
+
{"version":3,"file":"service-runner.d.ts","sourceRoot":"","sources":["../../../../../src/core/aggregation/runner/service-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAUzD,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EAAC,MAAM,eAAe,CAAC;AACtC,OAAO,EACL,kBAAkB,EACnB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,kEAAkE;AAClE,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAEtF,6EAA6E;AAC7E,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB,KAAK,OAAO,CAAC;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAErC,mEAAmE;AACnE,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,UAAU,CAAC;CACzB,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAE7B,MAAM,WAAW,+BAA+B;IAC9C,kEAAkE;IAClE,SAAS,EAAE,SAAS,CAAC;IAErB,+BAA+B;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,cAAc,CAAC;IAErB,4BAA4B;IAC5B,MAAM,EAAE,YAAY,CAAC;IAErB;;;OAGG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAElC;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IAEtC;;;OAGG;IACH,eAAe,EAAE,eAAe,CAAC;IAEjC;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;;;;;;OASG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,0FAA0F;AAC1F,eAAO,MAAM,iCAAiC,QAAS,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,wBAAyB,SAAQ,iBAAiB,CAAC,wBAAwB,CAAC;;IACvF,sEAAsE;IACtE,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC;gBA8BzB,OAAO,EAAE,+BAA+B;IAoCpD;;;;;OAKG;IACH,GAAG,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAiGjC;;;OAGG;IACH,IAAI,IAAI,IAAI;CAqQb"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SignedBTCR2Update } from '@did-btcr2/cryptosuite';
|
|
2
|
-
import type {
|
|
2
|
+
import type { CompressedSecp256k1PublicKey } from '@did-btcr2/keypair';
|
|
3
3
|
import type { Transaction } from '@scure/btc-signer';
|
|
4
4
|
import { AggregationCohort } from './cohort.js';
|
|
5
5
|
import type { BaseMessage } from './messages/base.js';
|
|
@@ -51,7 +51,13 @@ export interface Rejection {
|
|
|
51
51
|
export declare const DEFAULT_MAX_UPDATE_SIZE_BYTES: number;
|
|
52
52
|
export interface AggregationServiceParams {
|
|
53
53
|
did: string;
|
|
54
|
-
|
|
54
|
+
/**
|
|
55
|
+
* The service's compressed communication public key (placed in cohort adverts).
|
|
56
|
+
* The coordinator never signs - it aggregates public nonces and partial
|
|
57
|
+
* signatures - so it is given a public key only, never a secret-bearing
|
|
58
|
+
* keypair (see ADR 038).
|
|
59
|
+
*/
|
|
60
|
+
publicKey: CompressedSecp256k1PublicKey;
|
|
55
61
|
/**
|
|
56
62
|
* Maximum canonicalized byte-length of a signed update body accepted by the
|
|
57
63
|
* service. Submissions above this cap are silently dropped and surfaced as
|
|
@@ -73,9 +79,9 @@ export interface AggregationServiceParams {
|
|
|
73
79
|
export declare class AggregationService {
|
|
74
80
|
#private;
|
|
75
81
|
readonly did: string;
|
|
76
|
-
readonly
|
|
82
|
+
readonly publicKey: CompressedSecp256k1PublicKey;
|
|
77
83
|
readonly maxUpdateSizeBytes: number;
|
|
78
|
-
constructor({ did,
|
|
84
|
+
constructor({ did, publicKey, maxUpdateSizeBytes }: AggregationServiceParams);
|
|
79
85
|
receive(message: BaseMessage): void;
|
|
80
86
|
/**
|
|
81
87
|
* Drain the rejection log for a cohort. Used by runners to surface silent
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../src/core/aggregation/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../../src/core/aggregation/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { SchnorrKeyPair } from '@did-btcr2/keypair';
|
|
2
|
+
/**
|
|
3
|
+
* A signing capability for MuSig2 aggregation.
|
|
4
|
+
*
|
|
5
|
+
* MuSig2 (BIP-327) cannot be driven through a generic `sign(message)` primitive:
|
|
6
|
+
* both nonce generation and partial signing need the raw 32-byte secret scalar
|
|
7
|
+
* (see ADR 038). Rather than hand that scalar around as a long-lived field, the
|
|
8
|
+
* participant holds an `AggregationSigner` and materializes the secret only for
|
|
9
|
+
* the duration of a single operation via {@link AggregationSigner.withSecret},
|
|
10
|
+
* which is responsible for wiping its working copy afterward.
|
|
11
|
+
*
|
|
12
|
+
* This is the seam a non-extractable / KMS-backed signer (or a session-scoped
|
|
13
|
+
* signer that destroys its key after the cohort completes) plugs into without
|
|
14
|
+
* changing the participant state machine.
|
|
15
|
+
*
|
|
16
|
+
* @interface AggregationSigner
|
|
17
|
+
*/
|
|
18
|
+
export interface AggregationSigner {
|
|
19
|
+
/** Compressed 33-byte public key. Not secret; always available. */
|
|
20
|
+
readonly publicKey: Uint8Array;
|
|
21
|
+
/**
|
|
22
|
+
* Materialize the raw 32-byte secret key, pass it to `fn`, and zeroize the
|
|
23
|
+
* working copy before returning - even if `fn` throws. The secret must not
|
|
24
|
+
* escape the callback.
|
|
25
|
+
*
|
|
26
|
+
* @typeParam T The callback's return type (e.g. a nonce contribution or partial signature).
|
|
27
|
+
* @param {(secretKey: Uint8Array) => T} fn Operation that needs the raw secret for its duration.
|
|
28
|
+
* @returns {T} Whatever `fn` returns.
|
|
29
|
+
*/
|
|
30
|
+
withSecret<T>(fn: (secretKey: Uint8Array) => T): T;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* {@link AggregationSigner} backed by an in-memory {@link SchnorrKeyPair}.
|
|
34
|
+
*
|
|
35
|
+
* The keypair is held privately (never exposed as a public field) and each
|
|
36
|
+
* `withSecret` call pulls a fresh copy of the secret bytes, hands it to the
|
|
37
|
+
* callback, and wipes that copy on return. The underlying keypair is the
|
|
38
|
+
* caller's to own and destroy; this signer never mutates or destroys it.
|
|
39
|
+
*
|
|
40
|
+
* @class KeyPairAggregationSigner
|
|
41
|
+
* @implements {AggregationSigner}
|
|
42
|
+
*/
|
|
43
|
+
export declare class KeyPairAggregationSigner implements AggregationSigner {
|
|
44
|
+
#private;
|
|
45
|
+
readonly publicKey: Uint8Array;
|
|
46
|
+
/** @param {SchnorrKeyPair} keys The keypair whose secret backs this signer. */
|
|
47
|
+
constructor(keys: SchnorrKeyPair);
|
|
48
|
+
withSecret<T>(fn: (secretKey: Uint8Array) => T): T;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=signer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signer.d.ts","sourceRoot":"","sources":["../../../../src/core/aggregation/signer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGzD;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAE/B;;;;;;;;OAQG;IACH,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,EAAE,UAAU,KAAK,CAAC,GAAG,CAAC,CAAC;CACpD;AAED;;;;;;;;;;GAUG;AACH,qBAAa,wBAAyB,YAAW,iBAAiB;;IAChE,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAG/B,+EAA+E;gBACnE,IAAI,EAAE,cAAc;IAKhC,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,EAAE,UAAU,KAAK,CAAC,GAAG,CAAC;CAUnD"}
|
|
@@ -21,6 +21,7 @@ export interface SigningSessionParams {
|
|
|
21
21
|
* @class BeaconSigningSession
|
|
22
22
|
*/
|
|
23
23
|
export declare class BeaconSigningSession {
|
|
24
|
+
#private;
|
|
24
25
|
/** Unique identifier for this signing session. */
|
|
25
26
|
id: string;
|
|
26
27
|
/** The cohort this session signs for. */
|
|
@@ -41,8 +42,6 @@ export declare class BeaconSigningSession {
|
|
|
41
42
|
signature?: Uint8Array;
|
|
42
43
|
/** Current signing session phase. */
|
|
43
44
|
phase: SigningSessionPhaseType;
|
|
44
|
-
/** Participant's secret nonce (held only by the participant during signing). */
|
|
45
|
-
secretNonce?: Uint8Array;
|
|
46
45
|
constructor({ id, cohort, pendingTx, prevOutScripts, prevOutValues }: SigningSessionParams);
|
|
47
46
|
/**
|
|
48
47
|
* Computes the Taproot sighash (BIP-341) for the first input.
|
|
@@ -61,11 +60,20 @@ export declare class BeaconSigningSession {
|
|
|
61
60
|
* Generates a partial signature using the participant's secret key + secret nonce.
|
|
62
61
|
* Requires the aggregated nonce to have been set first (via the service).
|
|
63
62
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
63
|
+
* Clears the stored secret nonce after use on every path (success or throw)
|
|
64
|
+
* via {@link clearSecrets}. JS cannot truly erase memory (GC may relocate
|
|
65
|
+
* buffers), but overwriting the bytes shortens the exposure window and
|
|
66
|
+
* prevents accidental reuse or serialization of a spent nonce - reuse of a
|
|
67
|
+
* MuSig2 nonce leaks the secret key.
|
|
67
68
|
*/
|
|
68
69
|
generatePartialSignature(participantSecretKey: Uint8Array): Uint8Array;
|
|
70
|
+
/**
|
|
71
|
+
* Zeroize any retained secret nonce. Safe to call repeatedly and on any path
|
|
72
|
+
* (completion, failure, or teardown of an abandoned session). Callers that
|
|
73
|
+
* drop a session before it reaches a partial signature should invoke this so
|
|
74
|
+
* the secret nonce does not linger on the live object.
|
|
75
|
+
*/
|
|
76
|
+
clearSecrets(): void;
|
|
69
77
|
isComplete(): boolean;
|
|
70
78
|
isFailed(): boolean;
|
|
71
79
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signing-session.d.ts","sourceRoot":"","sources":["../../../../src/core/aggregation/signing-session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"signing-session.d.ts","sourceRoot":"","sources":["../../../../src/core/aggregation/signing-session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAG3D,KAAK,YAAY,GAAG,MAAM,CAAC;AAC3B,KAAK,KAAK,GAAG,UAAU,CAAC;AAExB,MAAM,WAAW,oBAAoB;IACnC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,iBAAiB,CAAC;IAC1B,SAAS,EAAE,WAAW,CAAC;IACvB,cAAc,CAAC,EAAE,UAAU,EAAE,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,qBAAa,oBAAoB;;IAC/B,kDAAkD;IAC3C,EAAE,EAAE,MAAM,CAAC;IAElB,yCAAyC;IAClC,MAAM,EAAE,iBAAiB,CAAC;IAEjC,4CAA4C;IACrC,SAAS,EAAE,WAAW,CAAC;IAE9B,yEAAyE;IAClE,cAAc,EAAE,UAAU,EAAE,CAAC;IAEpC,8DAA8D;IACvD,aAAa,EAAE,MAAM,EAAE,CAAC;IAE/B,oEAAoE;IAC7D,kBAAkB,EAAE,GAAG,CAAC,YAAY,EAAE,KAAK,CAAC,CAAa;IAEhE,0CAA0C;IACnC,eAAe,CAAC,EAAE,UAAU,CAAC;IAEpC,kDAAkD;IAC3C,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAa;IAE9D,uCAAuC;IAChC,SAAS,CAAC,EAAE,UAAU,CAAC;IAE9B,qCAAqC;IAC9B,KAAK,EAAE,uBAAuB,CAAC;gBAW1B,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,EAAE,oBAAoB;IAS1F;;OAEG;IACH,IAAI,OAAO,IAAI,UAAU,CAaxB;IAEM,oBAAoB,CAAC,cAAc,EAAE,MAAM,EAAE,iBAAiB,EAAE,UAAU,GAAG,IAAI;IAgCjF,uBAAuB,IAAI,UAAU;IAYrC,mBAAmB,CAAC,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI;IA0BzE,sBAAsB,IAAI,UAAU;IA4D3C;;;OAGG;IACI,yBAAyB,CAAC,oBAAoB,EAAE,UAAU,EAAE,oBAAoB,EAAE,UAAU,GAAG,UAAU;IAOhH;;;;;;;;;OASG;IACI,wBAAwB,CAAC,oBAAoB,EAAE,UAAU,GAAG,UAAU;IAqB7E;;;;;OAKG;IACI,YAAY,IAAI,IAAI;IAOpB,UAAU,IAAI,OAAO;IAIrB,QAAQ,IAAI,OAAO;CAG3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"in-memory.d.ts","sourceRoot":"","sources":["../../../../../src/core/aggregation/transport/in-memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"in-memory.d.ts","sourceRoot":"","sources":["../../../../../src/core/aggregation/transport/in-memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAShE;;;;;;;;;;;GAWG;AACH,qBAAa,WAAW;;IAGtB,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI;IAI5C,wCAAwC;IACxC,UAAU,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI;IAI9C;;;;OAIG;IACG,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA0BxF;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAkB,YAAW,SAAS;;IACjD,IAAI,EAAE,MAAM,CAAe;IAC3B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAK1B,+EAA+E;gBACnE,GAAG,GAAE,WAA+B;IAKhD,KAAK,IAAI,IAAI;IAIb,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI;IAOtD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI/C,kFAAkF;IAClF,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI9B,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,UAAU,GAAG,IAAI;IAI5D,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAI9C,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI;IAK5F,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAKrE,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQ5B,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1F,gBAAgB,CACd,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,IAAI;IAcb,wFAAwF;IAClF,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAOtE,8EAA8E;IACxE,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;CAI5F"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './core/aggregation/service.js';
|
|
2
2
|
export * from './core/aggregation/participant.js';
|
|
3
|
+
export * from './core/aggregation/signer.js';
|
|
3
4
|
export * from './core/aggregation/cohort.js';
|
|
4
5
|
export * from './core/aggregation/signing-session.js';
|
|
5
6
|
export * from './core/aggregation/phases.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AAGnD,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iCAAiC,CAAC;AAChD,cAAc,yBAAyB,CAAC;AAGxC,cAAc,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AAGnD,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,qBAAqB,CAAC;AACpC,cAAc,iCAAiC,CAAC;AAChD,cAAc,yBAAyB,CAAC;AAGxC,cAAc,gBAAgB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@did-btcr2/method",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Reference implementation for the did:btcr2 DID method written in TypeScript and JavaScript. did:btcr2 is a censorship resistant DID Method using the Bitcoin blockchain as a Verifiable Data Registry to announce changes to the DID document. This is the core method implementation for the did-btcr2-js monorepo.",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -80,11 +80,11 @@
|
|
|
80
80
|
"helia": "^5.5.1",
|
|
81
81
|
"multiformats": "^13.4.2",
|
|
82
82
|
"nostr-tools": "^2.23.3",
|
|
83
|
-
"@did-btcr2/smt": "^0.3.0",
|
|
84
83
|
"@did-btcr2/common": "^9.1.0",
|
|
85
84
|
"@did-btcr2/bitcoin": "^0.6.0",
|
|
86
|
-
"@did-btcr2/
|
|
87
|
-
"@did-btcr2/
|
|
85
|
+
"@did-btcr2/cryptosuite": "^8.0.0",
|
|
86
|
+
"@did-btcr2/smt": "^0.3.0",
|
|
87
|
+
"@did-btcr2/keypair": "^0.13.1"
|
|
88
88
|
},
|
|
89
89
|
"devDependencies": {
|
|
90
90
|
"@eslint/js": "^9.39.4",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getNetwork } from '@did-btcr2/bitcoin';
|
|
1
2
|
import { canonicalHash, canonicalize, hash } from '@did-btcr2/common';
|
|
2
3
|
import type { SignedBTCR2Update } from '@did-btcr2/cryptosuite';
|
|
3
4
|
import type { SerializedSMTProof, TreeEntry } from '@did-btcr2/smt';
|
|
@@ -118,7 +119,10 @@ export class AggregationCohort {
|
|
|
118
119
|
}
|
|
119
120
|
const keyAggContext = keyAggregate(this.#cohortKeys);
|
|
120
121
|
const aggPubkey = keyAggExport(keyAggContext);
|
|
121
|
-
|
|
122
|
+
// Derive the address for the cohort's network. Without the network arg
|
|
123
|
+
// p2tr defaults to mainnet, so a mutinynet/signet/regtest cohort would
|
|
124
|
+
// otherwise advertise a `bc1p...` address that no participant can fund.
|
|
125
|
+
const payment = p2tr(aggPubkey, undefined, getNetwork(this.network));
|
|
122
126
|
|
|
123
127
|
// BIP-341: key-path-only P2TR has no script tree. Compute the tweak:
|
|
124
128
|
// taggedHash("TapTweak", internalPubkey).
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { canonicalHash } from '@did-btcr2/common';
|
|
2
2
|
import type { SignedBTCR2Update } from '@did-btcr2/cryptosuite';
|
|
3
|
-
import type { SchnorrKeyPair } from '@did-btcr2/keypair';
|
|
4
3
|
import type { SerializedSMTProof} from '@did-btcr2/smt';
|
|
5
4
|
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
|
|
6
5
|
import { Transaction } from '@scure/btc-signer';
|
|
@@ -26,6 +25,7 @@ import {
|
|
|
26
25
|
} from './messages/factories.js';
|
|
27
26
|
import type { ParticipantCohortPhaseType } from './phases.js';
|
|
28
27
|
import { ParticipantCohortPhase } from './phases.js';
|
|
28
|
+
import type { AggregationSigner } from './signer.js';
|
|
29
29
|
import { BeaconSigningSession } from './signing-session.js';
|
|
30
30
|
|
|
31
31
|
/** Cohort advert as discovered by the participant (UI: list of joinable cohorts). */
|
|
@@ -82,7 +82,12 @@ interface ParticipantCohortState {
|
|
|
82
82
|
|
|
83
83
|
export interface AggregationParticipantParams {
|
|
84
84
|
did: string;
|
|
85
|
-
|
|
85
|
+
/**
|
|
86
|
+
* The participant's MuSig2 signing capability. The raw secret is materialized
|
|
87
|
+
* only for the duration of a single nonce/partial-sign operation (see ADR 038);
|
|
88
|
+
* pass a {@link KeyPairAggregationSigner} to back it with an in-memory keypair.
|
|
89
|
+
*/
|
|
90
|
+
signer: AggregationSigner;
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
/**
|
|
@@ -97,14 +102,21 @@ export interface AggregationParticipantParams {
|
|
|
97
102
|
*/
|
|
98
103
|
export class AggregationParticipant {
|
|
99
104
|
public readonly did: string;
|
|
100
|
-
|
|
105
|
+
|
|
106
|
+
/** MuSig2 signing capability. The raw secret never lives as a field here. */
|
|
107
|
+
readonly #signer: AggregationSigner;
|
|
101
108
|
|
|
102
109
|
/** Per-cohort state, keyed by cohortId. */
|
|
103
110
|
#cohortStates: Map<string, ParticipantCohortState> = new Map();
|
|
104
111
|
|
|
105
|
-
constructor({ did,
|
|
112
|
+
constructor({ did, signer }: AggregationParticipantParams) {
|
|
106
113
|
this.did = did;
|
|
107
|
-
this
|
|
114
|
+
this.#signer = signer;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The participant's compressed (33-byte) MuSig2 public key. Not secret. */
|
|
118
|
+
public get publicKey(): Uint8Array {
|
|
119
|
+
return this.#signer.publicKey;
|
|
108
120
|
}
|
|
109
121
|
|
|
110
122
|
|
|
@@ -205,8 +217,8 @@ export class AggregationParticipant {
|
|
|
205
217
|
from : this.did,
|
|
206
218
|
to : state.serviceDid,
|
|
207
219
|
cohortId,
|
|
208
|
-
participantPk : this.
|
|
209
|
-
communicationPk : this.
|
|
220
|
+
participantPk : this.publicKey,
|
|
221
|
+
communicationPk : this.publicKey,
|
|
210
222
|
});
|
|
211
223
|
|
|
212
224
|
return [optInMessage];
|
|
@@ -245,7 +257,7 @@ export class AggregationParticipant {
|
|
|
245
257
|
const cohortKeys = message.body?.cohortKeys;
|
|
246
258
|
if(!beaconAddress || !cohortKeys) return;
|
|
247
259
|
|
|
248
|
-
const participantPkHex = bytesToHex(this.
|
|
260
|
+
const participantPkHex = bytesToHex(this.publicKey);
|
|
249
261
|
const cohortKeysHex = cohortKeys.map(k => bytesToHex(new Uint8Array(k)));
|
|
250
262
|
|
|
251
263
|
state.cohort.validateMembership(participantPkHex, cohortKeysHex, beaconAddress);
|
|
@@ -435,9 +447,8 @@ export class AggregationParticipant {
|
|
|
435
447
|
});
|
|
436
448
|
state.signingSession = session;
|
|
437
449
|
|
|
438
|
-
const nonceContribution =
|
|
439
|
-
this.
|
|
440
|
-
this.keys.secretKey.bytes
|
|
450
|
+
const nonceContribution = this.#signer.withSecret(
|
|
451
|
+
secretKey => session.generateNonceContribution(this.publicKey, secretKey)
|
|
441
452
|
);
|
|
442
453
|
|
|
443
454
|
state.phase = ParticipantCohortPhase.NonceSent;
|
|
@@ -485,7 +496,10 @@ export class AggregationParticipant {
|
|
|
485
496
|
);
|
|
486
497
|
}
|
|
487
498
|
|
|
488
|
-
const
|
|
499
|
+
const signingSession = state.signingSession;
|
|
500
|
+
const partialSig = this.#signer.withSecret(
|
|
501
|
+
secretKey => signingSession.generatePartialSignature(secretKey)
|
|
502
|
+
);
|
|
489
503
|
state.phase = ParticipantCohortPhase.Complete;
|
|
490
504
|
|
|
491
505
|
return [createSignatureAuthorizationMessage({
|
|
@@ -501,4 +515,16 @@ export class AggregationParticipant {
|
|
|
501
515
|
public getCohortPhase(cohortId: string): ParticipantCohortPhaseType | undefined {
|
|
502
516
|
return this.#cohortStates.get(cohortId)?.phase;
|
|
503
517
|
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Zeroize any retained MuSig2 secret nonces across all cohorts. The raw
|
|
521
|
+
* signing key is never held here (it lives behind the {@link AggregationSigner}
|
|
522
|
+
* and is wiped per-operation), but an abandoned signing session can still hold
|
|
523
|
+
* a secret nonce; call this on teardown to clear it deterministically.
|
|
524
|
+
*/
|
|
525
|
+
public clearSecrets(): void {
|
|
526
|
+
for(const state of this.#cohortStates.values()) {
|
|
527
|
+
state.signingSession?.clearSecrets();
|
|
528
|
+
}
|
|
529
|
+
}
|
|
504
530
|
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
AggregationParticipant
|
|
19
19
|
} from '../participant.js';
|
|
20
20
|
import { ParticipantCohortPhase } from '../phases.js';
|
|
21
|
+
import { KeyPairAggregationSigner } from '../signer.js';
|
|
21
22
|
import type { Transport } from '../transport/transport.js';
|
|
22
23
|
import type { AggregationParticipantEvents } from './events.js';
|
|
23
24
|
import { TypedEventEmitter } from './typed-emitter.js';
|
|
@@ -129,7 +130,10 @@ export class AggregationParticipantRunner extends TypedEventEmitter<AggregationP
|
|
|
129
130
|
this.#onValidateData = options.onValidateData ?? (async (info) => ({ approved: info.matches }));
|
|
130
131
|
this.#onApproveSigning = options.onApproveSigning ?? (async () => ({ approved: true }));
|
|
131
132
|
|
|
132
|
-
this.session = new AggregationParticipant({
|
|
133
|
+
this.session = new AggregationParticipant({
|
|
134
|
+
did : options.did,
|
|
135
|
+
signer : new KeyPairAggregationSigner(options.keys),
|
|
136
|
+
});
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
/**
|
|
@@ -144,6 +148,10 @@ export class AggregationParticipantRunner extends TypedEventEmitter<AggregationP
|
|
|
144
148
|
stop(): void {
|
|
145
149
|
this.#stopped = true;
|
|
146
150
|
this.#unregisterHandlers();
|
|
151
|
+
// Deterministically wipe any secret nonce left on an abandoned signing
|
|
152
|
+
// session. The signing key itself lives behind the AggregationSigner and is
|
|
153
|
+
// never retained here.
|
|
154
|
+
this.session.clearSecrets();
|
|
147
155
|
}
|
|
148
156
|
|
|
149
157
|
/** Message types this runner listens for on the transport. */
|
|
@@ -184,8 +184,11 @@ export class AggregationServiceRunner extends TypedEventEmitter<AggregationServi
|
|
|
184
184
|
this.#advertRepeatIntervalMs = options.advertRepeatIntervalMs ?? DEFAULT_ADVERT_REPEAT_INTERVAL_MS;
|
|
185
185
|
|
|
186
186
|
this.session = new AggregationService({
|
|
187
|
+
// The coordinator never signs, so the state machine receives only the
|
|
188
|
+
// public half of the operator's keypair (see ADR 038). The full keypair
|
|
189
|
+
// remains the operator's transport/communication identity.
|
|
187
190
|
did : options.did,
|
|
188
|
-
|
|
191
|
+
publicKey : options.keys.publicKey,
|
|
189
192
|
maxUpdateSizeBytes : options.maxUpdateSizeBytes,
|
|
190
193
|
});
|
|
191
194
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { canonicalize } from '@did-btcr2/common';
|
|
2
2
|
import type { SignedBTCR2Update } from '@did-btcr2/cryptosuite';
|
|
3
3
|
import { BIP340Cryptosuite, SchnorrMultikey } from '@did-btcr2/cryptosuite';
|
|
4
|
-
import type {
|
|
4
|
+
import type { CompressedSecp256k1PublicKey } from '@did-btcr2/keypair';
|
|
5
5
|
import { bytesToHex } from '@noble/hashes/utils';
|
|
6
6
|
import type { Transaction } from '@scure/btc-signer';
|
|
7
7
|
import { getBeaconStrategy } from './beacon-strategy.js';
|
|
@@ -100,7 +100,13 @@ export const DEFAULT_MAX_UPDATE_SIZE_BYTES = 256 * 1024;
|
|
|
100
100
|
|
|
101
101
|
export interface AggregationServiceParams {
|
|
102
102
|
did: string;
|
|
103
|
-
|
|
103
|
+
/**
|
|
104
|
+
* The service's compressed communication public key (placed in cohort adverts).
|
|
105
|
+
* The coordinator never signs - it aggregates public nonces and partial
|
|
106
|
+
* signatures - so it is given a public key only, never a secret-bearing
|
|
107
|
+
* keypair (see ADR 038).
|
|
108
|
+
*/
|
|
109
|
+
publicKey: CompressedSecp256k1PublicKey;
|
|
104
110
|
/**
|
|
105
111
|
* Maximum canonicalized byte-length of a signed update body accepted by the
|
|
106
112
|
* service. Submissions above this cap are silently dropped and surfaced as
|
|
@@ -122,15 +128,15 @@ export interface AggregationServiceParams {
|
|
|
122
128
|
*/
|
|
123
129
|
export class AggregationService {
|
|
124
130
|
readonly did: string;
|
|
125
|
-
readonly
|
|
131
|
+
readonly publicKey: CompressedSecp256k1PublicKey;
|
|
126
132
|
readonly maxUpdateSizeBytes: number;
|
|
127
133
|
|
|
128
134
|
/** Per-cohort state, keyed by cohortId. */
|
|
129
135
|
#cohortStates: Map<string, ServiceCohortState> = new Map();
|
|
130
136
|
|
|
131
|
-
constructor({ did,
|
|
137
|
+
constructor({ did, publicKey, maxUpdateSizeBytes }: AggregationServiceParams) {
|
|
132
138
|
this.did = did;
|
|
133
|
-
this.
|
|
139
|
+
this.publicKey = publicKey;
|
|
134
140
|
this.maxUpdateSizeBytes = maxUpdateSizeBytes ?? DEFAULT_MAX_UPDATE_SIZE_BYTES;
|
|
135
141
|
}
|
|
136
142
|
|
|
@@ -234,7 +240,7 @@ export class AggregationService {
|
|
|
234
240
|
cohortSize : state.config.minParticipants,
|
|
235
241
|
beaconType : state.config.beaconType,
|
|
236
242
|
network : state.config.network,
|
|
237
|
-
communicationPk : this.
|
|
243
|
+
communicationPk : this.publicKey.compressed,
|
|
238
244
|
});
|
|
239
245
|
|
|
240
246
|
state.phase = ServiceCohortPhase.Advertised;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { SchnorrKeyPair } from '@did-btcr2/keypair';
|
|
2
|
+
import { wipe } from '@did-btcr2/keypair';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A signing capability for MuSig2 aggregation.
|
|
6
|
+
*
|
|
7
|
+
* MuSig2 (BIP-327) cannot be driven through a generic `sign(message)` primitive:
|
|
8
|
+
* both nonce generation and partial signing need the raw 32-byte secret scalar
|
|
9
|
+
* (see ADR 038). Rather than hand that scalar around as a long-lived field, the
|
|
10
|
+
* participant holds an `AggregationSigner` and materializes the secret only for
|
|
11
|
+
* the duration of a single operation via {@link AggregationSigner.withSecret},
|
|
12
|
+
* which is responsible for wiping its working copy afterward.
|
|
13
|
+
*
|
|
14
|
+
* This is the seam a non-extractable / KMS-backed signer (or a session-scoped
|
|
15
|
+
* signer that destroys its key after the cohort completes) plugs into without
|
|
16
|
+
* changing the participant state machine.
|
|
17
|
+
*
|
|
18
|
+
* @interface AggregationSigner
|
|
19
|
+
*/
|
|
20
|
+
export interface AggregationSigner {
|
|
21
|
+
/** Compressed 33-byte public key. Not secret; always available. */
|
|
22
|
+
readonly publicKey: Uint8Array;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Materialize the raw 32-byte secret key, pass it to `fn`, and zeroize the
|
|
26
|
+
* working copy before returning - even if `fn` throws. The secret must not
|
|
27
|
+
* escape the callback.
|
|
28
|
+
*
|
|
29
|
+
* @typeParam T The callback's return type (e.g. a nonce contribution or partial signature).
|
|
30
|
+
* @param {(secretKey: Uint8Array) => T} fn Operation that needs the raw secret for its duration.
|
|
31
|
+
* @returns {T} Whatever `fn` returns.
|
|
32
|
+
*/
|
|
33
|
+
withSecret<T>(fn: (secretKey: Uint8Array) => T): T;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* {@link AggregationSigner} backed by an in-memory {@link SchnorrKeyPair}.
|
|
38
|
+
*
|
|
39
|
+
* The keypair is held privately (never exposed as a public field) and each
|
|
40
|
+
* `withSecret` call pulls a fresh copy of the secret bytes, hands it to the
|
|
41
|
+
* callback, and wipes that copy on return. The underlying keypair is the
|
|
42
|
+
* caller's to own and destroy; this signer never mutates or destroys it.
|
|
43
|
+
*
|
|
44
|
+
* @class KeyPairAggregationSigner
|
|
45
|
+
* @implements {AggregationSigner}
|
|
46
|
+
*/
|
|
47
|
+
export class KeyPairAggregationSigner implements AggregationSigner {
|
|
48
|
+
readonly publicKey: Uint8Array;
|
|
49
|
+
readonly #keys: SchnorrKeyPair;
|
|
50
|
+
|
|
51
|
+
/** @param {SchnorrKeyPair} keys The keypair whose secret backs this signer. */
|
|
52
|
+
constructor(keys: SchnorrKeyPair) {
|
|
53
|
+
this.#keys = keys;
|
|
54
|
+
this.publicKey = keys.publicKey.compressed;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
withSecret<T>(fn: (secretKey: Uint8Array) => T): T {
|
|
58
|
+
// `.bytes` returns a fresh copy; wipe it once the operation completes so the
|
|
59
|
+
// raw scalar does not linger on the heap beyond a single MuSig2 step.
|
|
60
|
+
const secretKey = this.#keys.secretKey.bytes;
|
|
61
|
+
try {
|
|
62
|
+
return fn(secretKey);
|
|
63
|
+
} finally {
|
|
64
|
+
wipe(secretKey);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { wipe } from '@did-btcr2/keypair';
|
|
1
2
|
import type { Transaction } from '@scure/btc-signer';
|
|
2
3
|
import { SigHash } from '@scure/btc-signer';
|
|
3
4
|
import * as musig2 from '@scure/btc-signer/musig2';
|
|
@@ -58,8 +59,14 @@ export class BeaconSigningSession {
|
|
|
58
59
|
/** Current signing session phase. */
|
|
59
60
|
public phase: SigningSessionPhaseType;
|
|
60
61
|
|
|
61
|
-
/**
|
|
62
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Participant's MuSig2 secret nonce, held only on the participant side
|
|
64
|
+
* between {@link generateNonceContribution} and {@link generatePartialSignature}.
|
|
65
|
+
* Private and cleared on every terminal path (success, failure, or teardown
|
|
66
|
+
* via {@link clearSecrets}) so a spent or abandoned nonce cannot be reused or
|
|
67
|
+
* serialized.
|
|
68
|
+
*/
|
|
69
|
+
#secretNonce?: Uint8Array;
|
|
63
70
|
|
|
64
71
|
constructor({ id, cohort, pendingTx, prevOutScripts, prevOutValues }: SigningSessionParams) {
|
|
65
72
|
this.id = id || crypto.randomUUID();
|
|
@@ -225,7 +232,7 @@ export class BeaconSigningSession {
|
|
|
225
232
|
public generateNonceContribution(participantPublicKey: Uint8Array, participantSecretKey: Uint8Array): Uint8Array {
|
|
226
233
|
const aggPublicKey = musig2.keyAggExport(musig2.keyAggregate(this.cohort.cohortKeys));
|
|
227
234
|
const nonces = musig2.nonceGen(participantPublicKey, participantSecretKey, aggPublicKey);
|
|
228
|
-
this
|
|
235
|
+
this.#secretNonce = nonces.secret;
|
|
229
236
|
return nonces.public;
|
|
230
237
|
}
|
|
231
238
|
|
|
@@ -233,15 +240,17 @@ export class BeaconSigningSession {
|
|
|
233
240
|
* Generates a partial signature using the participant's secret key + secret nonce.
|
|
234
241
|
* Requires the aggregated nonce to have been set first (via the service).
|
|
235
242
|
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
243
|
+
* Clears the stored secret nonce after use on every path (success or throw)
|
|
244
|
+
* via {@link clearSecrets}. JS cannot truly erase memory (GC may relocate
|
|
245
|
+
* buffers), but overwriting the bytes shortens the exposure window and
|
|
246
|
+
* prevents accidental reuse or serialization of a spent nonce - reuse of a
|
|
247
|
+
* MuSig2 nonce leaks the secret key.
|
|
239
248
|
*/
|
|
240
249
|
public generatePartialSignature(participantSecretKey: Uint8Array): Uint8Array {
|
|
241
250
|
if(!this.aggregatedNonce) {
|
|
242
251
|
throw new SigningSessionError('Aggregated nonce not available.', 'MISSING_AGGREGATED_NONCE');
|
|
243
252
|
}
|
|
244
|
-
if(!this
|
|
253
|
+
if(!this.#secretNonce) {
|
|
245
254
|
throw new SigningSessionError('Secret nonce not available — generateNonceContribution() must be called first.', 'MISSING_SECRET_NONCE');
|
|
246
255
|
}
|
|
247
256
|
const session = new musig2.Session(
|
|
@@ -251,10 +260,24 @@ export class BeaconSigningSession {
|
|
|
251
260
|
[this.cohort.tapTweak],
|
|
252
261
|
[true]
|
|
253
262
|
);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
263
|
+
try {
|
|
264
|
+
return session.sign(this.#secretNonce, participantSecretKey);
|
|
265
|
+
} finally {
|
|
266
|
+
this.clearSecrets();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Zeroize any retained secret nonce. Safe to call repeatedly and on any path
|
|
272
|
+
* (completion, failure, or teardown of an abandoned session). Callers that
|
|
273
|
+
* drop a session before it reaches a partial signature should invoke this so
|
|
274
|
+
* the secret nonce does not linger on the live object.
|
|
275
|
+
*/
|
|
276
|
+
public clearSecrets(): void {
|
|
277
|
+
if(this.#secretNonce) {
|
|
278
|
+
wipe(this.#secretNonce);
|
|
279
|
+
this.#secretNonce = undefined;
|
|
280
|
+
}
|
|
258
281
|
}
|
|
259
282
|
|
|
260
283
|
public isComplete(): boolean {
|
|
@@ -5,7 +5,8 @@ import type { MessageHandler, Transport } from './transport.js';
|
|
|
5
5
|
|
|
6
6
|
/** Internal registration for a single actor sharing an {@link InMemoryTransport}. */
|
|
7
7
|
interface ActorEntry {
|
|
8
|
-
|
|
8
|
+
/** Compressed communication public key. The in-process bus does no encryption, so no secret is retained. */
|
|
9
|
+
publicKey: Uint8Array;
|
|
9
10
|
handlers: Map<string, MessageHandler>;
|
|
10
11
|
}
|
|
11
12
|
|
|
@@ -97,11 +98,14 @@ export class InMemoryTransport implements Transport {
|
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
registerActor(did: string, keys: SchnorrKeyPair): void {
|
|
100
|
-
|
|
101
|
+
// In-process delivery does no signing or encryption (same trust domain), so
|
|
102
|
+
// retain only the public key - the secret-bearing keypair is never stored in
|
|
103
|
+
// the transport registry (see ADR 038).
|
|
104
|
+
this.#actors.set(did, { publicKey: keys.publicKey.compressed, handlers: new Map() });
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
getActorPk(did: string): Uint8Array | undefined {
|
|
104
|
-
return this.#actors.get(did)?.
|
|
108
|
+
return this.#actors.get(did)?.publicKey;
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
/** True if `did` is registered on this transport. Used by the bus for routing. */
|