@interop/did-method-peer 0.0.0-stage → 0.1.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/LICENSE.md +20 -0
- package/README.md +350 -2
- package/dist/DidPeerDriver.d.ts +223 -0
- package/dist/DidPeerDriver.d.ts.map +1 -0
- package/dist/DidPeerDriver.js +647 -0
- package/dist/DidPeerDriver.js.map +1 -0
- package/dist/codec.d.ts +8 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +7 -0
- package/dist/codec.js.map +1 -0
- package/dist/constants.d.ts +35 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +40 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/inputDocument.d.ts +71 -0
- package/dist/inputDocument.d.ts.map +1 -0
- package/dist/inputDocument.js +192 -0
- package/dist/inputDocument.js.map +1 -0
- package/dist/numalgo4.d.ts +137 -0
- package/dist/numalgo4.d.ts.map +1 -0
- package/dist/numalgo4.js +309 -0
- package/dist/numalgo4.js.map +1 -0
- package/dist/resources.d.ts +27 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +47 -0
- package/dist/resources.js.map +1 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +95 -4
package/LICENSE.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Interop Alliance
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,351 @@
|
|
|
1
|
-
#
|
|
1
|
+
# did:peer:4 (numalgo 4) driver _(@interop/did-method-peer)_
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/interop-alliance/did-method-peer/actions?query=workflow%3A%22CI%22)
|
|
4
|
+
[](https://npm.im/@interop/did-method-peer)
|
|
5
|
+
|
|
6
|
+
> A [`did:peer`](https://identity.foundation/peer-did-method-spec/) numalgo 4
|
|
7
|
+
> codec and driver for `@interop/did-io`, for use in the browser, Node.js, and
|
|
8
|
+
> React Native.
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
- [Background](#background)
|
|
13
|
+
- [Security](#security)
|
|
14
|
+
- [Install](#install)
|
|
15
|
+
- [Usage](#usage)
|
|
16
|
+
- [Contribute](#contribute)
|
|
17
|
+
- [License](#license)
|
|
18
|
+
|
|
19
|
+
## Background
|
|
20
|
+
|
|
21
|
+
The [peer DID method](https://identity.foundation/peer-did-method-spec/) defines
|
|
22
|
+
several algorithms, called numalgos.
|
|
23
|
+
[Numalgo 4](https://identity.foundation/peer-did-method-spec/#method-4-short-form-and-long-form)
|
|
24
|
+
is the current one. It is statically resolvable. The DID carries everything
|
|
25
|
+
needed to resolve it, and no registry or network is involved.
|
|
26
|
+
|
|
27
|
+
A numalgo 4 DID starts from an input document. The input document looks like the
|
|
28
|
+
eventual DID document with four differences:
|
|
29
|
+
|
|
30
|
+
- it has no root `id`;
|
|
31
|
+
- every identifier defined in it is relative (`#key-0`);
|
|
32
|
+
- every reference to a resource in it is relative;
|
|
33
|
+
- a verification method omits `controller` when the controller is the document
|
|
34
|
+
owner.
|
|
35
|
+
|
|
36
|
+
To encode it, the input document is serialized as JSON without whitespace,
|
|
37
|
+
prefixed with the multicodec `json` code, and encoded as base58btc multibase.
|
|
38
|
+
That string is the encoded document. The SHA2-256 hash of the encoded document
|
|
39
|
+
string, as a multihash in base58btc, is the hash. Then:
|
|
40
|
+
|
|
41
|
+
- The long form is `did:peer:4{hash}:{encoded document}`. It carries the whole
|
|
42
|
+
document and resolves with no other input.
|
|
43
|
+
- The short form is `did:peer:4{hash}`. It is shorter, but it can only be
|
|
44
|
+
resolved by a party that has already seen the long form.
|
|
45
|
+
|
|
46
|
+
DIDComm v2 agents send the long form at first contact and the short form in
|
|
47
|
+
every message after that.
|
|
48
|
+
|
|
49
|
+
This package provides:
|
|
50
|
+
|
|
51
|
+
- A pure codec: `encodeLongForm`, `encodeShortForm`, `decode`, `longToShort`,
|
|
52
|
+
`resolveLongForm` and `resolveShortForm`, plus the form checks and regexes.
|
|
53
|
+
- A `@interop/did-io` driver with `generate()`, `get()`, `fromKeyPair()`,
|
|
54
|
+
`publicKeyToDidDoc()` and `publicMethodFor()`.
|
|
55
|
+
- Input document validation.
|
|
56
|
+
- A `LongFormStore` contract for resolving short forms.
|
|
57
|
+
|
|
58
|
+
Out of scope:
|
|
59
|
+
|
|
60
|
+
- Numalgos 0, 1, 2 and 3. `get()` rejects those DIDs with `DIDResolutionError`
|
|
61
|
+
`methodNotSupported`.
|
|
62
|
+
- A DID Resolution `resolveDID()` method on the driver. `CachedResolver` adapts
|
|
63
|
+
`get()` into a resolution result.
|
|
64
|
+
- A persistent long form store. The driver defines the contract and ships no
|
|
65
|
+
store of its own.
|
|
66
|
+
|
|
67
|
+
## Security
|
|
68
|
+
|
|
69
|
+
- The hash protects against corruption. It does not protect against a hostile
|
|
70
|
+
sender, because the sender computes the hash over a document of their choice.
|
|
71
|
+
A valid hash proves the document is the one the DID names. It does not prove
|
|
72
|
+
the document is trustworthy.
|
|
73
|
+
- `decode` rejects any long form longer than `MAX_LONG_FORM_LENGTH` (4096
|
|
74
|
+
characters) before hashing or decoding it. This stops a multi-megabyte DID
|
|
75
|
+
string from blocking the event loop. The limit is not configurable.
|
|
76
|
+
- Resolution does not validate the input document beyond a structural guard. A
|
|
77
|
+
hash-valid document with an unusable shape fails with `DIDResolutionError`
|
|
78
|
+
`invalidDid`.
|
|
79
|
+
- `alsoKnownAs` entries other than the one the library appends, and any
|
|
80
|
+
`controller` value naming a different DID, are claims made by the
|
|
81
|
+
counterparty. The library passes them through and does not verify them.
|
|
82
|
+
- A document's `@context` is supplied by the counterparty. Remote context
|
|
83
|
+
fetching is the consumer's documentLoader policy.
|
|
84
|
+
|
|
85
|
+
## Install
|
|
86
|
+
|
|
87
|
+
- Node.js 24+ is recommended.
|
|
88
|
+
|
|
89
|
+
### PNPM
|
|
90
|
+
|
|
91
|
+
To install via PNPM:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
pnpm install @interop/did-method-peer
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Development
|
|
98
|
+
|
|
99
|
+
To install locally (for development):
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
git clone https://github.com/interop-alliance/did-method-peer.git
|
|
103
|
+
cd did-method-peer
|
|
104
|
+
pnpm install
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Usage
|
|
108
|
+
|
|
109
|
+
### With did-io
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
import { CachedResolver } from '@interop/did-io'
|
|
113
|
+
import { Ed25519VerificationKey } from '@interop/ed25519-verification-key'
|
|
114
|
+
import * as didPeer from '@interop/did-method-peer'
|
|
115
|
+
|
|
116
|
+
const didPeerDriver = didPeer.driver({ longFormStore: new Map() })
|
|
117
|
+
didPeerDriver.use({
|
|
118
|
+
keyPairClass: Ed25519VerificationKey,
|
|
119
|
+
enableEncryptionKeyDerivation: true
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
const resolver = new CachedResolver()
|
|
123
|
+
resolver.use(didPeerDriver)
|
|
124
|
+
|
|
125
|
+
const { didDocument, keyPairs, methodFor } = await didPeerDriver.generate()
|
|
126
|
+
|
|
127
|
+
// Long form, resolved offline
|
|
128
|
+
await resolver.get({ did: didDocument.id })
|
|
129
|
+
|
|
130
|
+
// Key node, id is `${longForm}#z6Mk...`
|
|
131
|
+
const signingKey = methodFor({ purpose: 'authentication' })
|
|
132
|
+
await resolver.get({ url: signingKey.id })
|
|
133
|
+
|
|
134
|
+
// Short form, resolved through the long form store
|
|
135
|
+
await resolver.get({ did: didDocument.alsoKnownAs[0] })
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
`resolver.generate({ method: 'peer' })` also works, because the driver exposes
|
|
139
|
+
`generate`.
|
|
140
|
+
|
|
141
|
+
### Codec only
|
|
142
|
+
|
|
143
|
+
An application that only needs to encode and decode DIDs imports the `./codec`
|
|
144
|
+
subpath. It does not load the driver or any key suite.
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
import {
|
|
148
|
+
encodeLongForm,
|
|
149
|
+
decode,
|
|
150
|
+
resolveLongForm,
|
|
151
|
+
longToShort
|
|
152
|
+
} from '@interop/did-method-peer/codec'
|
|
153
|
+
|
|
154
|
+
const inputDocument = {
|
|
155
|
+
'@context': ['https://www.w3.org/ns/did/v1'],
|
|
156
|
+
verificationMethod: [
|
|
157
|
+
{
|
|
158
|
+
id: '#key-0',
|
|
159
|
+
type: 'Multikey',
|
|
160
|
+
publicKeyMultibase: 'z6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V'
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
authentication: ['#key-0']
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const longForm = encodeLongForm({ inputDocument })
|
|
167
|
+
const shortForm = longToShort({ did: longForm })
|
|
168
|
+
|
|
169
|
+
// The input document as it was encoded, relative ids intact
|
|
170
|
+
const decoded = decode({ did: longForm })
|
|
171
|
+
|
|
172
|
+
// The contextualized DID document, relative references left relative
|
|
173
|
+
const didDocument = resolveLongForm({ did: longForm })
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`encodeLongForm` and `encodeShortForm` do not validate. The codec functions are
|
|
177
|
+
synchronous. They throw `DIDResolutionError` with `code: 'invalidDid'` for
|
|
178
|
+
malformed input.
|
|
179
|
+
|
|
180
|
+
### Creating a DID with `generate()`
|
|
181
|
+
|
|
182
|
+
`generate()` takes one of three mutually exclusive entry paths. It returns
|
|
183
|
+
`{ didDocument, keyPairs, methodFor }`. `didDocument.id` is the long form.
|
|
184
|
+
|
|
185
|
+
- `inputDocument`. The library validates, encodes and resolves a document you
|
|
186
|
+
built. An optional `keyPairs` map, keyed by bare fragment, binds live key
|
|
187
|
+
pairs to methods in that document. A pair whose public key differs from the
|
|
188
|
+
document's method is a `TypeError`. A method serialized without
|
|
189
|
+
`publicKeyMultibase` (a 2018 suite's `publicKeyBase58`, a JSON Web Key) is
|
|
190
|
+
read through the pair's registered suite for the comparison.
|
|
191
|
+
- `seed`. A `Uint8Array` of exactly 32 bytes, or a string in `z1A...` form. The
|
|
192
|
+
library generates one key pair from it and proceeds as in the third path. The
|
|
193
|
+
same seed always yields the same DID.
|
|
194
|
+
- Nothing, or explicit key pairs. With no options, the library generates one
|
|
195
|
+
random key pair with the registered suite. Otherwise you pass
|
|
196
|
+
`verificationKeyPair`, `keyAgreementKeyPair`, `verificationMethods` and
|
|
197
|
+
`service`.
|
|
198
|
+
|
|
199
|
+
Details of the third path:
|
|
200
|
+
|
|
201
|
+
- The verification key gets the purposes `authentication`, `assertionMethod`,
|
|
202
|
+
`capabilityDelegation` and `capabilityInvocation`.
|
|
203
|
+
- The default fragment is the key's `fingerprint()`, so a method id looks like
|
|
204
|
+
`#z6Mk...`. A `verificationMethods` entry of the form
|
|
205
|
+
`{ keyPair, fragment, purposes }` overrides the fragment and the purposes.
|
|
206
|
+
- A `verificationMethods` entry can also be a third-party method. Pass a string
|
|
207
|
+
DID URL, or a full verification method object whose `controller` names the
|
|
208
|
+
other DID. It is placed as given and gets the four default purposes. To give
|
|
209
|
+
it other purposes, wrap it as `{ method, purposes }`.
|
|
210
|
+
- With `enableEncryptionKeyDerivation`, an Ed25519 key also yields a derived
|
|
211
|
+
`X25519KeyAgreementKey2020` key under `keyAgreement`. Derivation is opt-in,
|
|
212
|
+
because it uses one key's material for both signing and encryption.
|
|
213
|
+
- `service` is copied as given.
|
|
214
|
+
|
|
215
|
+
Mixing paths is a `TypeError`. For example, `inputDocument` together with
|
|
216
|
+
`seed`, or `seed` together with `verificationKeyPair`.
|
|
217
|
+
|
|
218
|
+
`methodFor({ purpose })` returns the live key pair for that purpose. It throws
|
|
219
|
+
when no method serves the purpose, and when the method has no live pair.
|
|
220
|
+
|
|
221
|
+
### The long form store
|
|
222
|
+
|
|
223
|
+
A short form is only a hash. To resolve one, the driver must have seen the long
|
|
224
|
+
form it belongs to. The spec says a short form cannot be resolved without first
|
|
225
|
+
seeing and storing its long form.
|
|
226
|
+
|
|
227
|
+
`LongFormStore` is that memory. It maps a short form to its long form:
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
interface LongFormStore {
|
|
231
|
+
get(shortForm: string): string | undefined | Promise<string | undefined>
|
|
232
|
+
set(shortForm: string, longForm: string): unknown
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
A plain `Map<string, string>` satisfies it. A persistent store (IndexedDB, a
|
|
237
|
+
wallet's encrypted storage) implements the same two methods and returns
|
|
238
|
+
promises. The driver awaits both methods. There is no default store. Without
|
|
239
|
+
one, every short form fails with `DIDResolutionError` `notFound`.
|
|
240
|
+
|
|
241
|
+
The driver uses the store like this:
|
|
242
|
+
|
|
243
|
+
- `generate()` always writes the new DID's pair, so the creator can resolve its
|
|
244
|
+
own short form.
|
|
245
|
+
- `get({ did })` with a long form writes the pair after a successful resolution,
|
|
246
|
+
when `rememberOnResolve` is on.
|
|
247
|
+
- `get({ did })` with a short form reads the store. It never writes.
|
|
248
|
+
- `publicKeyToDidDoc()` does not write.
|
|
249
|
+
|
|
250
|
+
`rememberOnResolve` is a constructor option and defaults to `true`. DIDComm
|
|
251
|
+
clients keep the default, since the counterparty sends its long form once. A
|
|
252
|
+
verifier that only fetches key nodes for signature checks should set it to
|
|
253
|
+
`false`. Otherwise every key fetch is a durable write, and a store failure would
|
|
254
|
+
fail the verification.
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
const verifierDriver = didPeer.driver({
|
|
258
|
+
longFormStore: store,
|
|
259
|
+
rememberOnResolve: false
|
|
260
|
+
})
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
An application that wants to gate contact acceptance resolves with
|
|
264
|
+
`rememberOnResolve: false`. It writes `store.set(longToShort({ did }), did)`
|
|
265
|
+
itself when it accepts the contact.
|
|
266
|
+
|
|
267
|
+
A stored long form is checked on the way out. `decode` checks it against its own
|
|
268
|
+
hash. The driver then checks that the long form's short form equals the one
|
|
269
|
+
requested. A corrupt or mis-keyed entry fails with `invalidDid`. A store that
|
|
270
|
+
throws or rejects fails with `internalError`, with the store error as `cause`. A
|
|
271
|
+
failed store write in a long form `get()` also fails with `internalError`.
|
|
272
|
+
|
|
273
|
+
Store growth. A store that writes on sight gains one entry per distinct long
|
|
274
|
+
form resolved. Any counterparty can mint unlimited distinct long forms. A
|
|
275
|
+
persistent store is therefore unbounded by construction. The store implementer
|
|
276
|
+
owns bounds and eviction.
|
|
277
|
+
|
|
278
|
+
The long form store is separate from the `CachedResolver` cache. That cache
|
|
279
|
+
memoizes successful results by exact DID string for a few seconds. It knows
|
|
280
|
+
nothing about short and long forms.
|
|
281
|
+
|
|
282
|
+
### Errors
|
|
283
|
+
|
|
284
|
+
| condition | thrown |
|
|
285
|
+
| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
286
|
+
| bad argument shape (`did` missing, both `inputDocument` and `seed`, ...) | `TypeError` |
|
|
287
|
+
| not `did:peer:` | `DIDResolutionError` `methodNotSupported` |
|
|
288
|
+
| `did:peer:` with numalgo other than 4 | `DIDResolutionError` `methodNotSupported` |
|
|
289
|
+
| fails the long form regex, hash mismatch, bad multibase or multicodec prefix, non-object JSON | `DIDResolutionError` `invalidDid` |
|
|
290
|
+
| short form with no stored long form | `DIDResolutionError` `notFound` |
|
|
291
|
+
| stored long form's short form differs from the one requested | `DIDResolutionError` `invalidDid` |
|
|
292
|
+
| store `get` throws or rejects | `DIDResolutionError` `internalError`, store error as `cause` |
|
|
293
|
+
| store `set` rejects in `generate()` | `generate()` rejects with the store error as `cause` |
|
|
294
|
+
| store `set` rejects in `get()` (long form) | `DIDResolutionError` `internalError`, store error as `cause` |
|
|
295
|
+
| hash-valid document whose shape contextualization cannot handle | `DIDResolutionError` `invalidDid` |
|
|
296
|
+
| long form over `MAX_LONG_FORM_LENGTH` | `DIDResolutionError` `invalidDid` |
|
|
297
|
+
| more than one `#` in a DID URL | `DIDResolutionError` `invalidDidUrl` |
|
|
298
|
+
| `?query` or `/path` in a DID URL | stripped, and the authority resolves (no error) |
|
|
299
|
+
| fragment not in document | `VerificationMethodError` `notFound` |
|
|
300
|
+
| input document fails `validateInputDocument` | `TypeError` with the failing path in the message |
|
|
301
|
+
| unsupported multibase header, no or multiple suites | plain `Error` |
|
|
302
|
+
|
|
303
|
+
No `TypeError` leaves resolution. A counterparty's document can only produce
|
|
304
|
+
`DIDResolutionError`s. Match on `name` and `code`.
|
|
305
|
+
|
|
306
|
+
`DIDResolutionError` comes from `@interop/data-integrity-core`. `CachedResolver`
|
|
307
|
+
(did-io 4.3.0 and later) recognizes it by `name` and `code`, so a duplicated
|
|
308
|
+
copy of that package in your dependency tree does not change the envelope.
|
|
309
|
+
|
|
310
|
+
### Notes for implementers
|
|
311
|
+
|
|
312
|
+
- Absolute and relative references. The driver returns absolute DID URLs. The
|
|
313
|
+
document from `get({ did })` and `generate()`, and the node from
|
|
314
|
+
`get({ url })`, use ids like `${did}#z6Mk...`. This is what the ecosystem's
|
|
315
|
+
verifiers compare against. The codec functions `resolveLongForm` and
|
|
316
|
+
`resolveShortForm` keep relative references, as the spec does.
|
|
317
|
+
- Use the long form in proofs. A node fetched by long form URL carries the long
|
|
318
|
+
form in `id` and `controller`. The same node fetched by short form URL carries
|
|
319
|
+
the short form. No verifier in the ecosystem treats `alsoKnownAs` as an
|
|
320
|
+
equivalence. A proof naming one form and a capability controller naming the
|
|
321
|
+
other fails authorization. Put the long form in `proof.verificationMethod` and
|
|
322
|
+
in zcap `controller` fields. It resolves with no store, and it survives a lost
|
|
323
|
+
store.
|
|
324
|
+
- `MAX_LONG_FORM_LENGTH` is 4096 characters, the longest base58 string the
|
|
325
|
+
`@scure/base` codec decodes. It is exported so callers can see it. The encoder
|
|
326
|
+
has its own limit of 2048 bytes of input; a larger input document fails
|
|
327
|
+
`encodeLongForm` and `generate()` with an `Error` naming the size.
|
|
328
|
+
- Non-ASCII interop. The library serializes the input document with
|
|
329
|
+
`JSON.stringify`, which emits non-ASCII characters raw. Python's `json.dumps`
|
|
330
|
+
escapes them by default. An identical input document containing non-ASCII text
|
|
331
|
+
therefore yields a different long form from the Python library. Resolution is
|
|
332
|
+
unaffected, because the hash covers the bytes the creator produced. Only
|
|
333
|
+
independent re-derivation of the same DID from the same document differs.
|
|
334
|
+
`JSON.stringify` also emits integer-like keys first and serializes floats
|
|
335
|
+
differently from Python. Published test vectors are all ASCII.
|
|
336
|
+
- DID URL queries. A `?query` or `/path` is stripped and ignored. A `?service=`
|
|
337
|
+
URL resolves to the document, or to the node named by its fragment.
|
|
338
|
+
- The library does not canonicalize the input document. Key order and formatting
|
|
339
|
+
are preserved as given.
|
|
340
|
+
|
|
341
|
+
## Contribute
|
|
342
|
+
|
|
343
|
+
PRs accepted. See [CONTRIBUTING.md](CONTRIBUTING.md) for editor setup (Prettier,
|
|
344
|
+
ESLint, and EditorConfig) and how it maps to CI.
|
|
345
|
+
|
|
346
|
+
If editing the Readme, please conform to the
|
|
347
|
+
[standard-readme](https://github.com/RichardLitt/standard-readme) specification.
|
|
348
|
+
|
|
349
|
+
## License
|
|
350
|
+
|
|
351
|
+
[MIT License](LICENSE.md) © 2026 Interop Alliance.
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import type { DidGenerationResult, DidMethodDriver } from '@interop/did-io';
|
|
2
|
+
import { type AbstractKeyPair, type IDIDDocument, type IKeyPair, type IPublicKey, type IVerificationMethodEntry } from '@interop/data-integrity-core';
|
|
3
|
+
import type { DidPeerDriverOptions, FromMultibase, InputDocument, KeyPairClass, LongFormStore, RegisteredKeyType } from './types.js';
|
|
4
|
+
type LiveEntry = {
|
|
5
|
+
keyPair: AbstractKeyPair | IKeyPair;
|
|
6
|
+
fragment?: string;
|
|
7
|
+
purposes?: string[];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A third-party method with explicit purposes. A bare
|
|
11
|
+
* `IVerificationMethodEntry` gets the four default purposes instead.
|
|
12
|
+
*/
|
|
13
|
+
type ThirdPartyEntry = {
|
|
14
|
+
method: IVerificationMethodEntry;
|
|
15
|
+
purposes: string[];
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* `generate()` options. The three entry paths are mutually exclusive:
|
|
19
|
+
* `inputDocument` (with optional `keyPairs`), `seed`, or explicit key pairs.
|
|
20
|
+
*/
|
|
21
|
+
export interface GenerateOptions {
|
|
22
|
+
inputDocument?: InputDocument;
|
|
23
|
+
keyPairs?: Map<string, AbstractKeyPair>;
|
|
24
|
+
seed?: string | Uint8Array;
|
|
25
|
+
keyType?: KeyPairClass | string;
|
|
26
|
+
verificationKeyPair?: AbstractKeyPair | IKeyPair;
|
|
27
|
+
keyAgreementKeyPair?: AbstractKeyPair | IKeyPair;
|
|
28
|
+
verificationMethods?: Array<LiveEntry | ThirdPartyEntry | IVerificationMethodEntry>;
|
|
29
|
+
service?: object[];
|
|
30
|
+
}
|
|
31
|
+
export declare class DidPeerDriver implements DidMethodDriver {
|
|
32
|
+
method: string;
|
|
33
|
+
longFormStore?: LongFormStore;
|
|
34
|
+
rememberOnResolve: boolean;
|
|
35
|
+
_allowedKeyTypes: Map<string, RegisteredKeyType>;
|
|
36
|
+
/**
|
|
37
|
+
* @param options {object}
|
|
38
|
+
* @param [options.longFormStore] {LongFormStore} - Where long forms are
|
|
39
|
+
* remembered so short forms resolve. A `Map` works for in-memory use.
|
|
40
|
+
* Without one, every short form is `notFound`.
|
|
41
|
+
* @param [options.rememberOnResolve=true] {boolean} - Whether a successful
|
|
42
|
+
* long form `get()` writes the store. `generate()` always writes.
|
|
43
|
+
*/
|
|
44
|
+
constructor({ longFormStore, rememberOnResolve }?: DidPeerDriverOptions);
|
|
45
|
+
/**
|
|
46
|
+
* Registers a key suite for generation and key rebuilding.
|
|
47
|
+
*
|
|
48
|
+
* Preferred form: pass a `keyPairClass` exposing static `multibaseHeader`,
|
|
49
|
+
* `from` and (optionally) `generate`. Lower-level form: pass
|
|
50
|
+
* `multibaseMultikeyHeader` plus a `fromMultibase` deserializer; suites
|
|
51
|
+
* registered this way cannot `generate()`.
|
|
52
|
+
*
|
|
53
|
+
* @param options {object}
|
|
54
|
+
* @param [options.keyPairClass] {KeyPairClass}
|
|
55
|
+
* @param [options.multibaseMultikeyHeader] {string}
|
|
56
|
+
* @param [options.fromMultibase] {FromMultibase}
|
|
57
|
+
* @param [options.enableEncryptionKeyDerivation=false] {boolean} - When
|
|
58
|
+
* `true`, generating with this (Ed25519) suite also derives an X25519
|
|
59
|
+
* `keyAgreement` key from the verification key. Off by default, since
|
|
60
|
+
* reusing one key for signing and key agreement is a posture choice.
|
|
61
|
+
*/
|
|
62
|
+
use({ keyPairClass, multibaseMultikeyHeader, fromMultibase, enableEncryptionKeyDerivation }?: {
|
|
63
|
+
keyPairClass?: KeyPairClass;
|
|
64
|
+
multibaseMultikeyHeader?: string;
|
|
65
|
+
fromMultibase?: FromMultibase;
|
|
66
|
+
enableEncryptionKeyDerivation?: boolean;
|
|
67
|
+
}): void;
|
|
68
|
+
/**
|
|
69
|
+
* Generates a did:peer numalgo 4 DID. Three mutually exclusive entry paths:
|
|
70
|
+
*
|
|
71
|
+
* 1. `inputDocument`: validated, encoded and resolved as given. `keyPairs`
|
|
72
|
+
* (keyed by bare fragment) binds live pairs to the document's methods;
|
|
73
|
+
* a fragment with no method, or a public key that differs, is a
|
|
74
|
+
* `TypeError`.
|
|
75
|
+
* 2. `seed`: one key pair is derived with the selected or sole registered
|
|
76
|
+
* suite, then as path 3.
|
|
77
|
+
* 3. Explicit or random key pairs: `verificationKeyPair` (or a fresh random
|
|
78
|
+
* pair) gets the four default purposes; `keyAgreementKeyPair` gets
|
|
79
|
+
* `keyAgreement`, or an X25519 key is derived when the suite was
|
|
80
|
+
* registered with `enableEncryptionKeyDerivation`; `verificationMethods`
|
|
81
|
+
* entries (`{ keyPair, fragment?, purposes? }`, `{ method, purposes }`
|
|
82
|
+
* for a third-party method with explicit purposes, or a bare third-party
|
|
83
|
+
* reference / method object with the default four) are added in order;
|
|
84
|
+
* `service` is copied.
|
|
85
|
+
*
|
|
86
|
+
* Every path binds the live pairs (`controller` and `id` set to the long
|
|
87
|
+
* form), writes the long form store when one is configured, and returns the
|
|
88
|
+
* did-io generation result. `didDocument.id` is the long form.
|
|
89
|
+
*
|
|
90
|
+
* @param [options] {GenerateOptions}
|
|
91
|
+
*
|
|
92
|
+
* @returns {Promise<DidGenerationResult>}
|
|
93
|
+
*/
|
|
94
|
+
generate(options?: GenerateOptions): Promise<DidGenerationResult>;
|
|
95
|
+
_generate({ inputDocument, keyPairs, seed, keyType, verificationKeyPair, keyAgreementKeyPair, verificationMethods, service }: GenerateOptions, { remember }: {
|
|
96
|
+
remember: boolean;
|
|
97
|
+
}): Promise<DidGenerationResult>;
|
|
98
|
+
/**
|
|
99
|
+
* Generates a did:peer DID from existing key pairs (live instances or plain
|
|
100
|
+
* public key descriptions, rebuilt through the registered suites). With only
|
|
101
|
+
* `keyAgreementKeyPair`, no signing key is generated.
|
|
102
|
+
*
|
|
103
|
+
* @param options {object}
|
|
104
|
+
* @param [options.verificationKeyPair] {AbstractKeyPair | IKeyPair}
|
|
105
|
+
* @param [options.keyAgreementKeyPair] {AbstractKeyPair | IKeyPair}
|
|
106
|
+
*
|
|
107
|
+
* @returns {Promise<DidGenerationResult>}
|
|
108
|
+
*/
|
|
109
|
+
fromKeyPair({ verificationKeyPair, keyAgreementKeyPair }?: {
|
|
110
|
+
verificationKeyPair?: AbstractKeyPair | IKeyPair;
|
|
111
|
+
keyAgreementKeyPair?: AbstractKeyPair | IKeyPair;
|
|
112
|
+
}): Promise<DidGenerationResult>;
|
|
113
|
+
/**
|
|
114
|
+
* Converts a public key description to a did:peer DID document. An X25519
|
|
115
|
+
* description becomes the `keyAgreement` key, anything else the
|
|
116
|
+
* verification key. Unlike `generate()`, no `keyPairs` map is returned and
|
|
117
|
+
* the long form store is not written.
|
|
118
|
+
*
|
|
119
|
+
* @param options {object}
|
|
120
|
+
* @param options.publicKeyDescription {AbstractKeyPair | IKeyPair | IPublicKey}
|
|
121
|
+
*
|
|
122
|
+
* @returns {Promise<{didDocument: IDIDDocument}>}
|
|
123
|
+
*/
|
|
124
|
+
publicKeyToDidDoc({ publicKeyDescription }?: {
|
|
125
|
+
publicKeyDescription?: AbstractKeyPair | IKeyPair | IPublicKey;
|
|
126
|
+
}): Promise<{
|
|
127
|
+
didDocument: IDIDDocument;
|
|
128
|
+
}>;
|
|
129
|
+
/**
|
|
130
|
+
* Resolves a did:peer numalgo 4 DID or DID URL. A long form resolves
|
|
131
|
+
* offline; a short form resolves through the long form store. A `#fragment`
|
|
132
|
+
* returns the verification method or service node with that id, cloned,
|
|
133
|
+
* with an `@context`. A `?query` or `/path` is stripped and the authority
|
|
134
|
+
* resolved.
|
|
135
|
+
*
|
|
136
|
+
* @param options {object}
|
|
137
|
+
* @param [options.did] {string}
|
|
138
|
+
* @param [options.url] {string} - Alias for `did`.
|
|
139
|
+
*
|
|
140
|
+
* @throws {DIDResolutionError} `methodNotSupported` for a non-did:peer DID
|
|
141
|
+
* or another numalgo; `invalidDid` for a malformed or tampered DID;
|
|
142
|
+
* `invalidDidUrl` for more than one `#`; `notFound` for a short form whose
|
|
143
|
+
* long form has not been seen; `internalError` for a store failure.
|
|
144
|
+
* @throws {VerificationMethodError} `notFound` for an unknown fragment.
|
|
145
|
+
*
|
|
146
|
+
* @returns {Promise<IDIDDocument | IPublicKey>}
|
|
147
|
+
*/
|
|
148
|
+
get({ did, url }?: {
|
|
149
|
+
did?: string;
|
|
150
|
+
url?: string;
|
|
151
|
+
[key: string]: unknown;
|
|
152
|
+
}): Promise<IDIDDocument | IPublicKey>;
|
|
153
|
+
/**
|
|
154
|
+
* Returns the public verification method for a purpose from a DID document
|
|
155
|
+
* returned by this driver (whose references are absolute), without
|
|
156
|
+
* `@context`.
|
|
157
|
+
*
|
|
158
|
+
* @param options {object}
|
|
159
|
+
* @param options.didDocument {IDIDDocument}
|
|
160
|
+
* @param options.purpose {string}
|
|
161
|
+
*
|
|
162
|
+
* @returns {IPublicKey}
|
|
163
|
+
*/
|
|
164
|
+
publicMethodFor({ didDocument, purpose }?: {
|
|
165
|
+
didDocument?: IDIDDocument;
|
|
166
|
+
purpose?: string;
|
|
167
|
+
}): IPublicKey;
|
|
168
|
+
/**
|
|
169
|
+
* Resolves a short form through the long form store: a missing store or a
|
|
170
|
+
* value that is not a non-empty string is `notFound`; a store that throws,
|
|
171
|
+
* or returns a string that is not a long form, is `internalError`; a stored
|
|
172
|
+
* long form whose own short form differs from the requested one is
|
|
173
|
+
* `invalidDid`. Does not write.
|
|
174
|
+
*/
|
|
175
|
+
_resolveShortForm(shortForm: string): Promise<IDIDDocument>;
|
|
176
|
+
/**
|
|
177
|
+
* Checks that a live pair's public key equals the input document's method
|
|
178
|
+
* `#${fragment}` before the pair is bound to it. A method carrying
|
|
179
|
+
* `publicKeyMultibase` is compared directly. Any other serialization (a 2018
|
|
180
|
+
* suite's `publicKeyBase58`, a JSON Web Key's `publicKeyJwk`) is rebuilt
|
|
181
|
+
* through the pair's own registered suite and compared by fingerprint, so
|
|
182
|
+
* the suite decides which serializations it can read.
|
|
183
|
+
*/
|
|
184
|
+
_assertPairMatchesMethod({ inputDocument, fragment, pair }: {
|
|
185
|
+
inputDocument: InputDocument;
|
|
186
|
+
fragment: string;
|
|
187
|
+
pair: AbstractKeyPair;
|
|
188
|
+
}): Promise<void>;
|
|
189
|
+
/**
|
|
190
|
+
* Returns a live pair's public key as a multibase Multikey string: its
|
|
191
|
+
* `publicKeyMultibase`, or its `fingerprint()` for a suite that serializes
|
|
192
|
+
* the public key another way.
|
|
193
|
+
*/
|
|
194
|
+
_publicKeyMultibaseOf(pair: AbstractKeyPair): string;
|
|
195
|
+
/**
|
|
196
|
+
* Derives an X25519 key agreement pair from a live Ed25519 pair when its
|
|
197
|
+
* suite was registered with `enableEncryptionKeyDerivation`; otherwise
|
|
198
|
+
* returns `undefined`.
|
|
199
|
+
*/
|
|
200
|
+
_deriveKeyAgreementKey(verificationPair: AbstractKeyPair): AbstractKeyPair | undefined;
|
|
201
|
+
/**
|
|
202
|
+
* Generates a key pair from the selected or sole registered suite.
|
|
203
|
+
*/
|
|
204
|
+
_generateKeyPair({ seed, keyType }?: {
|
|
205
|
+
seed?: string | Uint8Array;
|
|
206
|
+
keyType?: KeyPairClass | string;
|
|
207
|
+
}): Promise<AbstractKeyPair>;
|
|
208
|
+
/**
|
|
209
|
+
* Returns a live key pair for a live instance or a plain public key
|
|
210
|
+
* description (rebuilt through the registered suite for its header).
|
|
211
|
+
*/
|
|
212
|
+
_toKeyPair(description: AbstractKeyPair | IKeyPair | IPublicKey): Promise<AbstractKeyPair>;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Factory matching the `.driver()` API of the other did-io drivers.
|
|
216
|
+
*
|
|
217
|
+
* @param [options] {DidPeerDriverOptions}
|
|
218
|
+
*
|
|
219
|
+
* @returns {DidPeerDriver}
|
|
220
|
+
*/
|
|
221
|
+
export declare function driver(options?: DidPeerDriverOptions): DidPeerDriver;
|
|
222
|
+
export {};
|
|
223
|
+
//# sourceMappingURL=DidPeerDriver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DidPeerDriver.d.ts","sourceRoot":"","sources":["../src/DidPeerDriver.ts"],"names":[],"mappings":"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"}
|