@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
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// Adapt a `MinimalAdminReply` from `runProvisionIntegration` into the
|
|
2
|
+
// shape `installVtaMintedHolder` consumes. Decodes the multibase-encoded
|
|
3
|
+
// private keys, runs a defence-in-depth sanity check that the X25519
|
|
4
|
+
// keyAgreement secret derives correctly from the Ed25519 seed via
|
|
5
|
+
// Montgomery clamping (the canonical did:key derivation the VTA uses
|
|
6
|
+
// on its side), and returns just the seed + DIDs the wallet persists.
|
|
7
|
+
//
|
|
8
|
+
// Kept separate from `run.ts` so the round-trip + the persistence
|
|
9
|
+
// adapter can be unit-tested independently — the round-trip needs a
|
|
10
|
+
// mediator + a live VTA, the adapter is a pure byte-level transform.
|
|
11
|
+
import { ed25519, x25519 } from "@noble/curves/ed25519.js";
|
|
12
|
+
import { multibase } from "@openvtc/vti-didcomm-js";
|
|
13
|
+
const ED25519_PRIV_CODEC = new Uint8Array([0x80, 0x26]); // 0x1300 varint
|
|
14
|
+
const X25519_PRIV_CODEC = new Uint8Array([0x82, 0x26]); // 0x1302 varint
|
|
15
|
+
/** Pull the wallet-persistable shape out of a `MinimalAdminReply`.
|
|
16
|
+
*
|
|
17
|
+
* Decodes the multibase private keys, verifies the multicodec prefix,
|
|
18
|
+
* and cross-checks that:
|
|
19
|
+
* - the X25519 secret the VTA shipped equals `toMontgomerySecret(edSeed)`
|
|
20
|
+
* — defence against a buggy or hostile VTA that ships an X25519
|
|
21
|
+
* secret independent of the Ed25519 seed; the wallet's loader will
|
|
22
|
+
* *always* recompute X25519 from the seed, so the two MUST agree
|
|
23
|
+
* or any DIDComm authcrypt the wallet attempts later will fail in
|
|
24
|
+
* a deeply confusing way at AEAD-open time.
|
|
25
|
+
* - the Ed25519 public key the seed expands to matches the multibase
|
|
26
|
+
* identifier inside the `did:key` — confirms the wallet receives a
|
|
27
|
+
* legitimate did:key (not a forged DID claiming a key it doesn't
|
|
28
|
+
* control). */
|
|
29
|
+
export function holderInputsFromAdminReply(reply) {
|
|
30
|
+
const signingPriv = decodePrivateKey(reply.adminSigningPrivateMultibase, ED25519_PRIV_CODEC, "signing (Ed25519)");
|
|
31
|
+
const kaPriv = decodePrivateKey(reply.adminKaPrivateMultibase, X25519_PRIV_CODEC, "keyAgreement (X25519)");
|
|
32
|
+
// Cross-check 1: X25519 secret must equal Montgomery(seed). If the VTA
|
|
33
|
+
// ever changes its derivation, we want to know NOW (at install time)
|
|
34
|
+
// rather than silently storing the seed and then having every
|
|
35
|
+
// DIDComm authcrypt fail under this DID.
|
|
36
|
+
const derivedX25519 = ed25519.utils.toMontgomerySecret(signingPriv);
|
|
37
|
+
if (!constantTimeEqual(derivedX25519, kaPriv)) {
|
|
38
|
+
throw new Error("provision-integration: VTA's ka_key.private_key_multibase does not equal " +
|
|
39
|
+
"toMontgomerySecret(signing seed). The wallet stores the seed and " +
|
|
40
|
+
"re-derives X25519 on demand; a mismatch would break DIDComm.");
|
|
41
|
+
}
|
|
42
|
+
// Cross-check 2: Ed25519 public must match the did:key identifier. The
|
|
43
|
+
// adminDid is `did:key:z<multibase-of-ed25519-pub>`; we extract the
|
|
44
|
+
// multibase, decode it, and assert equality with edPub from the seed.
|
|
45
|
+
const edPub = ed25519.getPublicKey(signingPriv);
|
|
46
|
+
const didMb = extractDidKeyMultibase(reply.adminDid);
|
|
47
|
+
const decodedDidKey = multibase.decodeMultikey(didMb);
|
|
48
|
+
if (!constantTimeEqual(decodedDidKey.codec, multibase.MULTICODEC.ED25519_PUB)) {
|
|
49
|
+
throw new Error(`provision-integration: adminDid '${reply.adminDid}' is not an Ed25519 did:key`);
|
|
50
|
+
}
|
|
51
|
+
if (!constantTimeEqual(decodedDidKey.key, edPub)) {
|
|
52
|
+
throw new Error("provision-integration: adminDid's multibase identifier does not encode the " +
|
|
53
|
+
"Ed25519 public key that the shipped seed expands to. The wallet would " +
|
|
54
|
+
"publish a DID it does not actually control.");
|
|
55
|
+
}
|
|
56
|
+
// The keyAgreement VM id convention for did:key Ed25519 (mirrored by
|
|
57
|
+
// the VTA at mint.rs:124-132) is `<did>#<x25519-multibase-pub>`. The
|
|
58
|
+
// wallet derives the X25519 pubkey from the seed and emits the same
|
|
59
|
+
// shape so every signer-side check the wallet later runs against this
|
|
60
|
+
// identity matches the verifier-side view.
|
|
61
|
+
const xPub = x25519.getPublicKey(derivedX25519);
|
|
62
|
+
const xMb = multibase.encodeMultikey(multibase.MULTICODEC.X25519_PUB, xPub);
|
|
63
|
+
const keyAgreementKid = `${reply.adminDid}#${xMb}`;
|
|
64
|
+
// signingKid: the VTA ships this in the bundle as
|
|
65
|
+
// `admin.signing_key.key_id`; we trust the bundle's value. The
|
|
66
|
+
// canonical shape is `<did>#<signing-multibase>`, which is the did:key
|
|
67
|
+
// identifier itself (the fragment equals the multibase tag).
|
|
68
|
+
const signingKid = `${reply.adminDid}#${didMb}`;
|
|
69
|
+
return {
|
|
70
|
+
did: reply.adminDid,
|
|
71
|
+
signingKid,
|
|
72
|
+
keyAgreementKid,
|
|
73
|
+
edSeed: signingPriv,
|
|
74
|
+
vtaDid: reply.vtaDid,
|
|
75
|
+
...(reply.vtaUrl ? { vtaUrl: reply.vtaUrl } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function decodePrivateKey(multikey, expectedCodec, label) {
|
|
79
|
+
const { codec, key } = multibase.decodeMultikey(multikey);
|
|
80
|
+
if (!constantTimeEqual(codec, expectedCodec)) {
|
|
81
|
+
throw new Error(`provision-integration: ${label} multicodec mismatch — expected ` +
|
|
82
|
+
`${hex(expectedCodec)}, got ${hex(codec)}`);
|
|
83
|
+
}
|
|
84
|
+
if (key.length !== 32) {
|
|
85
|
+
throw new Error(`provision-integration: ${label} key length ${key.length} != 32 bytes`);
|
|
86
|
+
}
|
|
87
|
+
return key;
|
|
88
|
+
}
|
|
89
|
+
function extractDidKeyMultibase(did) {
|
|
90
|
+
if (!did.startsWith("did:key:")) {
|
|
91
|
+
throw new Error(`provision-integration: '${did}' is not a did:key`);
|
|
92
|
+
}
|
|
93
|
+
const mb = did.slice("did:key:".length);
|
|
94
|
+
if (!mb.startsWith("z")) {
|
|
95
|
+
throw new Error(`provision-integration: did:key identifier '${mb}' is not base58btc multibase`);
|
|
96
|
+
}
|
|
97
|
+
return mb;
|
|
98
|
+
}
|
|
99
|
+
function constantTimeEqual(a, b) {
|
|
100
|
+
if (a.length !== b.length)
|
|
101
|
+
return false;
|
|
102
|
+
let acc = 0;
|
|
103
|
+
for (let i = 0; i < a.length; i++)
|
|
104
|
+
acc |= a[i] ^ b[i];
|
|
105
|
+
return acc === 0;
|
|
106
|
+
}
|
|
107
|
+
function hex(bytes) {
|
|
108
|
+
let s = "";
|
|
109
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
110
|
+
s += bytes[i].toString(16).padStart(2, "0");
|
|
111
|
+
}
|
|
112
|
+
return s;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=adopt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adopt.js","sourceRoot":"","sources":["../../src/provision/adopt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SealedBundle } from "./types.js";
|
|
2
|
+
/** Decode armored input into one or more SealedBundles, grouped by Bundle-Id. */
|
|
3
|
+
export declare function decodeArmor(input: string): SealedBundle[];
|
|
4
|
+
/** Build the AAD bytes for one chunk's HPKE seal. Mirrors
|
|
5
|
+
* `ChunkPlaintext::aad` in `chunk.rs`:
|
|
6
|
+
*
|
|
7
|
+
* version || bundle_id || chunk_index_be || total_chunks_be ||
|
|
8
|
+
* digest_algo_len(u8) || digest_algo
|
|
9
|
+
*
|
|
10
|
+
* `digest_algo_len` is capped at 255 (the Rust side uses `u8::MAX`); for
|
|
11
|
+
* `sha256`/`sha512`/`blake3` this never trips. */
|
|
12
|
+
export declare function buildChunkAad(args: {
|
|
13
|
+
version: number;
|
|
14
|
+
bundleId: Uint8Array;
|
|
15
|
+
chunkIndex: number;
|
|
16
|
+
totalChunks: number;
|
|
17
|
+
digestAlgo: string;
|
|
18
|
+
}): Uint8Array;
|
|
19
|
+
//# sourceMappingURL=armor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"armor.d.ts","sourceRoot":"","sources":["../../src/provision/armor.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAgB,YAAY,EAAE,MAAM,YAAY,CAAC;AAoB7D,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE,CA+CzD;AA6GD;;;;;;;mDAOmD;AACnD,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,UAAU,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,UAAU,CAkBb"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// OpenPGP-style ASCII armor decoder for VTA sealed bundles.
|
|
2
|
+
//
|
|
3
|
+
// Port of `vta-sdk/src/sealed_transfer/armor.rs`. The wire shape is one or
|
|
4
|
+
// more BEGIN/END blocks; blocks sharing a `Bundle-Id:` header belong to the
|
|
5
|
+
// same bundle. The armor headers themselves are not signed — they reach the
|
|
6
|
+
// AEAD layer as AAD (built by the per-chunk caller), so a tampered header
|
|
7
|
+
// makes HPKE open fail rather than the armor parser.
|
|
8
|
+
//
|
|
9
|
+
// Frame:
|
|
10
|
+
// -----BEGIN VTA SEALED BUNDLE-----
|
|
11
|
+
// Version: 1
|
|
12
|
+
// Bundle-Id: <32 hex chars>
|
|
13
|
+
// Chunk: 1/N
|
|
14
|
+
// Digest-Algo: sha256
|
|
15
|
+
// <-- blank line
|
|
16
|
+
// <STANDARD base64, 64 chars/line>
|
|
17
|
+
// =<base64 of 3-byte CRC24 (big-endian)>
|
|
18
|
+
// -----END VTA SEALED BUNDLE-----
|
|
19
|
+
//
|
|
20
|
+
// STANDARD base64 (not URL-safe) — the armor format predates anything
|
|
21
|
+
// URL-aware and matches PGP precedent.
|
|
22
|
+
import { base64 } from "@scure/base";
|
|
23
|
+
import { crc24 } from "./crc24.js";
|
|
24
|
+
const BEGIN = "-----BEGIN VTA SEALED BUNDLE-----";
|
|
25
|
+
const END = "-----END VTA SEALED BUNDLE-----";
|
|
26
|
+
const SUPPORTED_VERSION = 1;
|
|
27
|
+
/** Recognised armor headers. Unknown headers are rejected at parse time so a
|
|
28
|
+
* future header that would participate in AAD cannot be silently dropped by
|
|
29
|
+
* this parser — bumping the `Version:` line is the path for adding new ones. */
|
|
30
|
+
const KNOWN_HEADERS = new Set(["Version", "Bundle-Id", "Chunk", "Digest-Algo"]);
|
|
31
|
+
/** Decode armored input into one or more SealedBundles, grouped by Bundle-Id. */
|
|
32
|
+
export function decodeArmor(input) {
|
|
33
|
+
const lines = input.split(/\r?\n/);
|
|
34
|
+
const bundles = [];
|
|
35
|
+
let i = 0;
|
|
36
|
+
while (i < lines.length) {
|
|
37
|
+
if ((lines[i] ?? "").trim() !== BEGIN) {
|
|
38
|
+
i++;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const bodyStart = i + 1;
|
|
42
|
+
let j = bodyStart;
|
|
43
|
+
while (j < lines.length && (lines[j] ?? "").trim() !== END)
|
|
44
|
+
j++;
|
|
45
|
+
if (j >= lines.length) {
|
|
46
|
+
throw new Error("armor: unterminated BEGIN block");
|
|
47
|
+
}
|
|
48
|
+
const block = parseBlock(lines.slice(bodyStart, j));
|
|
49
|
+
const existing = bundles.find((b) => bytesEqual(b.bundleId, block.bundleId));
|
|
50
|
+
if (existing) {
|
|
51
|
+
if (existing.digestAlgo !== block.digestAlgo) {
|
|
52
|
+
throw new Error("armor: digest_algo differs across chunks of the same bundle");
|
|
53
|
+
}
|
|
54
|
+
existing.chunks.push({
|
|
55
|
+
chunkIndex: block.chunkIndex,
|
|
56
|
+
totalChunks: block.totalChunks,
|
|
57
|
+
sealedBytes: block.sealedBytes,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
bundles.push({
|
|
62
|
+
bundleId: block.bundleId,
|
|
63
|
+
digestAlgo: block.digestAlgo,
|
|
64
|
+
chunks: [
|
|
65
|
+
{
|
|
66
|
+
chunkIndex: block.chunkIndex,
|
|
67
|
+
totalChunks: block.totalChunks,
|
|
68
|
+
sealedBytes: block.sealedBytes,
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
i = j + 1;
|
|
74
|
+
}
|
|
75
|
+
if (bundles.length === 0) {
|
|
76
|
+
throw new Error("armor: no BEGIN blocks found");
|
|
77
|
+
}
|
|
78
|
+
return bundles;
|
|
79
|
+
}
|
|
80
|
+
function parseBlock(lines) {
|
|
81
|
+
let idx = 0;
|
|
82
|
+
let version;
|
|
83
|
+
let bundleId;
|
|
84
|
+
let chunk;
|
|
85
|
+
let digestAlgo;
|
|
86
|
+
// Header lines, terminated by a blank line.
|
|
87
|
+
while (idx < lines.length) {
|
|
88
|
+
const raw = lines[idx] ?? "";
|
|
89
|
+
const trimmed = raw.replace(/\s+$/, "");
|
|
90
|
+
idx++;
|
|
91
|
+
if (trimmed.length === 0)
|
|
92
|
+
break;
|
|
93
|
+
const colon = trimmed.indexOf(":");
|
|
94
|
+
if (colon < 0) {
|
|
95
|
+
throw new Error(`armor: bad header: '${trimmed}'`);
|
|
96
|
+
}
|
|
97
|
+
const key = trimmed.slice(0, colon).trim();
|
|
98
|
+
const value = trimmed.slice(colon + 1).trim();
|
|
99
|
+
if (!KNOWN_HEADERS.has(key)) {
|
|
100
|
+
throw new Error(`armor: unknown header '${key}' (rejecting forward-compat hazard)`);
|
|
101
|
+
}
|
|
102
|
+
switch (key) {
|
|
103
|
+
case "Version": {
|
|
104
|
+
const n = Number.parseInt(value, 10);
|
|
105
|
+
if (!Number.isFinite(n))
|
|
106
|
+
throw new Error(`armor: bad Version: ${value}`);
|
|
107
|
+
version = n;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case "Bundle-Id": {
|
|
111
|
+
bundleId = hexDecode(value);
|
|
112
|
+
if (bundleId.length !== 16) {
|
|
113
|
+
throw new Error(`armor: Bundle-Id must be 16 bytes (got ${bundleId.length})`);
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case "Chunk": {
|
|
118
|
+
const slash = value.indexOf("/");
|
|
119
|
+
if (slash < 0)
|
|
120
|
+
throw new Error(`armor: bad Chunk header: ${value}`);
|
|
121
|
+
const oneBased = Number.parseInt(value.slice(0, slash), 10);
|
|
122
|
+
const total = Number.parseInt(value.slice(slash + 1), 10);
|
|
123
|
+
if (!Number.isFinite(oneBased) ||
|
|
124
|
+
!Number.isFinite(total) ||
|
|
125
|
+
oneBased === 0 ||
|
|
126
|
+
oneBased > total) {
|
|
127
|
+
throw new Error(`armor: chunk ${oneBased}/${total} out of range`);
|
|
128
|
+
}
|
|
129
|
+
chunk = { index: oneBased - 1, total };
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case "Digest-Algo": {
|
|
133
|
+
digestAlgo = value;
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (version === undefined)
|
|
139
|
+
throw new Error("armor: missing Version");
|
|
140
|
+
if (version !== SUPPORTED_VERSION) {
|
|
141
|
+
throw new Error(`armor: unsupported version ${version}`);
|
|
142
|
+
}
|
|
143
|
+
if (!bundleId)
|
|
144
|
+
throw new Error("armor: missing Bundle-Id");
|
|
145
|
+
if (!chunk)
|
|
146
|
+
throw new Error("armor: missing Chunk header");
|
|
147
|
+
if (!digestAlgo)
|
|
148
|
+
throw new Error("armor: missing Digest-Algo");
|
|
149
|
+
// Body: base64 lines until the `=<base64>` CRC line.
|
|
150
|
+
let b64 = "";
|
|
151
|
+
let crcB64;
|
|
152
|
+
while (idx < lines.length) {
|
|
153
|
+
const raw = lines[idx] ?? "";
|
|
154
|
+
const trimmed = raw.replace(/\s+$/, "");
|
|
155
|
+
idx++;
|
|
156
|
+
if (trimmed.length === 0)
|
|
157
|
+
continue;
|
|
158
|
+
if (trimmed.startsWith("=")) {
|
|
159
|
+
crcB64 = trimmed.slice(1);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
b64 += trimmed;
|
|
163
|
+
}
|
|
164
|
+
if (crcB64 === undefined)
|
|
165
|
+
throw new Error("armor: missing CRC line");
|
|
166
|
+
const sealedBytes = base64.decode(b64);
|
|
167
|
+
const crcBytes = base64.decode(crcB64);
|
|
168
|
+
if (crcBytes.length !== 3) {
|
|
169
|
+
throw new Error(`armor: CRC payload must be 3 bytes (got ${crcBytes.length})`);
|
|
170
|
+
}
|
|
171
|
+
const expected = (crcBytes[0] << 16) | (crcBytes[1] << 8) | crcBytes[2];
|
|
172
|
+
const got = crc24(sealedBytes);
|
|
173
|
+
if (got !== expected) {
|
|
174
|
+
throw new Error(`armor: CRC24 mismatch (expected 0x${expected.toString(16)}, got 0x${got.toString(16)})`);
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
bundleId,
|
|
178
|
+
digestAlgo,
|
|
179
|
+
chunkIndex: chunk.index,
|
|
180
|
+
totalChunks: chunk.total,
|
|
181
|
+
sealedBytes,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/** Build the AAD bytes for one chunk's HPKE seal. Mirrors
|
|
185
|
+
* `ChunkPlaintext::aad` in `chunk.rs`:
|
|
186
|
+
*
|
|
187
|
+
* version || bundle_id || chunk_index_be || total_chunks_be ||
|
|
188
|
+
* digest_algo_len(u8) || digest_algo
|
|
189
|
+
*
|
|
190
|
+
* `digest_algo_len` is capped at 255 (the Rust side uses `u8::MAX`); for
|
|
191
|
+
* `sha256`/`sha512`/`blake3` this never trips. */
|
|
192
|
+
export function buildChunkAad(args) {
|
|
193
|
+
if (args.bundleId.length !== 16) {
|
|
194
|
+
throw new Error(`buildChunkAad: bundleId must be 16 bytes (got ${args.bundleId.length})`);
|
|
195
|
+
}
|
|
196
|
+
const algoBytes = new TextEncoder().encode(args.digestAlgo);
|
|
197
|
+
const algoLen = Math.min(algoBytes.length, 255);
|
|
198
|
+
const buf = new Uint8Array(1 + 16 + 2 + 2 + 1 + algoLen);
|
|
199
|
+
let p = 0;
|
|
200
|
+
buf[p++] = args.version;
|
|
201
|
+
buf.set(args.bundleId, p);
|
|
202
|
+
p += 16;
|
|
203
|
+
buf[p++] = (args.chunkIndex >> 8) & 0xff;
|
|
204
|
+
buf[p++] = args.chunkIndex & 0xff;
|
|
205
|
+
buf[p++] = (args.totalChunks >> 8) & 0xff;
|
|
206
|
+
buf[p++] = args.totalChunks & 0xff;
|
|
207
|
+
buf[p++] = algoLen;
|
|
208
|
+
buf.set(algoBytes.subarray(0, algoLen), p);
|
|
209
|
+
return buf;
|
|
210
|
+
}
|
|
211
|
+
/** Decode a (lowercase or mixed-case) hex string into bytes. The armor format
|
|
212
|
+
* emits lowercase only, but accepting both costs nothing and matches the
|
|
213
|
+
* Rust parser's behaviour. */
|
|
214
|
+
function hexDecode(s) {
|
|
215
|
+
if (s.length % 2 !== 0)
|
|
216
|
+
throw new Error(`hex: odd length: ${s.length}`);
|
|
217
|
+
const out = new Uint8Array(s.length / 2);
|
|
218
|
+
for (let i = 0; i < s.length; i += 2) {
|
|
219
|
+
const hi = hexNibble(s.charCodeAt(i));
|
|
220
|
+
const lo = hexNibble(s.charCodeAt(i + 1));
|
|
221
|
+
out[i / 2] = (hi << 4) | lo;
|
|
222
|
+
}
|
|
223
|
+
return out;
|
|
224
|
+
}
|
|
225
|
+
function hexNibble(c) {
|
|
226
|
+
if (c >= 0x30 && c <= 0x39)
|
|
227
|
+
return c - 0x30;
|
|
228
|
+
if (c >= 0x61 && c <= 0x66)
|
|
229
|
+
return c - 0x61 + 10;
|
|
230
|
+
if (c >= 0x41 && c <= 0x46)
|
|
231
|
+
return c - 0x41 + 10;
|
|
232
|
+
throw new Error(`hex: non-hex byte 0x${c.toString(16)}`);
|
|
233
|
+
}
|
|
234
|
+
function bytesEqual(a, b) {
|
|
235
|
+
if (a.length !== b.length)
|
|
236
|
+
return false;
|
|
237
|
+
for (let i = 0; i < a.length; i++) {
|
|
238
|
+
if (a[i] !== b[i])
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=armor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"armor.js","sourceRoot":"","sources":["../../src/provision/armor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function crc24(data: Uint8Array): number;
|
|
2
|
+
/** Encode a 24-bit CRC value as the 3 big-endian bytes the armor body line
|
|
3
|
+
* base64-encodes after the `=` prefix. */
|
|
4
|
+
export declare function crc24ToBytes(crc: number): Uint8Array;
|
|
5
|
+
//# sourceMappingURL=crc24.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crc24.d.ts","sourceRoot":"","sources":["../../src/provision/crc24.ts"],"names":[],"mappings":"AAcA,wBAAgB,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAU9C;AAED;2CAC2C;AAC3C,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAEpD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// PGP-style CRC24 over raw bytes. Used by the armor parser to detect
|
|
2
|
+
// pasted-text corruption before the AEAD layer has a chance to.
|
|
3
|
+
//
|
|
4
|
+
// Mirrors `crc24` in `vta-sdk/src/sealed_transfer/armor.rs`:
|
|
5
|
+
// init = 0x00B704CE
|
|
6
|
+
// poly = 0x01864CFB
|
|
7
|
+
// width = 24 bits, output big-endian, no reflection.
|
|
8
|
+
//
|
|
9
|
+
// Reference vector at the bottom of this file; the armor parser must
|
|
10
|
+
// reject a corrupted body via `Crc24Mismatch` before HPKE sees it.
|
|
11
|
+
const INIT = 0x00b704ce;
|
|
12
|
+
const POLY = 0x01864cfb;
|
|
13
|
+
export function crc24(data) {
|
|
14
|
+
let crc = INIT;
|
|
15
|
+
for (let i = 0; i < data.length; i++) {
|
|
16
|
+
crc ^= data[i] << 16;
|
|
17
|
+
for (let bit = 0; bit < 8; bit++) {
|
|
18
|
+
crc <<= 1;
|
|
19
|
+
if ((crc & 0x01000000) !== 0)
|
|
20
|
+
crc ^= POLY;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return crc & 0x00ffffff;
|
|
24
|
+
}
|
|
25
|
+
/** Encode a 24-bit CRC value as the 3 big-endian bytes the armor body line
|
|
26
|
+
* base64-encodes after the `=` prefix. */
|
|
27
|
+
export function crc24ToBytes(crc) {
|
|
28
|
+
return new Uint8Array([(crc >> 16) & 0xff, (crc >> 8) & 0xff, crc & 0xff]);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=crc24.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crc24.js","sourceRoot":"","sources":["../../src/provision/crc24.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,gEAAgE;AAChE,EAAE;AACF,6DAA6D;AAC7D,uBAAuB;AACvB,uBAAuB;AACvB,uDAAuD;AACvD,EAAE;AACF,qEAAqE;AACrE,mEAAmE;AAEnE,MAAM,IAAI,GAAG,UAAU,CAAC;AACxB,MAAM,IAAI,GAAG,UAAU,CAAC;AAExB,MAAM,UAAU,KAAK,CAAC,IAAgB;IACpC,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,GAAG,IAAK,IAAI,CAAC,CAAC,CAAY,IAAI,EAAE,CAAC;QACjC,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC;YACjC,GAAG,KAAK,CAAC,CAAC;YACV,IAAI,CAAC,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC;gBAAE,GAAG,IAAI,IAAI,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,OAAO,GAAG,GAAG,UAAU,CAAC;AAC1B,CAAC;AAED;2CAC2C;AAC3C,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface HpkeOpenInput {
|
|
2
|
+
/** 32-byte X25519 secret. Derived from the wallet's Ed25519 seed via
|
|
3
|
+
* Montgomery conversion + clamping (see `bundle-secret.ts`). */
|
|
4
|
+
recipientSecret: Uint8Array;
|
|
5
|
+
/** 32-byte KEM encapsulation (the sender's ephemeral X25519 pubkey). */
|
|
6
|
+
kemEncap: Uint8Array;
|
|
7
|
+
/** AEAD ciphertext (ciphertext || tag). */
|
|
8
|
+
ciphertext: Uint8Array;
|
|
9
|
+
/** Additional authenticated data — built from the chunk header via
|
|
10
|
+
* `buildChunkAad`. Must byte-match the AAD used at seal time. */
|
|
11
|
+
aad: Uint8Array;
|
|
12
|
+
}
|
|
13
|
+
/** Open one HPKE-sealed chunk. Returns the plaintext bytes (CBOR-encoded
|
|
14
|
+
* ChunkPlaintext, decoded by the caller). Throws on AEAD failure — wrong
|
|
15
|
+
* recipient secret, tampered AAD, or replayed ciphertext. */
|
|
16
|
+
export declare function hpkeOpen(input: HpkeOpenInput): Promise<Uint8Array>;
|
|
17
|
+
//# sourceMappingURL=hpke.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hpke.d.ts","sourceRoot":"","sources":["../../src/provision/hpke.ts"],"names":[],"mappings":"AAqCA,MAAM,WAAW,aAAa;IAC5B;qEACiE;IACjE,eAAe,EAAE,UAAU,CAAC;IAC5B,wEAAwE;IACxE,QAAQ,EAAE,UAAU,CAAC;IACrB,2CAA2C;IAC3C,UAAU,EAAE,UAAU,CAAC;IACvB;sEACkE;IAClE,GAAG,EAAE,UAAU,CAAC;CACjB;AAED;;8DAE8D;AAC9D,wBAAsB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAmBxE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// HPKE (RFC 9180) base-mode open for VTA sealed bundles.
|
|
2
|
+
//
|
|
3
|
+
// Suite (pinned, matches `vta-sdk/src/sealed_transfer/hpke.rs`):
|
|
4
|
+
// KEM: DHKEM(X25519, HKDF-SHA256)
|
|
5
|
+
// KDF: HKDF-SHA256
|
|
6
|
+
// AEAD: ChaCha20-Poly1305
|
|
7
|
+
//
|
|
8
|
+
// Single-shot, base mode (no PSK, no auth-mode KEM). The chunk header is
|
|
9
|
+
// bound as AEAD AAD by the caller (`buildChunkAad` in `armor.ts`); the info
|
|
10
|
+
// string `vta-sealed-transfer/v1` domain-separates this suite from any
|
|
11
|
+
// future use of the same primitives.
|
|
12
|
+
//
|
|
13
|
+
// We feed the X25519 secret as raw 32-byte material via the suite's KEM
|
|
14
|
+
// `importKey("raw", secret, false)` — the Rust side derives the X25519
|
|
15
|
+
// secret from an Ed25519 seed via SHA-512 + clamping; the wallet does the
|
|
16
|
+
// same conversion in `bundle-secret.ts` before calling `hpkeOpen` here.
|
|
17
|
+
import { CipherSuite, DhkemX25519HkdfSha256, HkdfSha256 } from "@hpke/core";
|
|
18
|
+
import { Chacha20Poly1305 } from "@hpke/chacha20poly1305";
|
|
19
|
+
/** Domain-binding info string. Hardcoded — a different envelope format means
|
|
20
|
+
* a different info string, not a parameter the caller picks. */
|
|
21
|
+
const HPKE_INFO = new TextEncoder().encode("vta-sealed-transfer/v1");
|
|
22
|
+
let _suite = null;
|
|
23
|
+
function suite() {
|
|
24
|
+
if (!_suite) {
|
|
25
|
+
_suite = new CipherSuite({
|
|
26
|
+
kem: new DhkemX25519HkdfSha256(),
|
|
27
|
+
kdf: new HkdfSha256(),
|
|
28
|
+
aead: new Chacha20Poly1305(),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return _suite;
|
|
32
|
+
}
|
|
33
|
+
/** Open one HPKE-sealed chunk. Returns the plaintext bytes (CBOR-encoded
|
|
34
|
+
* ChunkPlaintext, decoded by the caller). Throws on AEAD failure — wrong
|
|
35
|
+
* recipient secret, tampered AAD, or replayed ciphertext. */
|
|
36
|
+
export async function hpkeOpen(input) {
|
|
37
|
+
if (input.recipientSecret.length !== 32) {
|
|
38
|
+
throw new Error(`hpke: recipientSecret must be 32 bytes (got ${input.recipientSecret.length})`);
|
|
39
|
+
}
|
|
40
|
+
if (input.kemEncap.length !== 32) {
|
|
41
|
+
throw new Error(`hpke: kemEncap must be 32 bytes (got ${input.kemEncap.length})`);
|
|
42
|
+
}
|
|
43
|
+
const cs = suite();
|
|
44
|
+
const recipientKey = await cs.kem.importKey("raw", asArrayBuffer(input.recipientSecret), false);
|
|
45
|
+
const pt = await cs.open({
|
|
46
|
+
recipientKey,
|
|
47
|
+
enc: asArrayBuffer(input.kemEncap),
|
|
48
|
+
info: HPKE_INFO,
|
|
49
|
+
}, asArrayBuffer(input.ciphertext), asArrayBuffer(input.aad));
|
|
50
|
+
return new Uint8Array(pt);
|
|
51
|
+
}
|
|
52
|
+
/** Force-detached ArrayBuffer copy. `@hpke/core` rejects typed-array views
|
|
53
|
+
* whose underlying buffer is a SharedArrayBuffer or has unusual byteOffset
|
|
54
|
+
* semantics; a fresh copy normalises everything. */
|
|
55
|
+
function asArrayBuffer(u8) {
|
|
56
|
+
const out = new ArrayBuffer(u8.byteLength);
|
|
57
|
+
new Uint8Array(out).set(u8);
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=hpke.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hpke.js","sourceRoot":"","sources":["../../src/provision/hpke.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,EAAE;AACF,iEAAiE;AACjE,qCAAqC;AACrC,sBAAsB;AACtB,4BAA4B;AAC5B,EAAE;AACF,yEAAyE;AACzE,4EAA4E;AAC5E,uEAAuE;AACvE,qCAAqC;AACrC,EAAE;AACF,wEAAwE;AACxE,uEAAuE;AACvE,0EAA0E;AAC1E,wEAAwE;AAExE,OAAO,EAAE,WAAW,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D;iEACiE;AACjE,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC;AAErE,IAAI,MAAM,GAAuB,IAAI,CAAC;AAEtC,SAAS,KAAK;IACZ,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,GAAG,IAAI,WAAW,CAAC;YACvB,GAAG,EAAE,IAAI,qBAAqB,EAAE;YAChC,GAAG,EAAE,IAAI,UAAU,EAAE;YACrB,IAAI,EAAE,IAAI,gBAAgB,EAAE;SAC7B,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAeD;;8DAE8D;AAC9D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,KAAoB;IACjD,IAAI,KAAK,CAAC,eAAe,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,+CAA+C,KAAK,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;IAClG,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,EAAE,GAAG,KAAK,EAAE,CAAC;IACnB,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,eAAe,CAAC,EAAE,KAAK,CAAC,CAAC;IAChG,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CACtB;QACE,YAAY;QACZ,GAAG,EAAE,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC;QAClC,IAAI,EAAE,SAAS;KAChB,EACD,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,EAC/B,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CACzB,CAAC;IACF,OAAO,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;AAC5B,CAAC;AAED;;qDAEqD;AACrD,SAAS,aAAa,CAAC,EAAc;IACnC,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5B,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { decodeArmor, buildChunkAad } from "./armor.js";
|
|
2
|
+
export { crc24, crc24ToBytes } from "./crc24.js";
|
|
3
|
+
export { hpkeOpen, type HpkeOpenInput } from "./hpke.js";
|
|
4
|
+
export { openSealedBundle, openBundle, openAdminRotationBundle, type OpenedBundle } from "./open.js";
|
|
5
|
+
export { buildBootstrapRequest, type BootstrapAsk, type BootstrapRequestVp, type BuildBootstrapRequestOptions, type DidTemplateRef, } from "./request.js";
|
|
6
|
+
export { sendProvisionIntegration, ProvisionProblemReportError, type ProblemReportPayload, type ProvisionIntegrationRequestBody, type ProvisionIntegrationResponseBody, type ProvisionSummary, type SendProvisionIntegrationOptions, } from "./send.js";
|
|
7
|
+
export { runProvisionIntegration, type MinimalAdminReply, type RunProvisionIntegrationOptions, } from "./run.js";
|
|
8
|
+
export { holderInputsFromAdminReply, type HolderInputsFromAdminReply, } from "./adopt.js";
|
|
9
|
+
export type { AdminRotationPayload, ArmoredChunk, AssertionProof, ChunkPlaintext, DidKeyMaterial, HpkeSealed, KeyPair, ProducerAssertion, SealedBundle, SealedPayloadV1, TemplateBootstrapPayload, VtaTrustBundle, } from "./types.js";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provision/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,uBAAuB,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AACrG,OAAO,EACL,qBAAqB,EACrB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,KAAK,4BAA4B,EACjC,KAAK,cAAc,GACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,wBAAwB,EACxB,2BAA2B,EAC3B,KAAK,oBAAoB,EACzB,KAAK,+BAA+B,EACpC,KAAK,gCAAgC,EACrC,KAAK,gBAAgB,EACrB,KAAK,+BAA+B,GACrC,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,8BAA8B,GACpC,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,0BAA0B,EAC1B,KAAK,0BAA0B,GAChC,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,oBAAoB,EACpB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,cAAc,EACd,UAAU,EACV,OAAO,EACP,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,wBAAwB,EACxB,cAAc,GACf,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Public surface for the wallet's sealed-bundle openers.
|
|
2
|
+
//
|
|
3
|
+
// The wallet calls `openAdminRotationBundle` on the armored response from a
|
|
4
|
+
// VTA's `provision-integration/1.0/provision-integration-result` reply,
|
|
5
|
+
// using the same Ed25519 seed it signed the request with. Returns the
|
|
6
|
+
// freshly-minted long-term admin DID + private keys the wallet adopts as
|
|
7
|
+
// its holder identity.
|
|
8
|
+
export { decodeArmor, buildChunkAad } from "./armor.js";
|
|
9
|
+
export { crc24, crc24ToBytes } from "./crc24.js";
|
|
10
|
+
export { hpkeOpen } from "./hpke.js";
|
|
11
|
+
export { openSealedBundle, openBundle, openAdminRotationBundle } from "./open.js";
|
|
12
|
+
export { buildBootstrapRequest, } from "./request.js";
|
|
13
|
+
export { sendProvisionIntegration, ProvisionProblemReportError, } from "./send.js";
|
|
14
|
+
export { runProvisionIntegration, } from "./run.js";
|
|
15
|
+
export { holderInputsFromAdminReply, } from "./adopt.js";
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/provision/index.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,EAAE;AACF,4EAA4E;AAC5E,wEAAwE;AACxE,sEAAsE;AACtE,yEAAyE;AACzE,uBAAuB;AAEvB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAsB,MAAM,WAAW,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,uBAAuB,EAAqB,MAAM,WAAW,CAAC;AACrG,OAAO,EACL,qBAAqB,GAKtB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,wBAAwB,EACxB,2BAA2B,GAM5B,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,uBAAuB,GAGxB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,0BAA0B,GAE3B,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AdminRotationPayload, SealedBundle, SealedPayloadV1 } from "./types.js";
|
|
2
|
+
export interface OpenedBundle {
|
|
3
|
+
bundleId: Uint8Array;
|
|
4
|
+
digestAlgo: string;
|
|
5
|
+
payload: SealedPayloadV1;
|
|
6
|
+
}
|
|
7
|
+
/** Open the first bundle in `armored` using the recipient's Ed25519 seed.
|
|
8
|
+
*
|
|
9
|
+
* The seed is converted to its X25519 secret via Montgomery clamping
|
|
10
|
+
* (same derivation the Rust side uses; see
|
|
11
|
+
* `affinidi_crypto::ed25519::ed25519_private_to_x25519`). The wallet's
|
|
12
|
+
* ephemeral Ed25519 seed produced at onboarding is what gets passed here.
|
|
13
|
+
*
|
|
14
|
+
* If the armored input contains multiple bundles (different Bundle-Ids),
|
|
15
|
+
* this opens only the first. The provision-integration reply has exactly
|
|
16
|
+
* one bundle today; multi-bundle armored payloads are reserved for future
|
|
17
|
+
* flows.
|
|
18
|
+
*/
|
|
19
|
+
export declare function openSealedBundle(armored: string, edSeed: Uint8Array): Promise<OpenedBundle>;
|
|
20
|
+
/** Lower-level: open a pre-parsed SealedBundle with an X25519 secret. */
|
|
21
|
+
export declare function openBundle(bundle: SealedBundle, x25519Secret: Uint8Array): Promise<OpenedBundle>;
|
|
22
|
+
/** Convenience: open the bundle and assert it carries an `AdminRotation`
|
|
23
|
+
* variant, returning the typed payload. Throws if any other variant. */
|
|
24
|
+
export declare function openAdminRotationBundle(armored: string, edSeed: Uint8Array): Promise<{
|
|
25
|
+
bundleId: Uint8Array;
|
|
26
|
+
payload: AdminRotationPayload;
|
|
27
|
+
}>;
|
|
28
|
+
//# sourceMappingURL=open.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open.d.ts","sourceRoot":"","sources":["../../src/provision/open.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,oBAAoB,EAGpB,YAAY,EACZ,eAAe,EAEhB,MAAM,YAAY,CAAC;AASpB,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,UAAU,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,eAAe,CAAC;CAC1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,YAAY,CAAC,CAQvB;AAED,yEAAyE;AACzE,wBAAsB,UAAU,CAC9B,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,UAAU,GACvB,OAAO,CAAC,YAAY,CAAC,CAkEvB;AAED;yEACyE;AACzE,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC;IAAE,QAAQ,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,oBAAoB,CAAA;CAAE,CAAC,CAUlE"}
|