@did-btcr2/method 0.50.0 → 0.51.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/README.md +13 -13
- package/dist/.tsbuildinfo +1 -1
- package/dist/browser.js +3 -41554
- package/dist/browser.mjs +3 -41548
- package/dist/cjs/index.js +46 -17
- package/dist/esm/core/did-sender-resolver.js +74 -9
- package/dist/esm/core/did-sender-resolver.js.map +1 -1
- package/dist/types/core/did-sender-resolver.d.ts +49 -9
- package/dist/types/core/did-sender-resolver.d.ts.map +1 -1
- package/package.json +6 -20
- package/src/core/did-sender-resolver.ts +90 -9
package/dist/cjs/index.js
CHANGED
|
@@ -61,6 +61,7 @@ __export(index_exports, {
|
|
|
61
61
|
deriveSingletonAddress: () => deriveSingletonAddress,
|
|
62
62
|
detectSingletonScriptKind: () => detectSingletonScriptKind,
|
|
63
63
|
extractOpReturnSignal: () => extractOpReturnSignal,
|
|
64
|
+
getAggregationCommunicationKey: () => getAggregationCommunicationKey,
|
|
64
65
|
isMultikeyVerificationMethod: () => isMultikeyVerificationMethod,
|
|
65
66
|
opReturnScript: () => opReturnScript,
|
|
66
67
|
resolveBtcr2SenderPk: () => resolveBtcr2SenderPk,
|
|
@@ -1285,23 +1286,15 @@ var BeaconSignalDiscovery = class {
|
|
|
1285
1286
|
};
|
|
1286
1287
|
|
|
1287
1288
|
// src/core/did-sender-resolver.ts
|
|
1288
|
-
var
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
const components = Identifier.decode(did);
|
|
1292
|
-
if (components.idType === "KEY") {
|
|
1293
|
-
return new import_keypair2.CompressedSecp256k1PublicKey(components.genesisBytes);
|
|
1294
|
-
}
|
|
1295
|
-
} catch {
|
|
1296
|
-
}
|
|
1297
|
-
return void 0;
|
|
1298
|
-
}
|
|
1289
|
+
var import_common13 = require("@did-btcr2/common");
|
|
1290
|
+
var import_cryptosuite3 = require("@did-btcr2/cryptosuite");
|
|
1291
|
+
var import_keypair4 = require("@did-btcr2/keypair");
|
|
1299
1292
|
|
|
1300
1293
|
// src/core/resolver.ts
|
|
1301
1294
|
var import_bitcoin5 = require("@did-btcr2/bitcoin");
|
|
1302
1295
|
var import_common12 = require("@did-btcr2/common");
|
|
1303
1296
|
var import_cryptosuite2 = require("@did-btcr2/cryptosuite");
|
|
1304
|
-
var
|
|
1297
|
+
var import_keypair3 = require("@did-btcr2/keypair");
|
|
1305
1298
|
|
|
1306
1299
|
// src/did-btcr2.ts
|
|
1307
1300
|
var import_common11 = require("@did-btcr2/common");
|
|
@@ -1314,7 +1307,7 @@ var import_cryptosuite = require("@did-btcr2/cryptosuite");
|
|
|
1314
1307
|
// src/utils/did-document.ts
|
|
1315
1308
|
var import_bitcoin4 = require("@did-btcr2/bitcoin");
|
|
1316
1309
|
var import_common9 = require("@did-btcr2/common");
|
|
1317
|
-
var
|
|
1310
|
+
var import_keypair2 = require("@did-btcr2/keypair");
|
|
1318
1311
|
var import_utils4 = require("@web5/dids/utils");
|
|
1319
1312
|
var import_btc_signer3 = require("@scure/btc-signer");
|
|
1320
1313
|
var BTCR2_DID_DOCUMENT_CONTEXT = [
|
|
@@ -1652,7 +1645,7 @@ var GenesisDocument = class _GenesisDocument extends DidDocument {
|
|
|
1652
1645
|
* @returns {GenesisDocument} A new GenesisDocument with the placeholder ID.
|
|
1653
1646
|
*/
|
|
1654
1647
|
static fromPublicKey(publicKey, network) {
|
|
1655
|
-
const pk = new
|
|
1648
|
+
const pk = new import_keypair2.CompressedSecp256k1PublicKey(publicKey);
|
|
1656
1649
|
const id = ID_PLACEHOLDER_VALUE;
|
|
1657
1650
|
const address = (0, import_btc_signer3.p2pkh)(pk.compressed, (0, import_bitcoin4.getNetwork)(network)).address;
|
|
1658
1651
|
const services = [{
|
|
@@ -2212,7 +2205,7 @@ var Resolver = class _Resolver {
|
|
|
2212
2205
|
static deterministic(didComponents) {
|
|
2213
2206
|
const genesisBytes = didComponents.genesisBytes;
|
|
2214
2207
|
const did = Identifier.encode(genesisBytes, didComponents);
|
|
2215
|
-
const { multibase } = new
|
|
2208
|
+
const { multibase } = new import_keypair3.CompressedSecp256k1PublicKey(genesisBytes);
|
|
2216
2209
|
const service = BeaconUtils.generateBeaconServices({
|
|
2217
2210
|
id: did,
|
|
2218
2211
|
publicKey: genesisBytes,
|
|
@@ -2635,13 +2628,48 @@ var Resolver = class _Resolver {
|
|
|
2635
2628
|
}
|
|
2636
2629
|
};
|
|
2637
2630
|
|
|
2631
|
+
// src/core/did-sender-resolver.ts
|
|
2632
|
+
function getAggregationCommunicationKey(document) {
|
|
2633
|
+
const invocation = document.capabilityInvocation?.[0];
|
|
2634
|
+
if (invocation === void 0) {
|
|
2635
|
+
throw new import_common13.DidDocumentError(
|
|
2636
|
+
"Cannot derive aggregation communication key: capabilityInvocation is absent",
|
|
2637
|
+
import_common13.INVALID_DID_DOCUMENT,
|
|
2638
|
+
{ id: document.id }
|
|
2639
|
+
);
|
|
2640
|
+
}
|
|
2641
|
+
const vm = typeof invocation === "string" ? document.verificationMethod?.find((method) => method.id === invocation) : invocation;
|
|
2642
|
+
if (!vm) {
|
|
2643
|
+
throw new import_common13.DidDocumentError(
|
|
2644
|
+
`Cannot derive aggregation communication key: capabilityInvocation[0] "${invocation}" does not resolve to a verification method`,
|
|
2645
|
+
import_common13.INVALID_DID_DOCUMENT,
|
|
2646
|
+
{ id: document.id, invocation }
|
|
2647
|
+
);
|
|
2648
|
+
}
|
|
2649
|
+
return import_cryptosuite3.SchnorrMultikey.fromVerificationMethod(vm).publicKey;
|
|
2650
|
+
}
|
|
2651
|
+
function resolveBtcr2SenderPk(did, opts) {
|
|
2652
|
+
try {
|
|
2653
|
+
const components = Identifier.decode(did);
|
|
2654
|
+
if (components.idType === "KEY") {
|
|
2655
|
+
return new import_keypair4.CompressedSecp256k1PublicKey(components.genesisBytes);
|
|
2656
|
+
}
|
|
2657
|
+
if (opts?.genesisDocument) {
|
|
2658
|
+
const document = Resolver.external(components, opts.genesisDocument);
|
|
2659
|
+
return getAggregationCommunicationKey(document);
|
|
2660
|
+
}
|
|
2661
|
+
} catch {
|
|
2662
|
+
}
|
|
2663
|
+
return void 0;
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2638
2666
|
// src/utils/did-document-builder.ts
|
|
2639
|
-
var
|
|
2667
|
+
var import_common14 = require("@did-btcr2/common");
|
|
2640
2668
|
var DidDocumentBuilder = class {
|
|
2641
2669
|
document = {};
|
|
2642
2670
|
constructor(initialDocument) {
|
|
2643
2671
|
if (!initialDocument.id) {
|
|
2644
|
-
throw new
|
|
2672
|
+
throw new import_common14.DidDocumentError('Missing required "id" property', import_common14.INVALID_DID_DOCUMENT, initialDocument);
|
|
2645
2673
|
}
|
|
2646
2674
|
this.document.id = initialDocument.id;
|
|
2647
2675
|
this.document.verificationMethod = initialDocument.verificationMethod ?? [];
|
|
@@ -2732,6 +2760,7 @@ var DidDocumentBuilder = class {
|
|
|
2732
2760
|
deriveSingletonAddress,
|
|
2733
2761
|
detectSingletonScriptKind,
|
|
2734
2762
|
extractOpReturnSignal,
|
|
2763
|
+
getAggregationCommunicationKey,
|
|
2735
2764
|
isMultikeyVerificationMethod,
|
|
2736
2765
|
opReturnScript,
|
|
2737
2766
|
resolveBtcr2SenderPk,
|
|
@@ -1,25 +1,90 @@
|
|
|
1
|
+
import { DidDocumentError, INVALID_DID_DOCUMENT } from '@did-btcr2/common';
|
|
2
|
+
import { SchnorrMultikey } from '@did-btcr2/cryptosuite';
|
|
1
3
|
import { CompressedSecp256k1PublicKey } from '@did-btcr2/keypair';
|
|
2
4
|
import { Identifier } from './identifier.js';
|
|
5
|
+
import { Resolver } from './resolver.js';
|
|
6
|
+
/**
|
|
7
|
+
* Derive the aggregation communication public key from a resolved did:btcr2 DID Document.
|
|
8
|
+
*
|
|
9
|
+
* The communication key is the verification method referenced by
|
|
10
|
+
* `capabilityInvocation[0]`, resolved to its public key. This is the exact relationship
|
|
11
|
+
* the method already enforces for DID updates (construct and sign require the signing
|
|
12
|
+
* method to be in `capabilityInvocation`, the update proof is built and verified with
|
|
13
|
+
* `proofPurpose: 'capabilityInvocation'`), so binding the transport communication key to
|
|
14
|
+
* it yields the invariant "transport-authenticated as D implies authorized to update D."
|
|
15
|
+
* For a KEY (`k1`) document the single deterministic key is already the sole
|
|
16
|
+
* `capabilityInvocation` entry, so this is a no-op for KEY DIDs.
|
|
17
|
+
*
|
|
18
|
+
* There is deliberately no `verificationMethod[0]` fallback: a document without
|
|
19
|
+
* `capabilityInvocation` cannot be updated at all, so it is useless for aggregation and is
|
|
20
|
+
* rejected here rather than bound to an unrelated key.
|
|
21
|
+
*
|
|
22
|
+
* @param {DidDocument} document The resolved DID Document (placeholder id already replaced).
|
|
23
|
+
* @returns {CompressedSecp256k1PublicKey} The compressed public key of the communication method.
|
|
24
|
+
* @throws {DidDocumentError} If `capabilityInvocation` is absent or its first entry does not
|
|
25
|
+
* resolve to a verification method in the document.
|
|
26
|
+
*/
|
|
27
|
+
export function getAggregationCommunicationKey(document) {
|
|
28
|
+
const invocation = document.capabilityInvocation?.[0];
|
|
29
|
+
if (invocation === undefined) {
|
|
30
|
+
throw new DidDocumentError('Cannot derive aggregation communication key: capabilityInvocation is absent', INVALID_DID_DOCUMENT, { id: document.id });
|
|
31
|
+
}
|
|
32
|
+
// Resolve capabilityInvocation[0] to a verification method: a string reference is
|
|
33
|
+
// dereferenced by id against verificationMethod; an embedded method is used directly.
|
|
34
|
+
// A local id lookup is used rather than getSigningMethod, which defaults to #initialKey
|
|
35
|
+
// and does not resolve embedded methods.
|
|
36
|
+
const vm = typeof invocation === 'string'
|
|
37
|
+
? document.verificationMethod?.find(method => method.id === invocation)
|
|
38
|
+
: invocation;
|
|
39
|
+
if (!vm) {
|
|
40
|
+
throw new DidDocumentError(`Cannot derive aggregation communication key: capabilityInvocation[0] "${invocation}" `
|
|
41
|
+
+ 'does not resolve to a verification method', INVALID_DID_DOCUMENT, { id: document.id, invocation });
|
|
42
|
+
}
|
|
43
|
+
return SchnorrMultikey.fromVerificationMethod(vm).publicKey;
|
|
44
|
+
}
|
|
3
45
|
/**
|
|
4
46
|
* Resolve a did:btcr2 sender's communication public key from its DID, for the
|
|
5
|
-
* aggregation HTTP transport's `resolveSenderPk` option
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
47
|
+
* aggregation HTTP transport's `resolveSenderPk` option.
|
|
48
|
+
*
|
|
49
|
+
* A KEY (`k1`) identifier decodes directly to its genesis public key: the DID string is
|
|
50
|
+
* the key. An EXTERNAL (`x1`) identifier is a commitment to the hash of a genesis
|
|
51
|
+
* document, so there is no key in the DID string. When the genesis document is supplied
|
|
52
|
+
* in-band (via `opts.genesisDocument`), it is self-verifying against the DID: `Resolver`'s
|
|
53
|
+
* external path recomputes its canonical hash, compares it to the identifier's genesis
|
|
54
|
+
* bytes (throwing on mismatch), and resolves it, after which the communication key is
|
|
55
|
+
* derived from `capabilityInvocation[0]` ({@link getAggregationCommunicationKey}). Without
|
|
56
|
+
* a genesis document, an `x1` DID still resolves to `undefined`, so callers that pass no
|
|
57
|
+
* second argument behave exactly as before.
|
|
9
58
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
59
|
+
* The aggregation transport is DID-method-agnostic and does not name `Identifier`; method
|
|
60
|
+
* supplies this resolver when it wires the transport so a sender that is not a
|
|
61
|
+
* pre-registered peer can still be authenticated from its DID.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} did The sender's DID.
|
|
64
|
+
* @param {object} [opts] Optional resolution inputs.
|
|
65
|
+
* @param {object} [opts.genesisDocument] The `x1` sender's genesis document, carried in-band
|
|
66
|
+
* on the bootstrap opt-in. Ignored for KEY identifiers.
|
|
67
|
+
* @returns {CompressedSecp256k1PublicKey | undefined} The sender's compressed public key, or
|
|
68
|
+
* `undefined` when the DID is not a decodable did:btcr2 identifier, is an `x1` identifier
|
|
69
|
+
* with no (or a non-matching) genesis document, or has no usable communication key.
|
|
13
70
|
*/
|
|
14
|
-
export function resolveBtcr2SenderPk(did) {
|
|
71
|
+
export function resolveBtcr2SenderPk(did, opts) {
|
|
15
72
|
try {
|
|
16
73
|
const components = Identifier.decode(did);
|
|
17
74
|
if (components.idType === 'KEY') {
|
|
18
75
|
return new CompressedSecp256k1PublicKey(components.genesisBytes);
|
|
19
76
|
}
|
|
77
|
+
// EXTERNAL (x1): the DID commits to the hash of a genesis document. With the genesis
|
|
78
|
+
// supplied in-band, verify it hashes to the DID and derive the communication key from
|
|
79
|
+
// it; without it, there is no key to return.
|
|
80
|
+
if (opts?.genesisDocument) {
|
|
81
|
+
const document = Resolver.external(components, opts.genesisDocument);
|
|
82
|
+
return getAggregationCommunicationKey(document);
|
|
83
|
+
}
|
|
20
84
|
}
|
|
21
85
|
catch {
|
|
22
|
-
// Not a decodable did:btcr2
|
|
86
|
+
// Not a decodable did:btcr2 identifier, the genesis does not hash to the DID, or the
|
|
87
|
+
// document has no usable capabilityInvocation communication key.
|
|
23
88
|
}
|
|
24
89
|
return undefined;
|
|
25
90
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"did-sender-resolver.js","sourceRoot":"","sources":["../../../src/core/did-sender-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"did-sender-resolver.js","sourceRoot":"","sources":["../../../src/core/did-sender-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAElE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,8BAA8B,CAC5C,QAAqB;IAErB,MAAM,UAAU,GAAG,QAAQ,CAAC,oBAAoB,EAAE,CAAC,CAAC,CAAC,CAAC;IACtD,IAAG,UAAU,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,IAAI,gBAAgB,CACxB,6EAA6E,EAC7E,oBAAoB,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,CAC1C,CAAC;IACJ,CAAC;IAED,kFAAkF;IAClF,sFAAsF;IACtF,wFAAwF;IACxF,yCAAyC;IACzC,MAAM,EAAE,GAAsC,OAAO,UAAU,KAAK,QAAQ;QAC1E,CAAC,CAAC,QAAQ,CAAC,kBAAkB,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,KAAK,UAAU,CAAC;QACvE,CAAC,CAAC,UAAU,CAAC;IAEf,IAAG,CAAC,EAAE,EAAE,CAAC;QACP,MAAM,IAAI,gBAAgB,CACxB,yEAAyE,UAAU,IAAI;cACnF,2CAA2C,EAC/C,oBAAoB,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,CACtD,CAAC;IACJ,CAAC;IAED,OAAO,eAAe,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,oBAAoB,CAClC,GAAW,EACX,IAAmC;IAEnC,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAG,UAAU,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YAC/B,OAAO,IAAI,4BAA4B,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QACnE,CAAC;QACD,qFAAqF;QACrF,sFAAsF;QACtF,6CAA6C;QAC7C,IAAG,IAAI,EAAE,eAAe,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;YACrE,OAAO,8BAA8B,CAAC,QAAQ,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,qFAAqF;QACrF,iEAAiE;IACnE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -1,14 +1,54 @@
|
|
|
1
1
|
import { CompressedSecp256k1PublicKey } from '@did-btcr2/keypair';
|
|
2
|
+
import type { DidDocument } from '../utils/did-document.js';
|
|
3
|
+
/**
|
|
4
|
+
* Derive the aggregation communication public key from a resolved did:btcr2 DID Document.
|
|
5
|
+
*
|
|
6
|
+
* The communication key is the verification method referenced by
|
|
7
|
+
* `capabilityInvocation[0]`, resolved to its public key. This is the exact relationship
|
|
8
|
+
* the method already enforces for DID updates (construct and sign require the signing
|
|
9
|
+
* method to be in `capabilityInvocation`, the update proof is built and verified with
|
|
10
|
+
* `proofPurpose: 'capabilityInvocation'`), so binding the transport communication key to
|
|
11
|
+
* it yields the invariant "transport-authenticated as D implies authorized to update D."
|
|
12
|
+
* For a KEY (`k1`) document the single deterministic key is already the sole
|
|
13
|
+
* `capabilityInvocation` entry, so this is a no-op for KEY DIDs.
|
|
14
|
+
*
|
|
15
|
+
* There is deliberately no `verificationMethod[0]` fallback: a document without
|
|
16
|
+
* `capabilityInvocation` cannot be updated at all, so it is useless for aggregation and is
|
|
17
|
+
* rejected here rather than bound to an unrelated key.
|
|
18
|
+
*
|
|
19
|
+
* @param {DidDocument} document The resolved DID Document (placeholder id already replaced).
|
|
20
|
+
* @returns {CompressedSecp256k1PublicKey} The compressed public key of the communication method.
|
|
21
|
+
* @throws {DidDocumentError} If `capabilityInvocation` is absent or its first entry does not
|
|
22
|
+
* resolve to a verification method in the document.
|
|
23
|
+
*/
|
|
24
|
+
export declare function getAggregationCommunicationKey(document: DidDocument): CompressedSecp256k1PublicKey;
|
|
2
25
|
/**
|
|
3
26
|
* Resolve a did:btcr2 sender's communication public key from its DID, for the
|
|
4
|
-
* aggregation HTTP transport's `resolveSenderPk` option
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
27
|
+
* aggregation HTTP transport's `resolveSenderPk` option.
|
|
28
|
+
*
|
|
29
|
+
* A KEY (`k1`) identifier decodes directly to its genesis public key: the DID string is
|
|
30
|
+
* the key. An EXTERNAL (`x1`) identifier is a commitment to the hash of a genesis
|
|
31
|
+
* document, so there is no key in the DID string. When the genesis document is supplied
|
|
32
|
+
* in-band (via `opts.genesisDocument`), it is self-verifying against the DID: `Resolver`'s
|
|
33
|
+
* external path recomputes its canonical hash, compares it to the identifier's genesis
|
|
34
|
+
* bytes (throwing on mismatch), and resolves it, after which the communication key is
|
|
35
|
+
* derived from `capabilityInvocation[0]` ({@link getAggregationCommunicationKey}). Without
|
|
36
|
+
* a genesis document, an `x1` DID still resolves to `undefined`, so callers that pass no
|
|
37
|
+
* second argument behave exactly as before.
|
|
38
|
+
*
|
|
39
|
+
* The aggregation transport is DID-method-agnostic and does not name `Identifier`; method
|
|
40
|
+
* supplies this resolver when it wires the transport so a sender that is not a
|
|
41
|
+
* pre-registered peer can still be authenticated from its DID.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} did The sender's DID.
|
|
44
|
+
* @param {object} [opts] Optional resolution inputs.
|
|
45
|
+
* @param {object} [opts.genesisDocument] The `x1` sender's genesis document, carried in-band
|
|
46
|
+
* on the bootstrap opt-in. Ignored for KEY identifiers.
|
|
47
|
+
* @returns {CompressedSecp256k1PublicKey | undefined} The sender's compressed public key, or
|
|
48
|
+
* `undefined` when the DID is not a decodable did:btcr2 identifier, is an `x1` identifier
|
|
49
|
+
* with no (or a non-matching) genesis document, or has no usable communication key.
|
|
12
50
|
*/
|
|
13
|
-
export declare function resolveBtcr2SenderPk(did: string
|
|
51
|
+
export declare function resolveBtcr2SenderPk(did: string, opts?: {
|
|
52
|
+
genesisDocument?: object;
|
|
53
|
+
}): CompressedSecp256k1PublicKey | undefined;
|
|
14
54
|
//# sourceMappingURL=did-sender-resolver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"did-sender-resolver.d.ts","sourceRoot":"","sources":["../../../src/core/did-sender-resolver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"did-sender-resolver.d.ts","sourceRoot":"","sources":["../../../src/core/did-sender-resolver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,4BAA4B,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAyB,MAAM,0BAA0B,CAAC;AAInF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,WAAW,GACpB,4BAA4B,CA0B9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GAClC,4BAA4B,GAAG,SAAS,CAkB1C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@did-btcr2/method",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.51.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",
|
|
@@ -66,49 +66,35 @@
|
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@noble/curves": "^1.9.7",
|
|
68
68
|
"@noble/hashes": "^1.8.0",
|
|
69
|
-
"@noble/secp256k1": "^2.3.0",
|
|
70
69
|
"@scure/base": "^1.2.6",
|
|
71
|
-
"@scure/bip32": "^1.7.0",
|
|
72
|
-
"@scure/bip39": "^1.6.0",
|
|
73
70
|
"@scure/btc-signer": "^1.8.1",
|
|
74
|
-
"@web5/common": "^1.1.0",
|
|
75
|
-
"@web5/crypto": "^1.0.6",
|
|
76
71
|
"@web5/dids": "^1.2.0",
|
|
77
|
-
"
|
|
78
|
-
"dotenv": "^16.6.1",
|
|
79
|
-
"nostr-tools": "^2.23.3",
|
|
80
|
-
"@did-btcr2/common": "^9.1.0",
|
|
81
|
-
"@did-btcr2/keypair": "^0.13.1",
|
|
72
|
+
"@did-btcr2/bitcoin": "^0.8.0",
|
|
82
73
|
"@did-btcr2/smt": "^0.3.0",
|
|
83
|
-
"@did-btcr2/
|
|
84
|
-
"@did-btcr2/
|
|
74
|
+
"@did-btcr2/keypair": "^0.13.1",
|
|
75
|
+
"@did-btcr2/common": "^9.1.0",
|
|
76
|
+
"@did-btcr2/cryptosuite": "^9.0.0"
|
|
85
77
|
},
|
|
86
78
|
"devDependencies": {
|
|
87
79
|
"@eslint/js": "^9.39.4",
|
|
88
80
|
"@types/chai": "^5.2.3",
|
|
89
|
-
"@types/chai-as-promised": "^8.0.2",
|
|
90
|
-
"@types/eslint": "^9.6.1",
|
|
91
81
|
"@types/mocha": "^10.0.10",
|
|
92
82
|
"@types/node": "^25.5.0",
|
|
93
83
|
"@typescript-eslint/eslint-plugin": "^8.58.0",
|
|
94
84
|
"@typescript-eslint/parser": "^8.58.0",
|
|
95
85
|
"c8": "^10.1.3",
|
|
96
86
|
"chai": "^5.3.3",
|
|
97
|
-
"chai-as-promised": "^8.0.2",
|
|
98
87
|
"commander": "^13.1.0",
|
|
99
88
|
"esbuild": "^0.24.2",
|
|
100
89
|
"eslint": "^9.39.4",
|
|
101
90
|
"eslint-plugin-mocha": "^10.5.0",
|
|
102
91
|
"globals": "^15.15.0",
|
|
103
92
|
"mocha": "^10.8.2",
|
|
104
|
-
"mocha-junit-reporter": "^2.2.1",
|
|
105
93
|
"multiformats": "^13.4.2",
|
|
106
94
|
"node-stdlib-browser": "^1.3.1",
|
|
107
95
|
"rimraf": "^6.1.3",
|
|
108
96
|
"tsup": "^8.5.1",
|
|
109
|
-
"
|
|
110
|
-
"typescript": "^5.9.3",
|
|
111
|
-
"typescript-eslint": "^8.58.0"
|
|
97
|
+
"typescript": "^5.9.3"
|
|
112
98
|
},
|
|
113
99
|
"scripts": {
|
|
114
100
|
"clean": "rimraf dist coverage tests/compiled",
|
|
@@ -1,25 +1,106 @@
|
|
|
1
|
+
import { DidDocumentError, INVALID_DID_DOCUMENT } from '@did-btcr2/common';
|
|
2
|
+
import { SchnorrMultikey } from '@did-btcr2/cryptosuite';
|
|
1
3
|
import { CompressedSecp256k1PublicKey } from '@did-btcr2/keypair';
|
|
4
|
+
import type { DidDocument, DidVerificationMethod } from '../utils/did-document.js';
|
|
2
5
|
import { Identifier } from './identifier.js';
|
|
6
|
+
import { Resolver } from './resolver.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Derive the aggregation communication public key from a resolved did:btcr2 DID Document.
|
|
10
|
+
*
|
|
11
|
+
* The communication key is the verification method referenced by
|
|
12
|
+
* `capabilityInvocation[0]`, resolved to its public key. This is the exact relationship
|
|
13
|
+
* the method already enforces for DID updates (construct and sign require the signing
|
|
14
|
+
* method to be in `capabilityInvocation`, the update proof is built and verified with
|
|
15
|
+
* `proofPurpose: 'capabilityInvocation'`), so binding the transport communication key to
|
|
16
|
+
* it yields the invariant "transport-authenticated as D implies authorized to update D."
|
|
17
|
+
* For a KEY (`k1`) document the single deterministic key is already the sole
|
|
18
|
+
* `capabilityInvocation` entry, so this is a no-op for KEY DIDs.
|
|
19
|
+
*
|
|
20
|
+
* There is deliberately no `verificationMethod[0]` fallback: a document without
|
|
21
|
+
* `capabilityInvocation` cannot be updated at all, so it is useless for aggregation and is
|
|
22
|
+
* rejected here rather than bound to an unrelated key.
|
|
23
|
+
*
|
|
24
|
+
* @param {DidDocument} document The resolved DID Document (placeholder id already replaced).
|
|
25
|
+
* @returns {CompressedSecp256k1PublicKey} The compressed public key of the communication method.
|
|
26
|
+
* @throws {DidDocumentError} If `capabilityInvocation` is absent or its first entry does not
|
|
27
|
+
* resolve to a verification method in the document.
|
|
28
|
+
*/
|
|
29
|
+
export function getAggregationCommunicationKey(
|
|
30
|
+
document: DidDocument,
|
|
31
|
+
): CompressedSecp256k1PublicKey {
|
|
32
|
+
const invocation = document.capabilityInvocation?.[0];
|
|
33
|
+
if(invocation === undefined) {
|
|
34
|
+
throw new DidDocumentError(
|
|
35
|
+
'Cannot derive aggregation communication key: capabilityInvocation is absent',
|
|
36
|
+
INVALID_DID_DOCUMENT, { id: document.id }
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Resolve capabilityInvocation[0] to a verification method: a string reference is
|
|
41
|
+
// dereferenced by id against verificationMethod; an embedded method is used directly.
|
|
42
|
+
// A local id lookup is used rather than getSigningMethod, which defaults to #initialKey
|
|
43
|
+
// and does not resolve embedded methods.
|
|
44
|
+
const vm: DidVerificationMethod | undefined = typeof invocation === 'string'
|
|
45
|
+
? document.verificationMethod?.find(method => method.id === invocation)
|
|
46
|
+
: invocation;
|
|
47
|
+
|
|
48
|
+
if(!vm) {
|
|
49
|
+
throw new DidDocumentError(
|
|
50
|
+
`Cannot derive aggregation communication key: capabilityInvocation[0] "${invocation}" `
|
|
51
|
+
+ 'does not resolve to a verification method',
|
|
52
|
+
INVALID_DID_DOCUMENT, { id: document.id, invocation }
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return SchnorrMultikey.fromVerificationMethod(vm).publicKey;
|
|
57
|
+
}
|
|
3
58
|
|
|
4
59
|
/**
|
|
5
60
|
* Resolve a did:btcr2 sender's communication public key from its DID, for the
|
|
6
|
-
* aggregation HTTP transport's `resolveSenderPk` option
|
|
7
|
-
* its genesis public key. The aggregation transport is DID-method-agnostic and does
|
|
8
|
-
* not name `Identifier`; method supplies this resolver when it wires the transport so
|
|
9
|
-
* a sender that is not a pre-registered peer can still be authenticated from its DID.
|
|
61
|
+
* aggregation HTTP transport's `resolveSenderPk` option.
|
|
10
62
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
63
|
+
* A KEY (`k1`) identifier decodes directly to its genesis public key: the DID string is
|
|
64
|
+
* the key. An EXTERNAL (`x1`) identifier is a commitment to the hash of a genesis
|
|
65
|
+
* document, so there is no key in the DID string. When the genesis document is supplied
|
|
66
|
+
* in-band (via `opts.genesisDocument`), it is self-verifying against the DID: `Resolver`'s
|
|
67
|
+
* external path recomputes its canonical hash, compares it to the identifier's genesis
|
|
68
|
+
* bytes (throwing on mismatch), and resolves it, after which the communication key is
|
|
69
|
+
* derived from `capabilityInvocation[0]` ({@link getAggregationCommunicationKey}). Without
|
|
70
|
+
* a genesis document, an `x1` DID still resolves to `undefined`, so callers that pass no
|
|
71
|
+
* second argument behave exactly as before.
|
|
72
|
+
*
|
|
73
|
+
* The aggregation transport is DID-method-agnostic and does not name `Identifier`; method
|
|
74
|
+
* supplies this resolver when it wires the transport so a sender that is not a
|
|
75
|
+
* pre-registered peer can still be authenticated from its DID.
|
|
76
|
+
*
|
|
77
|
+
* @param {string} did The sender's DID.
|
|
78
|
+
* @param {object} [opts] Optional resolution inputs.
|
|
79
|
+
* @param {object} [opts.genesisDocument] The `x1` sender's genesis document, carried in-band
|
|
80
|
+
* on the bootstrap opt-in. Ignored for KEY identifiers.
|
|
81
|
+
* @returns {CompressedSecp256k1PublicKey | undefined} The sender's compressed public key, or
|
|
82
|
+
* `undefined` when the DID is not a decodable did:btcr2 identifier, is an `x1` identifier
|
|
83
|
+
* with no (or a non-matching) genesis document, or has no usable communication key.
|
|
14
84
|
*/
|
|
15
|
-
export function resolveBtcr2SenderPk(
|
|
85
|
+
export function resolveBtcr2SenderPk(
|
|
86
|
+
did: string,
|
|
87
|
+
opts?: { genesisDocument?: object },
|
|
88
|
+
): CompressedSecp256k1PublicKey | undefined {
|
|
16
89
|
try {
|
|
17
90
|
const components = Identifier.decode(did);
|
|
18
91
|
if(components.idType === 'KEY') {
|
|
19
92
|
return new CompressedSecp256k1PublicKey(components.genesisBytes);
|
|
20
93
|
}
|
|
94
|
+
// EXTERNAL (x1): the DID commits to the hash of a genesis document. With the genesis
|
|
95
|
+
// supplied in-band, verify it hashes to the DID and derive the communication key from
|
|
96
|
+
// it; without it, there is no key to return.
|
|
97
|
+
if(opts?.genesisDocument) {
|
|
98
|
+
const document = Resolver.external(components, opts.genesisDocument);
|
|
99
|
+
return getAggregationCommunicationKey(document);
|
|
100
|
+
}
|
|
21
101
|
} catch {
|
|
22
|
-
// Not a decodable did:btcr2
|
|
102
|
+
// Not a decodable did:btcr2 identifier, the genesis does not hash to the DID, or the
|
|
103
|
+
// document has no usable capabilityInvocation communication key.
|
|
23
104
|
}
|
|
24
105
|
return undefined;
|
|
25
106
|
}
|