@openvtc/pnm-core 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/README.md +129 -0
- package/dist/did/derive-signing-key.d.ts +19 -0
- package/dist/did/derive-signing-key.d.ts.map +1 -0
- package/dist/did/derive-signing-key.js +96 -0
- package/dist/did/derive-signing-key.js.map +1 -0
- package/dist/did/index.d.ts +5 -0
- package/dist/did/index.d.ts.map +1 -0
- package/dist/did/index.js +5 -0
- package/dist/did/index.js.map +1 -0
- package/dist/did/peer.d.ts +37 -0
- package/dist/did/peer.d.ts.map +1 -0
- package/dist/did/peer.js +49 -0
- package/dist/did/peer.js.map +1 -0
- package/dist/did/verification-method.d.ts +43 -0
- package/dist/did/verification-method.d.ts.map +1 -0
- package/dist/did/verification-method.js +32 -0
- package/dist/did/verification-method.js.map +1 -0
- package/dist/did/verify.d.ts +49 -0
- package/dist/did/verify.d.ts.map +1 -0
- package/dist/did/verify.js +89 -0
- package/dist/did/verify.js.map +1 -0
- package/dist/didcomm/index.d.ts +235 -0
- package/dist/didcomm/index.d.ts.map +1 -0
- package/dist/didcomm/index.js +415 -0
- package/dist/didcomm/index.js.map +1 -0
- package/dist/inbound/confirm.d.ts +50 -0
- package/dist/inbound/confirm.d.ts.map +1 -0
- package/dist/inbound/confirm.js +64 -0
- package/dist/inbound/confirm.js.map +1 -0
- package/dist/inbound/dedup.d.ts +9 -0
- package/dist/inbound/dedup.d.ts.map +1 -0
- package/dist/inbound/dedup.js +31 -0
- package/dist/inbound/dedup.js.map +1 -0
- package/dist/inbound/index.d.ts +3 -0
- package/dist/inbound/index.d.ts.map +1 -0
- package/dist/inbound/index.js +3 -0
- package/dist/inbound/index.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/onboarding/index.d.ts +2 -0
- package/dist/onboarding/index.d.ts.map +1 -0
- package/dist/onboarding/index.js +2 -0
- package/dist/onboarding/index.js.map +1 -0
- package/dist/onboarding/swap.d.ts +60 -0
- package/dist/onboarding/swap.d.ts.map +1 -0
- package/dist/onboarding/swap.js +148 -0
- package/dist/onboarding/swap.js.map +1 -0
- package/dist/provision/adopt.d.ts +31 -0
- package/dist/provision/adopt.d.ts.map +1 -0
- package/dist/provision/adopt.js +114 -0
- package/dist/provision/adopt.js.map +1 -0
- package/dist/provision/armor.d.ts +19 -0
- package/dist/provision/armor.d.ts.map +1 -0
- package/dist/provision/armor.js +243 -0
- package/dist/provision/armor.js.map +1 -0
- package/dist/provision/crc24.d.ts +5 -0
- package/dist/provision/crc24.d.ts.map +1 -0
- package/dist/provision/crc24.js +30 -0
- package/dist/provision/crc24.js.map +1 -0
- package/dist/provision/hpke.d.ts +17 -0
- package/dist/provision/hpke.d.ts.map +1 -0
- package/dist/provision/hpke.js +60 -0
- package/dist/provision/hpke.js.map +1 -0
- package/dist/provision/index.d.ts +10 -0
- package/dist/provision/index.d.ts.map +1 -0
- package/dist/provision/index.js +16 -0
- package/dist/provision/index.js.map +1 -0
- package/dist/provision/open.d.ts +28 -0
- package/dist/provision/open.d.ts.map +1 -0
- package/dist/provision/open.js +224 -0
- package/dist/provision/open.js.map +1 -0
- package/dist/provision/request.d.ts +65 -0
- package/dist/provision/request.d.ts.map +1 -0
- package/dist/provision/request.js +53 -0
- package/dist/provision/request.js.map +1 -0
- package/dist/provision/run.d.ts +76 -0
- package/dist/provision/run.d.ts.map +1 -0
- package/dist/provision/run.js +110 -0
- package/dist/provision/run.js.map +1 -0
- package/dist/provision/send.d.ts +85 -0
- package/dist/provision/send.d.ts.map +1 -0
- package/dist/provision/send.js +87 -0
- package/dist/provision/send.js.map +1 -0
- package/dist/provision/types.d.ts +110 -0
- package/dist/provision/types.d.ts.map +1 -0
- package/dist/provision/types.js +17 -0
- package/dist/provision/types.js.map +1 -0
- package/dist/rp-login/didcomm.d.ts +34 -0
- package/dist/rp-login/didcomm.d.ts.map +1 -0
- package/dist/rp-login/didcomm.js +72 -0
- package/dist/rp-login/didcomm.js.map +1 -0
- package/dist/rp-login/index.d.ts +3 -0
- package/dist/rp-login/index.d.ts.map +1 -0
- package/dist/rp-login/index.js +3 -0
- package/dist/rp-login/index.js.map +1 -0
- package/dist/rp-login/step-up.d.ts +43 -0
- package/dist/rp-login/step-up.d.ts.map +1 -0
- package/dist/rp-login/step-up.js +118 -0
- package/dist/rp-login/step-up.js.map +1 -0
- package/dist/siop/index.d.ts +3 -0
- package/dist/siop/index.d.ts.map +1 -0
- package/dist/siop/index.js +3 -0
- package/dist/siop/index.js.map +1 -0
- package/dist/siop/login-client.d.ts +29 -0
- package/dist/siop/login-client.d.ts.map +1 -0
- package/dist/siop/login-client.js +79 -0
- package/dist/siop/login-client.js.map +1 -0
- package/dist/siop/self-issued.d.ts +96 -0
- package/dist/siop/self-issued.d.ts.map +1 -0
- package/dist/siop/self-issued.js +162 -0
- package/dist/siop/self-issued.js.map +1 -0
- package/dist/store/holder-identity.d.ts +241 -0
- package/dist/store/holder-identity.d.ts.map +1 -0
- package/dist/store/holder-identity.js +441 -0
- package/dist/store/holder-identity.js.map +1 -0
- package/dist/store/index.d.ts +4 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +4 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/kv-store.d.ts +51 -0
- package/dist/store/kv-store.d.ts.map +1 -0
- package/dist/store/kv-store.js +100 -0
- package/dist/store/kv-store.js.map +1 -0
- package/dist/store/secret-wrap.d.ts +109 -0
- package/dist/store/secret-wrap.d.ts.map +1 -0
- package/dist/store/secret-wrap.js +85 -0
- package/dist/store/secret-wrap.js.map +1 -0
- package/dist/trust-tasks/index.d.ts +2 -0
- package/dist/trust-tasks/index.d.ts.map +1 -0
- package/dist/trust-tasks/index.js +2 -0
- package/dist/trust-tasks/index.js.map +1 -0
- package/dist/trust-tasks/sign.d.ts +31 -0
- package/dist/trust-tasks/sign.d.ts.map +1 -0
- package/dist/trust-tasks/sign.js +141 -0
- package/dist/trust-tasks/sign.js.map +1 -0
- package/dist/util/timing.d.ts +14 -0
- package/dist/util/timing.d.ts.map +1 -0
- package/dist/util/timing.js +20 -0
- package/dist/util/timing.js.map +1 -0
- package/dist/vault/delete.d.ts +19 -0
- package/dist/vault/delete.d.ts.map +1 -0
- package/dist/vault/delete.js +35 -0
- package/dist/vault/delete.js.map +1 -0
- package/dist/vault/index.d.ts +8 -0
- package/dist/vault/index.d.ts.map +1 -0
- package/dist/vault/index.js +7 -0
- package/dist/vault/index.js.map +1 -0
- package/dist/vault/list.d.ts +96 -0
- package/dist/vault/list.d.ts.map +1 -0
- package/dist/vault/list.js +106 -0
- package/dist/vault/list.js.map +1 -0
- package/dist/vault/proxy-login.d.ts +100 -0
- package/dist/vault/proxy-login.d.ts.map +1 -0
- package/dist/vault/proxy-login.js +106 -0
- package/dist/vault/proxy-login.js.map +1 -0
- package/dist/vault/release.d.ts +33 -0
- package/dist/vault/release.d.ts.map +1 -0
- package/dist/vault/release.js +83 -0
- package/dist/vault/release.js.map +1 -0
- package/dist/vault/sign-trust-task.d.ts +26 -0
- package/dist/vault/sign-trust-task.d.ts.map +1 -0
- package/dist/vault/sign-trust-task.js +53 -0
- package/dist/vault/sign-trust-task.js.map +1 -0
- package/dist/vault/transport.d.ts +50 -0
- package/dist/vault/transport.d.ts.map +1 -0
- package/dist/vault/transport.js +118 -0
- package/dist/vault/transport.js.map +1 -0
- package/dist/vault/upsert.d.ts +102 -0
- package/dist/vault/upsert.d.ts.map +1 -0
- package/dist/vault/upsert.js +92 -0
- package/dist/vault/upsert.js.map +1 -0
- package/dist/vta/bridge-mediator-session.d.ts +26 -0
- package/dist/vta/bridge-mediator-session.d.ts.map +1 -0
- package/dist/vta/bridge-mediator-session.js +37 -0
- package/dist/vta/bridge-mediator-session.js.map +1 -0
- package/dist/vta/bridge-memory.d.ts +80 -0
- package/dist/vta/bridge-memory.d.ts.map +1 -0
- package/dist/vta/bridge-memory.js +162 -0
- package/dist/vta/bridge-memory.js.map +1 -0
- package/dist/vta/client.d.ts +40 -0
- package/dist/vta/client.d.ts.map +1 -0
- package/dist/vta/client.js +91 -0
- package/dist/vta/client.js.map +1 -0
- package/dist/vta/contexts.d.ts +60 -0
- package/dist/vta/contexts.d.ts.map +1 -0
- package/dist/vta/contexts.js +118 -0
- package/dist/vta/contexts.js.map +1 -0
- package/dist/vta/didcomm.d.ts +57 -0
- package/dist/vta/didcomm.d.ts.map +1 -0
- package/dist/vta/didcomm.js +138 -0
- package/dist/vta/didcomm.js.map +1 -0
- package/dist/vta/errors.d.ts +20 -0
- package/dist/vta/errors.d.ts.map +1 -0
- package/dist/vta/errors.js +64 -0
- package/dist/vta/errors.js.map +1 -0
- package/dist/vta/index.d.ts +15 -0
- package/dist/vta/index.d.ts.map +1 -0
- package/dist/vta/index.js +15 -0
- package/dist/vta/index.js.map +1 -0
- package/dist/vta/mediation.d.ts +80 -0
- package/dist/vta/mediation.d.ts.map +1 -0
- package/dist/vta/mediation.js +29 -0
- package/dist/vta/mediation.js.map +1 -0
- package/dist/vta/mediator-client.d.ts +66 -0
- package/dist/vta/mediator-client.d.ts.map +1 -0
- package/dist/vta/mediator-client.js +139 -0
- package/dist/vta/mediator-client.js.map +1 -0
- package/dist/vta/pickup.d.ts +81 -0
- package/dist/vta/pickup.d.ts.map +1 -0
- package/dist/vta/pickup.js +30 -0
- package/dist/vta/pickup.js.map +1 -0
- package/dist/vta/protocol.d.ts +76 -0
- package/dist/vta/protocol.d.ts.map +1 -0
- package/dist/vta/protocol.js +30 -0
- package/dist/vta/protocol.js.map +1 -0
- package/dist/vta/smoke.d.ts +59 -0
- package/dist/vta/smoke.d.ts.map +1 -0
- package/dist/vta/smoke.js +408 -0
- package/dist/vta/smoke.js.map +1 -0
- package/dist/vta/transport.d.ts +55 -0
- package/dist/vta/transport.d.ts.map +1 -0
- package/dist/vta/transport.js +2 -0
- package/dist/vta/transport.js.map +1 -0
- package/dist/vta/types.d.ts +50 -0
- package/dist/vta/types.d.ts.map +1 -0
- package/dist/vta/types.js +2 -0
- package/dist/vta/types.js.map +1 -0
- package/dist/vta/wallet-session.d.ts +87 -0
- package/dist/vta/wallet-session.d.ts.map +1 -0
- package/dist/vta/wallet-session.js +106 -0
- package/dist/vta/wallet-session.js.map +1 -0
- package/dist/webauthn/base64url.d.ts +3 -0
- package/dist/webauthn/base64url.d.ts.map +1 -0
- package/dist/webauthn/base64url.js +17 -0
- package/dist/webauthn/base64url.js.map +1 -0
- package/dist/webauthn/index.d.ts +4 -0
- package/dist/webauthn/index.d.ts.map +1 -0
- package/dist/webauthn/index.js +4 -0
- package/dist/webauthn/index.js.map +1 -0
- package/dist/webauthn/multikey.d.ts +26 -0
- package/dist/webauthn/multikey.d.ts.map +1 -0
- package/dist/webauthn/multikey.js +91 -0
- package/dist/webauthn/multikey.js.map +1 -0
- package/dist/webauthn/register.d.ts +36 -0
- package/dist/webauthn/register.d.ts.map +1 -0
- package/dist/webauthn/register.js +77 -0
- package/dist/webauthn/register.js.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @openvtc/pnm-core
|
|
2
|
+
|
|
3
|
+
Browser-side bridge between **WebAuthn passkeys** and **VTA-managed
|
|
4
|
+
DIDs**. Lets a relying party prove the user controls a DID hosted in
|
|
5
|
+
a remote Verifiable Trust Agent (VTA) by performing a passkey
|
|
6
|
+
ceremony in the browser — no DID private keys ever leave the VTA,
|
|
7
|
+
and no long-lived bearer token sits in browser storage. Speaks both
|
|
8
|
+
REST and **full DIDComm v2** to the VTA, including when the VTA is
|
|
9
|
+
private-network and only reachable via a mediator.
|
|
10
|
+
|
|
11
|
+
This package is the shared TypeScript library that backs the
|
|
12
|
+
[Personal Network Manager browser
|
|
13
|
+
plugin](https://github.com/OpenVTC/vta-browser-plugin): both the
|
|
14
|
+
[PWA](https://github.com/OpenVTC/vta-browser-plugin/tree/main/packages/pwa)
|
|
15
|
+
and the
|
|
16
|
+
[Manifest v3 extension](https://github.com/OpenVTC/vta-browser-plugin/tree/main/packages/extension)
|
|
17
|
+
are thin shells over it. External consumers can use it directly to
|
|
18
|
+
build mobile companions, desktop wallets, or RP-side integrations
|
|
19
|
+
that need the same flows without the bundled UI.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm install @openvtc/pnm-core
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## What it gives you
|
|
28
|
+
|
|
29
|
+
| Sub-module | Surface |
|
|
30
|
+
|---|---|
|
|
31
|
+
| **`@openvtc/pnm-core/webauthn`** | Passkey enrol / login ceremonies, COSE-key extraction, DID `verificationMethod` builder, PRF-derived secret-wrap helpers. |
|
|
32
|
+
| **`@openvtc/pnm-core/did`** | Multikey ↔ JWK conversion, DID-URL parsing, did:webvh log resolution. |
|
|
33
|
+
| **`@openvtc/pnm-core/vta`** | Typed REST + DIDComm transports against a VTA daemon. Mirrors the [`vta-sdk`](https://crates.io/crates/vta-sdk) Rust client's surface. |
|
|
34
|
+
| Plus modules for | SIOPv2 / OpenID4VP RP-side helpers, sealed-bootstrap provisioning, vault proxy-login flows, Trust-Task envelope construction, indexed-DB key/value persistence. |
|
|
35
|
+
|
|
36
|
+
The package's [`src/index.ts`](https://github.com/OpenVTC/vta-browser-plugin/blob/main/packages/core/src/index.ts)
|
|
37
|
+
re-exports everything from a single entry; the slash-suffixed
|
|
38
|
+
sub-entries above are an optional convenience for callers who only
|
|
39
|
+
want a slice of the surface.
|
|
40
|
+
|
|
41
|
+
## Minimal example — passkey enrolment
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import {
|
|
45
|
+
beginEnrolment,
|
|
46
|
+
finishEnrolment,
|
|
47
|
+
type WebauthnEnrolmentChallenge,
|
|
48
|
+
} from "@openvtc/pnm-core";
|
|
49
|
+
|
|
50
|
+
// 1. Ask the VTA for an enrolment challenge for the named DID.
|
|
51
|
+
const challenge: WebauthnEnrolmentChallenge = await vtaClient.enrolBegin({
|
|
52
|
+
did: "did:webvh:example.com:alice",
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// 2. Run the WebAuthn create() ceremony in the browser.
|
|
56
|
+
const credential = await beginEnrolment(challenge);
|
|
57
|
+
|
|
58
|
+
// 3. Submit the assertion. The VTA verifies it, appends the COSE
|
|
59
|
+
// public key as a `verificationMethod` on the WebVH log, and
|
|
60
|
+
// publishes the new DID-document revision.
|
|
61
|
+
const result = await finishEnrolment(credential, challenge.session_id);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Wire compatibility
|
|
65
|
+
|
|
66
|
+
This package is byte-compatible with:
|
|
67
|
+
|
|
68
|
+
- The Rust [`vta-sdk`](https://crates.io/crates/vta-sdk) — typed VTA client used by the `pnm` CLI and other server-side consumers.
|
|
69
|
+
- The Rust [`did-hosting-client`](https://github.com/affinidi/affinidi-webvh-service/tree/main/did-hosting-client) — typed WebVH hosting client.
|
|
70
|
+
- The TypeScript [`@openvtc/vti-didcomm-js`](https://www.npmjs.com/package/@openvtc/vti-didcomm-js) — DIDComm v2 framing helpers (a runtime dependency of this package).
|
|
71
|
+
- The TypeScript [`@openvtc/trust-tasks`](https://www.npmjs.com/package/@openvtc/trust-tasks) — generated payload types for the [Trust Tasks framework](https://trusttasks.org).
|
|
72
|
+
|
|
73
|
+
A change to the wire surface is made in [`dtgwg-trust-tasks-tf`](https://github.com/trustoverip/dtgwg-trust-tasks-tf)
|
|
74
|
+
first, regenerates the Rust + TS bindings, and only then lands in
|
|
75
|
+
this package — see the project's spec-first development discipline.
|
|
76
|
+
|
|
77
|
+
## Architecture
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
┌──────────────┐ WebAuthn ┌────────────────┐
|
|
81
|
+
│ Browser │ ───────────────▶ │ Authenticator │ (Touch ID,
|
|
82
|
+
│ (PWA / ext) │ ◀─────────────── │ / Passkey │ Windows Hello,
|
|
83
|
+
└──────┬───────┘ pubkey + sig └────────────────┘ YubiKey, …)
|
|
84
|
+
│
|
|
85
|
+
│ enrol(passkey_pubkey) verify(assertion)
|
|
86
|
+
▼ ▲
|
|
87
|
+
┌──────────────┐ ┌───────┴────────┐
|
|
88
|
+
│ VTA │ ── WebVH update ─────▶│ Public DID doc │
|
|
89
|
+
│ (remote) │ │ (resolvable by │
|
|
90
|
+
└──────────────┘ │ any verifier) │
|
|
91
|
+
└────────────────┘
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
A passkey is enrolled as a `verificationMethod` (purpose:
|
|
95
|
+
`authentication`) in the DID document the VTA publishes via WebVH.
|
|
96
|
+
Any verifier that resolves the DID can then validate a WebAuthn
|
|
97
|
+
assertion against the embedded public key without ever talking to
|
|
98
|
+
the VTA — the DID document *is* the trust anchor.
|
|
99
|
+
|
|
100
|
+
## Browser / runtime support
|
|
101
|
+
|
|
102
|
+
- Modern browsers with WebAuthn level 2 + WebCrypto (Chrome 108+,
|
|
103
|
+
Safari 17+, Firefox 122+).
|
|
104
|
+
- Node 20+ for server-side use (the WebAuthn-specific entry points
|
|
105
|
+
are no-ops in non-browser contexts; the DID / VTA / DIDComm
|
|
106
|
+
transports work everywhere).
|
|
107
|
+
|
|
108
|
+
ESM-only — no CommonJS build.
|
|
109
|
+
|
|
110
|
+
## Versioning
|
|
111
|
+
|
|
112
|
+
Pre-1.0 (`0.x`) — breaking changes may land in minor bumps. The
|
|
113
|
+
internal contract this package depends on (`@openvtc/vti-didcomm-js`,
|
|
114
|
+
`@openvtc/trust-tasks`) follows the same cadence. Once the
|
|
115
|
+
underlying `SPEC.md` reaches 1.0 this package will follow.
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
Apache-2.0. See [LICENSE](https://github.com/OpenVTC/vta-browser-plugin/blob/main/LICENSE)
|
|
120
|
+
at the repo root.
|
|
121
|
+
|
|
122
|
+
## Contributing
|
|
123
|
+
|
|
124
|
+
Source lives in
|
|
125
|
+
[`OpenVTC/vta-browser-plugin`](https://github.com/OpenVTC/vta-browser-plugin)
|
|
126
|
+
under `packages/core/`. See the
|
|
127
|
+
[root README](https://github.com/OpenVTC/vta-browser-plugin#readme)
|
|
128
|
+
for the workspace layout, development setup, and the smoke-test
|
|
129
|
+
harness.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface DeriveSigningKeyIdResult {
|
|
2
|
+
/** The DID the caller asked us to derive for. Echoed for symmetry. */
|
|
3
|
+
did: string;
|
|
4
|
+
/** Plausible verification-method ids the holder could sign with.
|
|
5
|
+
* Empty when resolution failed; one entry for the trivial case
|
|
6
|
+
* (did:key or single-key DIDs); multiple for DIDs with several
|
|
7
|
+
* authentication keys. The popup auto-fills only on
|
|
8
|
+
* `candidates.length === 1`. */
|
|
9
|
+
candidates: string[];
|
|
10
|
+
/** Human-readable resolution error (network / structural / hash
|
|
11
|
+
* chain), if any. Surfaces to the operator so they know whether
|
|
12
|
+
* to type the kid by hand or fix the DID. */
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Derive the verification-method id (`signingKeyId`) candidates from
|
|
16
|
+
* the given DID. Never throws — failures land as `candidates: []` +
|
|
17
|
+
* an `error` string. */
|
|
18
|
+
export declare function deriveSigningKeyId(did: string): Promise<DeriveSigningKeyIdResult>;
|
|
19
|
+
//# sourceMappingURL=derive-signing-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-signing-key.d.ts","sourceRoot":"","sources":["../../src/did/derive-signing-key.ts"],"names":[],"mappings":"AAkCA,MAAM,WAAW,wBAAwB;IACvC,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ;;;;qCAIiC;IACjC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB;;kDAE8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;yBAEyB;AACzB,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAkDvF"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// Derive a candidate `signingKeyId` (verification-method id) from a
|
|
2
|
+
// DID. Used by the popup's AddEntryForm to auto-fill the field when
|
|
3
|
+
// the operator types a principal DID — saves them looking up
|
|
4
|
+
// `<did>#key-0` style fragments by hand.
|
|
5
|
+
//
|
|
6
|
+
// Resolution strategy per method:
|
|
7
|
+
//
|
|
8
|
+
// - **did:key**: lexical. `did:key:zXxx` decomposes to
|
|
9
|
+
// `did:key:zXxx#zXxx` — the multibase tag IS the verification-
|
|
10
|
+
// method fragment. No network round-trip.
|
|
11
|
+
//
|
|
12
|
+
// - **did:peer:2**: self-resolving from the DID identifier
|
|
13
|
+
// itself; the keys are inline-encoded. The wallet's existing
|
|
14
|
+
// resolver in `vti-didcomm-js` handles this offline.
|
|
15
|
+
//
|
|
16
|
+
// - **did:webvh** / **did:web**: requires resolving the DID
|
|
17
|
+
// document over the network. The resolver in `vti-didcomm-js`
|
|
18
|
+
// walks the webvh log + verifies every Data Integrity proof,
|
|
19
|
+
// so a successful resolve is a real attestation.
|
|
20
|
+
//
|
|
21
|
+
// Selection: when the resolved DID document has a single
|
|
22
|
+
// `authentication` (or `assertionMethod`) entry, return its id and
|
|
23
|
+
// `candidates.length === 1` so the popup can auto-fill the field.
|
|
24
|
+
// When multiple are present, return them all so the popup can offer
|
|
25
|
+
// a picker — the operator picks which key signs.
|
|
26
|
+
//
|
|
27
|
+
// `verifyDid` already exists for the consent-prompt's DID resolution
|
|
28
|
+
// path; this file is a parallel surface focused on extracting the
|
|
29
|
+
// VM id rather than just validating resolution.
|
|
30
|
+
import { resolve as vtiResolve } from "@openvtc/vti-didcomm-js";
|
|
31
|
+
import { didMethod } from "./verify.js";
|
|
32
|
+
/** Derive the verification-method id (`signingKeyId`) candidates from
|
|
33
|
+
* the given DID. Never throws — failures land as `candidates: []` +
|
|
34
|
+
* an `error` string. */
|
|
35
|
+
export async function deriveSigningKeyId(did) {
|
|
36
|
+
const method = didMethod(did);
|
|
37
|
+
if (method === "key") {
|
|
38
|
+
// did:key:zXxx → did:key:zXxx#zXxx. The multibase tag (after
|
|
39
|
+
// `did:key:`) IS the fragment by convention.
|
|
40
|
+
const mb = did.slice("did:key:".length);
|
|
41
|
+
if (!mb.startsWith("z")) {
|
|
42
|
+
return { did, candidates: [], error: "did:key identifier is not base58btc multibase" };
|
|
43
|
+
}
|
|
44
|
+
return { did, candidates: [`${did}#${mb}`] };
|
|
45
|
+
}
|
|
46
|
+
if (method === "unknown") {
|
|
47
|
+
return { did, candidates: [], error: "Unrecognised DID method" };
|
|
48
|
+
}
|
|
49
|
+
// did:peer, did:webvh: full resolution. did:peer is offline; webvh
|
|
50
|
+
// hits the network and verifies the log.
|
|
51
|
+
try {
|
|
52
|
+
const resolution = (await vtiResolve(did, {}));
|
|
53
|
+
const resolverError = resolution.didResolutionMetadata?.error;
|
|
54
|
+
if (resolverError) {
|
|
55
|
+
return { did, candidates: [], error: resolverError };
|
|
56
|
+
}
|
|
57
|
+
const doc = resolution.didDocument;
|
|
58
|
+
if (!doc) {
|
|
59
|
+
return { did, candidates: [], error: "Resolver returned no DID document" };
|
|
60
|
+
}
|
|
61
|
+
// Prefer `authentication` (the signing-purpose-correct list for
|
|
62
|
+
// SIOP-style id_tokens). Fall back to `assertionMethod` if absent.
|
|
63
|
+
// Final fallback: enumerate `verificationMethod` ids — better to
|
|
64
|
+
// give the operator something than nothing.
|
|
65
|
+
const candidates = collectVmIds(doc.authentication) ||
|
|
66
|
+
collectVmIds(doc.assertionMethod) ||
|
|
67
|
+
collectVmIds(doc.verificationMethod) ||
|
|
68
|
+
[];
|
|
69
|
+
if (candidates.length === 0) {
|
|
70
|
+
return { did, candidates: [], error: "DID document has no usable verification methods" };
|
|
71
|
+
}
|
|
72
|
+
return { did, candidates };
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
return { did, candidates: [], error: e instanceof Error ? e.message : String(e) };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Extract the `id` string of every verification-method reference,
|
|
79
|
+
* whether stored as a bare URL string or an embedded VM object.
|
|
80
|
+
* Returns `null` (not `[]`) when the input is `undefined` so the
|
|
81
|
+
* caller can short-circuit the `||` chain to the next purpose
|
|
82
|
+
* rather than landing on an empty list it would otherwise treat as
|
|
83
|
+
* authoritative. */
|
|
84
|
+
function collectVmIds(vms) {
|
|
85
|
+
if (!vms || vms.length === 0)
|
|
86
|
+
return null;
|
|
87
|
+
const out = [];
|
|
88
|
+
for (const entry of vms) {
|
|
89
|
+
if (typeof entry === "string")
|
|
90
|
+
out.push(entry);
|
|
91
|
+
else if (entry && typeof entry === "object" && typeof entry.id === "string")
|
|
92
|
+
out.push(entry.id);
|
|
93
|
+
}
|
|
94
|
+
return out.length > 0 ? out : null;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=derive-signing-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive-signing-key.js","sourceRoot":"","sources":["../../src/did/derive-signing-key.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/did/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/did/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Abbreviated DIDComm service for a `did:peer:2` `S` segment. The
|
|
2
|
+
* abbreviation (`t`/`s`/`r`/`a`) is the did:peer:2 convention the resolver
|
|
3
|
+
* decodes back to a `DIDCommMessaging` service. */
|
|
4
|
+
export interface DidPeerService {
|
|
5
|
+
/** Service type. `"dm"` abbreviates `DIDCommMessaging` (the default). */
|
|
6
|
+
type?: string;
|
|
7
|
+
/** serviceEndpoint URI — for mediator-routed delivery this is the
|
|
8
|
+
* mediator's DID. */
|
|
9
|
+
serviceEndpoint: string;
|
|
10
|
+
/** Optional routing keys. */
|
|
11
|
+
routingKeys?: string[];
|
|
12
|
+
/** Accepted profiles (default `["didcomm/v2"]`). */
|
|
13
|
+
accept?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface DidPeer2 {
|
|
16
|
+
/** The full `did:peer:2` string. */
|
|
17
|
+
did: string;
|
|
18
|
+
/** Ed25519 authentication VM id — `<did>#key-2`. The SIOP `id_token` `kid`. */
|
|
19
|
+
authKid: string;
|
|
20
|
+
/** X25519 keyAgreement VM id — `<did>#key-1`. Used for DIDComm authcrypt. */
|
|
21
|
+
keyAgreementKid: string;
|
|
22
|
+
}
|
|
23
|
+
export interface CreateDidPeer2Args {
|
|
24
|
+
/** Ed25519 public key (authentication / signing). */
|
|
25
|
+
ed25519PublicKey: Uint8Array;
|
|
26
|
+
/** X25519 public key (keyAgreement / authcrypt). */
|
|
27
|
+
x25519PublicKey: Uint8Array;
|
|
28
|
+
/** Optional DIDComm service to advertise (e.g. the wallet's mediator). */
|
|
29
|
+
service?: DidPeerService;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build a `did:peer:2` from an Ed25519 (auth) + X25519 (keyAgreement) key
|
|
33
|
+
* pair and an optional DIDComm service. Returns the DID plus the
|
|
34
|
+
* deterministic VM ids (`#key-1` = keyAgreement, `#key-2` = authentication).
|
|
35
|
+
*/
|
|
36
|
+
export declare function createDidPeer2(args: CreateDidPeer2Args): DidPeer2;
|
|
37
|
+
//# sourceMappingURL=peer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peer.d.ts","sourceRoot":"","sources":["../../src/did/peer.ts"],"names":[],"mappings":"AAqBA;;oDAEoD;AACpD,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;0BACsB;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,+EAA+E;IAC/E,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,gBAAgB,EAAE,UAAU,CAAC;IAC7B,oDAAoD;IACpD,eAAe,EAAE,UAAU,CAAC;IAC5B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,QAAQ,CA2BjE"}
|
package/dist/did/peer.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// did:peer:2 (numalgo 2) generation for the wallet's holder identity.
|
|
2
|
+
//
|
|
3
|
+
// Unlike `did:key`, a `did:peer:2` can encode a service endpoint inline, so
|
|
4
|
+
// the wallet can advertise its mediator and be *reachable* for inbound
|
|
5
|
+
// DIDComm (RP-initiated confirm/approve requests) — see the RP→wallet
|
|
6
|
+
// trigger design. The DID is self-contained and resolves with no network.
|
|
7
|
+
//
|
|
8
|
+
// Segment order is FIXED — keyAgreement (`E`, X25519) first, authentication
|
|
9
|
+
// (`V`, Ed25519) second, service (`S`) last — because resolvers number
|
|
10
|
+
// verification methods **positionally** (`#key-1`, `#key-2`, …) in the order
|
|
11
|
+
// segments appear. Verified against the affinidi `DIDCacheClient` (the RP's
|
|
12
|
+
// resolver): for `did:peer:2.E….V….S…` it emits `#key-1` = the X25519 KA
|
|
13
|
+
// key and `#key-2` = the Ed25519 auth key, as `Multikey`/`publicKeyMultibase`.
|
|
14
|
+
// So the SIOP `id_token` `kid` is `<did>#key-2` and the authcrypt
|
|
15
|
+
// keyAgreement kid is `<did>#key-1`.
|
|
16
|
+
import { base64url, multibase } from "@openvtc/vti-didcomm-js";
|
|
17
|
+
const X25519_PUB = multibase.MULTICODEC.X25519_PUB;
|
|
18
|
+
const ED25519_PUB = multibase.MULTICODEC.ED25519_PUB;
|
|
19
|
+
/**
|
|
20
|
+
* Build a `did:peer:2` from an Ed25519 (auth) + X25519 (keyAgreement) key
|
|
21
|
+
* pair and an optional DIDComm service. Returns the DID plus the
|
|
22
|
+
* deterministic VM ids (`#key-1` = keyAgreement, `#key-2` = authentication).
|
|
23
|
+
*/
|
|
24
|
+
export function createDidPeer2(args) {
|
|
25
|
+
// E (keyAgreement, X25519) first → #key-1; V (authentication, Ed25519)
|
|
26
|
+
// second → #key-2. Order is load-bearing (positional VM numbering).
|
|
27
|
+
const kaMultibase = multibase.encodeMultikey(X25519_PUB, args.x25519PublicKey);
|
|
28
|
+
const authMultibase = multibase.encodeMultikey(ED25519_PUB, args.ed25519PublicKey);
|
|
29
|
+
let did = `did:peer:2.E${kaMultibase}.V${authMultibase}`;
|
|
30
|
+
if (args.service) {
|
|
31
|
+
const s = args.service;
|
|
32
|
+
// Abbreviated DIDComm service; key insertion order t,s,r,a matches the
|
|
33
|
+
// did:peer:2 convention. `r` omitted when there are no routing keys.
|
|
34
|
+
const abbreviated = {
|
|
35
|
+
t: s.type ?? "dm",
|
|
36
|
+
s: s.serviceEndpoint,
|
|
37
|
+
...(s.routingKeys && s.routingKeys.length > 0 ? { r: s.routingKeys } : {}),
|
|
38
|
+
a: s.accept ?? ["didcomm/v2"],
|
|
39
|
+
};
|
|
40
|
+
const encoded = base64url.encode(new TextEncoder().encode(JSON.stringify(abbreviated)));
|
|
41
|
+
did += `.S${encoded}`;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
did,
|
|
45
|
+
authKid: `${did}#key-2`,
|
|
46
|
+
keyAgreementKid: `${did}#key-1`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=peer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peer.js","sourceRoot":"","sources":["../../src/did/peer.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,4EAA4E;AAC5E,uEAAuE;AACvE,sEAAsE;AACtE,0EAA0E;AAC1E,EAAE;AACF,4EAA4E;AAC5E,uEAAuE;AACvE,6EAA6E;AAC7E,4EAA4E;AAC5E,yEAAyE;AACzE,+EAA+E;AAC/E,kEAAkE;AAClE,qCAAqC;AAErC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAE/D,MAAM,UAAU,GAAG,SAAS,CAAC,UAAU,CAAC,UAAU,CAAC;AACnD,MAAM,WAAW,GAAG,SAAS,CAAC,UAAU,CAAC,WAAW,CAAC;AAmCrD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAwB;IACrD,uEAAuE;IACvE,oEAAoE;IACpE,MAAM,WAAW,GAAG,SAAS,CAAC,cAAc,CAAC,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IAC/E,MAAM,aAAa,GAAG,SAAS,CAAC,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAEnF,IAAI,GAAG,GAAG,eAAe,WAAW,KAAK,aAAa,EAAE,CAAC;IAEzD,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC;QACvB,uEAAuE;QACvE,qEAAqE;QACrE,MAAM,WAAW,GAA4B;YAC3C,CAAC,EAAE,CAAC,CAAC,IAAI,IAAI,IAAI;YACjB,CAAC,EAAE,CAAC,CAAC,eAAe;YACpB,GAAG,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1E,CAAC,EAAE,CAAC,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC;SAC9B,CAAC;QACF,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QACxF,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,OAAO;QACL,GAAG;QACH,OAAO,EAAE,GAAG,GAAG,QAAQ;QACvB,eAAe,EAAE,GAAG,GAAG,QAAQ;KAChC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A `verificationMethod` entry, in the shape we want appended to the
|
|
3
|
+
* VTA-managed DID document. Type `Multikey` is the W3C-recommended
|
|
4
|
+
* representation; `publicKeyMultibase` carries the base58btc-encoded
|
|
5
|
+
* multicodec-prefixed key.
|
|
6
|
+
*
|
|
7
|
+
* The `webauthn*` extensions are non-core but explicit: any verifier
|
|
8
|
+
* resolving the DID can find the VM by `credentialId` hash and verify
|
|
9
|
+
* a WebAuthn assertion against `publicKeyMultibase` directly — no
|
|
10
|
+
* round-trip to the VTA required.
|
|
11
|
+
*/
|
|
12
|
+
export interface PasskeyVerificationMethod {
|
|
13
|
+
id: string;
|
|
14
|
+
type: "Multikey";
|
|
15
|
+
controller: string;
|
|
16
|
+
publicKeyMultibase: string;
|
|
17
|
+
/** WebAuthn credential id, base64url-encoded. */
|
|
18
|
+
webauthnCredentialId: string;
|
|
19
|
+
/** Transport hints the authenticator reported (e.g. "internal", "hybrid"). */
|
|
20
|
+
webauthnTransports?: AuthenticatorTransport[];
|
|
21
|
+
/**
|
|
22
|
+
* Optional friendly label set by the operator (e.g. "MacBook Touch
|
|
23
|
+
* ID", "YubiKey 5C"). Surfaced in UI; not used for verification.
|
|
24
|
+
*/
|
|
25
|
+
label?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface BuildVerificationMethodArgs {
|
|
28
|
+
did: string;
|
|
29
|
+
credentialId: string;
|
|
30
|
+
credentialIdBytes: Uint8Array;
|
|
31
|
+
publicKeyMultikey: string;
|
|
32
|
+
transports?: AuthenticatorTransport[];
|
|
33
|
+
label?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Derive the VM fragment from the credential id. SHA-256 keeps the
|
|
37
|
+
* fragment short and stable while still being deterministic — a
|
|
38
|
+
* verifier given a WebAuthn assertion can recompute the fragment
|
|
39
|
+
* from `credential.id` and look it up in the DID document.
|
|
40
|
+
*/
|
|
41
|
+
export declare function passkeyVerificationMethodFragment(credentialIdBytes: Uint8Array): Promise<string>;
|
|
42
|
+
export declare function buildPasskeyVerificationMethod(args: BuildVerificationMethodArgs): Promise<PasskeyVerificationMethod>;
|
|
43
|
+
//# sourceMappingURL=verification-method.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-method.d.ts","sourceRoot":"","sources":["../../src/did/verification-method.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iDAAiD;IACjD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,8EAA8E;IAC9E,kBAAkB,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC9C;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,2BAA2B;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,UAAU,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,CAAC,EAAE,sBAAsB,EAAE,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;;;;;GAKG;AACH,wBAAsB,iCAAiC,CACrD,iBAAiB,EAAE,UAAU,GAC5B,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,2BAA2B,GAChC,OAAO,CAAC,yBAAyB,CAAC,CAgBpC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { bytesToBase64url } from "../webauthn/base64url.js";
|
|
2
|
+
async function sha256(bytes) {
|
|
3
|
+
return new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Derive the VM fragment from the credential id. SHA-256 keeps the
|
|
7
|
+
* fragment short and stable while still being deterministic — a
|
|
8
|
+
* verifier given a WebAuthn assertion can recompute the fragment
|
|
9
|
+
* from `credential.id` and look it up in the DID document.
|
|
10
|
+
*/
|
|
11
|
+
export async function passkeyVerificationMethodFragment(credentialIdBytes) {
|
|
12
|
+
const hash = await sha256(credentialIdBytes);
|
|
13
|
+
return `passkey-${bytesToBase64url(hash)}`;
|
|
14
|
+
}
|
|
15
|
+
export async function buildPasskeyVerificationMethod(args) {
|
|
16
|
+
const fragment = await passkeyVerificationMethodFragment(args.credentialIdBytes);
|
|
17
|
+
const vm = {
|
|
18
|
+
id: `${args.did}#${fragment}`,
|
|
19
|
+
type: "Multikey",
|
|
20
|
+
controller: args.did,
|
|
21
|
+
publicKeyMultibase: args.publicKeyMultikey,
|
|
22
|
+
webauthnCredentialId: args.credentialId,
|
|
23
|
+
};
|
|
24
|
+
if (args.transports && args.transports.length > 0) {
|
|
25
|
+
vm.webauthnTransports = args.transports;
|
|
26
|
+
}
|
|
27
|
+
if (args.label) {
|
|
28
|
+
vm.label = args.label;
|
|
29
|
+
}
|
|
30
|
+
return vm;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=verification-method.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-method.js","sourceRoot":"","sources":["../../src/did/verification-method.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAsC5D,KAAK,UAAU,MAAM,CAAC,KAAiB;IACrC,OAAO,IAAI,UAAU,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,KAAqB,CAAC,CAAC,CAAC;AACtF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC,CACrD,iBAA6B;IAE7B,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC7C,OAAO,WAAW,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,IAAiC;IAEjC,MAAM,QAAQ,GAAG,MAAM,iCAAiC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IACjF,MAAM,EAAE,GAA8B;QACpC,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,QAAQ,EAAE;QAC7B,IAAI,EAAE,UAAU;QAChB,UAAU,EAAE,IAAI,CAAC,GAAG;QACpB,kBAAkB,EAAE,IAAI,CAAC,iBAAiB;QAC1C,oBAAoB,EAAE,IAAI,CAAC,YAAY;KACxC,CAAC;IACF,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClD,EAAE,CAAC,kBAAkB,GAAG,IAAI,CAAC,UAAU,CAAC;IAC1C,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACxB,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export type DidMethod = "webvh" | "peer" | "key" | "unknown";
|
|
2
|
+
export interface VerifyDidResult {
|
|
3
|
+
/** Canonical DID under verification. */
|
|
4
|
+
did: string;
|
|
5
|
+
/** Parsed DID method. `unknown` if the input is not a recognisable DID. */
|
|
6
|
+
method: DidMethod;
|
|
7
|
+
/** True if resolution succeeded. For did:webvh this implies the SCID + log
|
|
8
|
+
* hash chain + every Data Integrity proof verified. For did:peer/did:key
|
|
9
|
+
* it implies the identifier was structurally valid. */
|
|
10
|
+
resolved: boolean;
|
|
11
|
+
/** For did:webvh — the host the DID identifies (extracted from the DID,
|
|
12
|
+
* not the resolved document, so the operator sees what the wallet asked
|
|
13
|
+
* for). The DID is `did:webvh:<scid>:<host>[:<path>...]`. */
|
|
14
|
+
domain?: string;
|
|
15
|
+
/** Human-readable error if resolution failed. */
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Parse the method of a DID string. Cheap, structural — does not resolve.
|
|
20
|
+
*/
|
|
21
|
+
export declare function didMethod(did: string): DidMethod;
|
|
22
|
+
/**
|
|
23
|
+
* Extract the host portion of a `did:webvh:<scid>:<host>[:path:...]`. Returns
|
|
24
|
+
* `undefined` for non-webvh inputs. The path-style segments after the host
|
|
25
|
+
* are intentionally discarded — the operator-facing display only needs the
|
|
26
|
+
* host to compare against the page origin.
|
|
27
|
+
*
|
|
28
|
+
* webvh's wire format percent-encodes the host's `:` and `/`; we leave them
|
|
29
|
+
* encoded because the consent UI only matches on hostname (which never
|
|
30
|
+
* contains either).
|
|
31
|
+
*/
|
|
32
|
+
export declare function didWebvhDomain(did: string): string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve and validate an RP DID. Never throws — failures are returned via
|
|
35
|
+
* `resolved: false` and `error`, because the consent UI wants to *render*
|
|
36
|
+
* the error rather than crash.
|
|
37
|
+
*/
|
|
38
|
+
export declare function verifyDid(did: string): Promise<VerifyDidResult>;
|
|
39
|
+
/**
|
|
40
|
+
* Decide whether a page origin's hostname is consistent with a did:webvh
|
|
41
|
+
* domain. Exact match or proper subdomain count as consistent. Anything
|
|
42
|
+
* else (sibling subdomain, unrelated host, etc.) is flagged.
|
|
43
|
+
*
|
|
44
|
+
* For non-webvh DIDs this returns `"not-applicable"` — there's no domain
|
|
45
|
+
* encoded in the DID, so the wallet has nothing to compare against.
|
|
46
|
+
*/
|
|
47
|
+
export type OriginMatch = "match" | "subdomain" | "mismatch" | "not-applicable";
|
|
48
|
+
export declare function compareOriginToDidDomain(originHost: string | undefined, didDomain: string | undefined): OriginMatch;
|
|
49
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../src/did/verify.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,2EAA2E;IAC3E,MAAM,EAAE,SAAS,CAAC;IAClB;;4DAEwD;IACxD,QAAQ,EAAE,OAAO,CAAC;IAClB;;kEAE8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAKhD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAK9D;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA4BrE;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,WAAW,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAEhF,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,WAAW,CAMb"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// DID verification for the consent prompt.
|
|
2
|
+
//
|
|
3
|
+
// Before the wallet logs into an RP, the consent popup wants to tell the
|
|
4
|
+
// operator whether the rpDid actually resolves — and, for did:webvh, whether
|
|
5
|
+
// its declared domain is consistent with the page origin the request came
|
|
6
|
+
// from. This is the operator-facing complement to origin pinning: pinning
|
|
7
|
+
// catches "this site swapped which RP it points at"; verification catches
|
|
8
|
+
// "this site is pointing at an RP whose DID does not resolve" or "claims an
|
|
9
|
+
// RP whose domain has nothing to do with the page".
|
|
10
|
+
//
|
|
11
|
+
// did:webvh resolution is cryptographic (the library walks the log, verifies
|
|
12
|
+
// the SCID/hash chain, and verifies every Data Integrity proof on each entry)
|
|
13
|
+
// — a successful resolve is a real attestation, not just "fetch returned 200".
|
|
14
|
+
// did:peer / did:key are self-certifying (the keys are encoded in the id), so
|
|
15
|
+
// resolution is purely structural validation.
|
|
16
|
+
import { resolve as vtiResolve } from "@openvtc/vti-didcomm-js";
|
|
17
|
+
/**
|
|
18
|
+
* Parse the method of a DID string. Cheap, structural — does not resolve.
|
|
19
|
+
*/
|
|
20
|
+
export function didMethod(did) {
|
|
21
|
+
if (did.startsWith("did:webvh:"))
|
|
22
|
+
return "webvh";
|
|
23
|
+
if (did.startsWith("did:peer:"))
|
|
24
|
+
return "peer";
|
|
25
|
+
if (did.startsWith("did:key:"))
|
|
26
|
+
return "key";
|
|
27
|
+
return "unknown";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Extract the host portion of a `did:webvh:<scid>:<host>[:path:...]`. Returns
|
|
31
|
+
* `undefined` for non-webvh inputs. The path-style segments after the host
|
|
32
|
+
* are intentionally discarded — the operator-facing display only needs the
|
|
33
|
+
* host to compare against the page origin.
|
|
34
|
+
*
|
|
35
|
+
* webvh's wire format percent-encodes the host's `:` and `/`; we leave them
|
|
36
|
+
* encoded because the consent UI only matches on hostname (which never
|
|
37
|
+
* contains either).
|
|
38
|
+
*/
|
|
39
|
+
export function didWebvhDomain(did) {
|
|
40
|
+
if (!did.startsWith("did:webvh:"))
|
|
41
|
+
return undefined;
|
|
42
|
+
const parts = did.split(":");
|
|
43
|
+
// ["did", "webvh", "<scid>", "<host>", ...path]
|
|
44
|
+
return parts[3];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolve and validate an RP DID. Never throws — failures are returned via
|
|
48
|
+
* `resolved: false` and `error`, because the consent UI wants to *render*
|
|
49
|
+
* the error rather than crash.
|
|
50
|
+
*/
|
|
51
|
+
export async function verifyDid(did) {
|
|
52
|
+
const method = didMethod(did);
|
|
53
|
+
const domain = method === "webvh" ? didWebvhDomain(did) : undefined;
|
|
54
|
+
const base = {
|
|
55
|
+
did,
|
|
56
|
+
method,
|
|
57
|
+
resolved: false,
|
|
58
|
+
...(domain ? { domain } : {}),
|
|
59
|
+
};
|
|
60
|
+
if (method === "unknown") {
|
|
61
|
+
return { ...base, error: "Unrecognised DID method" };
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const resolution = (await vtiResolve(did, {}));
|
|
65
|
+
const resolverError = resolution.didResolutionMetadata?.error;
|
|
66
|
+
if (resolverError) {
|
|
67
|
+
return { ...base, error: resolverError };
|
|
68
|
+
}
|
|
69
|
+
if (!resolution.didDocument?.id) {
|
|
70
|
+
return { ...base, error: "Resolver returned no DID document" };
|
|
71
|
+
}
|
|
72
|
+
return { ...base, resolved: true };
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
return { ...base, error: e instanceof Error ? e.message : String(e) };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export function compareOriginToDidDomain(originHost, didDomain) {
|
|
79
|
+
if (!didDomain)
|
|
80
|
+
return "not-applicable";
|
|
81
|
+
if (!originHost)
|
|
82
|
+
return "mismatch";
|
|
83
|
+
if (originHost === didDomain)
|
|
84
|
+
return "match";
|
|
85
|
+
if (originHost.endsWith(`.${didDomain}`))
|
|
86
|
+
return "subdomain";
|
|
87
|
+
return "mismatch";
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../src/did/verify.ts"],"names":[],"mappings":"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"}
|