@verbeth/sdk 0.1.4 → 0.1.5
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 +16 -167
- package/dist/esm/src/client/HsrTagIndex.d.ts +77 -0
- package/dist/esm/src/client/HsrTagIndex.d.ts.map +1 -0
- package/dist/esm/src/client/HsrTagIndex.js +157 -0
- package/dist/esm/src/client/PendingManager.d.ts +65 -0
- package/dist/esm/src/client/PendingManager.d.ts.map +1 -0
- package/dist/esm/src/client/PendingManager.js +84 -0
- package/dist/esm/src/client/SessionManager.d.ts +65 -0
- package/dist/esm/src/client/SessionManager.d.ts.map +1 -0
- package/dist/esm/src/client/SessionManager.js +146 -0
- package/dist/esm/src/client/VerbethClient.d.ts +153 -99
- package/dist/esm/src/client/VerbethClient.d.ts.map +1 -1
- package/dist/esm/src/client/VerbethClient.js +429 -123
- package/dist/esm/src/client/VerbethClientBuilder.d.ts +105 -0
- package/dist/esm/src/client/VerbethClientBuilder.d.ts.map +1 -0
- package/dist/esm/src/client/VerbethClientBuilder.js +146 -0
- package/dist/esm/src/client/hsrMatcher.d.ts +22 -0
- package/dist/esm/src/client/hsrMatcher.d.ts.map +1 -0
- package/dist/esm/src/client/hsrMatcher.js +31 -0
- package/dist/esm/src/client/index.d.ts +6 -1
- package/dist/esm/src/client/index.d.ts.map +1 -1
- package/dist/esm/src/client/index.js +2 -0
- package/dist/esm/src/client/types.d.ts +151 -10
- package/dist/esm/src/client/types.d.ts.map +1 -1
- package/dist/esm/src/crypto(old).d.ts +46 -0
- package/dist/esm/src/crypto(old).d.ts.map +1 -0
- package/dist/esm/src/crypto(old).js +137 -0
- package/dist/esm/src/crypto.d.ts +7 -29
- package/dist/esm/src/crypto.d.ts.map +1 -1
- package/dist/esm/src/crypto.js +36 -72
- package/dist/esm/src/executor.d.ts +1 -2
- package/dist/esm/src/executor.d.ts.map +1 -1
- package/dist/esm/src/executor.js +8 -24
- package/dist/esm/src/handshake.d.ts +51 -0
- package/dist/esm/src/handshake.d.ts.map +1 -0
- package/dist/esm/src/handshake.js +105 -0
- package/dist/esm/src/identity.d.ts +24 -18
- package/dist/esm/src/identity.d.ts.map +1 -1
- package/dist/esm/src/identity.js +126 -31
- package/dist/esm/src/index.d.ts +10 -7
- package/dist/esm/src/index.d.ts.map +1 -1
- package/dist/esm/src/index.js +9 -7
- package/dist/esm/src/payload.d.ts +3 -30
- package/dist/esm/src/payload.d.ts.map +1 -1
- package/dist/esm/src/payload.js +3 -77
- package/dist/esm/src/pq/kem.d.ts +33 -0
- package/dist/esm/src/pq/kem.d.ts.map +1 -0
- package/dist/esm/src/pq/kem.js +40 -0
- package/dist/esm/src/ratchet/auth.d.ts +34 -0
- package/dist/esm/src/ratchet/auth.d.ts.map +1 -0
- package/dist/esm/src/ratchet/auth.js +88 -0
- package/dist/esm/src/ratchet/codec.d.ts +52 -0
- package/dist/esm/src/ratchet/codec.d.ts.map +1 -0
- package/dist/esm/src/ratchet/codec.js +127 -0
- package/dist/esm/src/ratchet/decrypt.d.ts +28 -0
- package/dist/esm/src/ratchet/decrypt.d.ts.map +1 -0
- package/dist/esm/src/ratchet/decrypt.js +255 -0
- package/dist/esm/src/ratchet/encrypt.d.ts +17 -0
- package/dist/esm/src/ratchet/encrypt.d.ts.map +1 -0
- package/dist/esm/src/ratchet/encrypt.js +78 -0
- package/dist/esm/src/ratchet/index.d.ts +8 -0
- package/dist/esm/src/ratchet/index.d.ts.map +1 -0
- package/dist/esm/src/ratchet/index.js +8 -0
- package/dist/esm/src/ratchet/kdf.d.ts +60 -0
- package/dist/esm/src/ratchet/kdf.d.ts.map +1 -0
- package/dist/esm/src/ratchet/kdf.js +91 -0
- package/dist/esm/src/ratchet/session.d.ts +43 -0
- package/dist/esm/src/ratchet/session.d.ts.map +1 -0
- package/dist/esm/src/ratchet/session.js +139 -0
- package/dist/esm/src/ratchet/types.d.ts +168 -0
- package/dist/esm/src/ratchet/types.d.ts.map +1 -0
- package/dist/esm/src/ratchet/types.js +27 -0
- package/dist/esm/src/safeSessionSigner.d.ts +35 -0
- package/dist/esm/src/safeSessionSigner.d.ts.map +1 -0
- package/dist/esm/src/safeSessionSigner.js +59 -0
- package/dist/esm/src/send.d.ts +32 -24
- package/dist/esm/src/send.d.ts.map +1 -1
- package/dist/esm/src/send.js +84 -39
- package/dist/esm/src/types.d.ts +8 -13
- package/dist/esm/src/types.d.ts.map +1 -1
- package/dist/esm/src/utils/safeSessionSigner.d.ts +23 -0
- package/dist/esm/src/utils/safeSessionSigner.d.ts.map +1 -0
- package/dist/esm/src/utils/safeSessionSigner.js +59 -0
- package/dist/esm/src/utils/txQueue.d.ts +12 -0
- package/dist/esm/src/utils/txQueue.d.ts.map +1 -0
- package/dist/esm/src/utils/txQueue.js +25 -0
- package/dist/esm/src/utils.d.ts +2 -3
- package/dist/esm/src/utils.d.ts.map +1 -1
- package/dist/esm/src/utils.js +5 -5
- package/dist/esm/src/verify.d.ts +9 -25
- package/dist/esm/src/verify.d.ts.map +1 -1
- package/dist/esm/src/verify.js +49 -50
- package/dist/src/client/HsrTagIndex.d.ts +77 -0
- package/dist/src/client/HsrTagIndex.d.ts.map +1 -0
- package/dist/src/client/HsrTagIndex.js +157 -0
- package/dist/src/client/PendingManager.d.ts +65 -0
- package/dist/src/client/PendingManager.d.ts.map +1 -0
- package/dist/src/client/PendingManager.js +84 -0
- package/dist/src/client/SessionManager.d.ts +65 -0
- package/dist/src/client/SessionManager.d.ts.map +1 -0
- package/dist/src/client/SessionManager.js +146 -0
- package/dist/src/client/VerbethClient.d.ts +153 -99
- package/dist/src/client/VerbethClient.d.ts.map +1 -1
- package/dist/src/client/VerbethClient.js +429 -123
- package/dist/src/client/VerbethClientBuilder.d.ts +105 -0
- package/dist/src/client/VerbethClientBuilder.d.ts.map +1 -0
- package/dist/src/client/VerbethClientBuilder.js +146 -0
- package/dist/src/client/hsrMatcher.d.ts +22 -0
- package/dist/src/client/hsrMatcher.d.ts.map +1 -0
- package/dist/src/client/hsrMatcher.js +31 -0
- package/dist/src/client/index.d.ts +6 -1
- package/dist/src/client/index.d.ts.map +1 -1
- package/dist/src/client/index.js +2 -0
- package/dist/src/client/types.d.ts +151 -10
- package/dist/src/client/types.d.ts.map +1 -1
- package/dist/src/crypto(old).d.ts +46 -0
- package/dist/src/crypto(old).d.ts.map +1 -0
- package/dist/src/crypto(old).js +137 -0
- package/dist/src/crypto.d.ts +7 -29
- package/dist/src/crypto.d.ts.map +1 -1
- package/dist/src/crypto.js +36 -72
- package/dist/src/executor.d.ts +1 -2
- package/dist/src/executor.d.ts.map +1 -1
- package/dist/src/executor.js +8 -24
- package/dist/src/handshake.d.ts +51 -0
- package/dist/src/handshake.d.ts.map +1 -0
- package/dist/src/handshake.js +105 -0
- package/dist/src/identity.d.ts +24 -18
- package/dist/src/identity.d.ts.map +1 -1
- package/dist/src/identity.js +126 -31
- package/dist/src/index.d.ts +10 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -7
- package/dist/src/payload.d.ts +3 -30
- package/dist/src/payload.d.ts.map +1 -1
- package/dist/src/payload.js +3 -77
- package/dist/src/pq/kem.d.ts +33 -0
- package/dist/src/pq/kem.d.ts.map +1 -0
- package/dist/src/pq/kem.js +40 -0
- package/dist/src/ratchet/auth.d.ts +34 -0
- package/dist/src/ratchet/auth.d.ts.map +1 -0
- package/dist/src/ratchet/auth.js +88 -0
- package/dist/src/ratchet/codec.d.ts +52 -0
- package/dist/src/ratchet/codec.d.ts.map +1 -0
- package/dist/src/ratchet/codec.js +127 -0
- package/dist/src/ratchet/decrypt.d.ts +28 -0
- package/dist/src/ratchet/decrypt.d.ts.map +1 -0
- package/dist/src/ratchet/decrypt.js +255 -0
- package/dist/src/ratchet/encrypt.d.ts +17 -0
- package/dist/src/ratchet/encrypt.d.ts.map +1 -0
- package/dist/src/ratchet/encrypt.js +78 -0
- package/dist/src/ratchet/index.d.ts +8 -0
- package/dist/src/ratchet/index.d.ts.map +1 -0
- package/dist/src/ratchet/index.js +8 -0
- package/dist/src/ratchet/kdf.d.ts +60 -0
- package/dist/src/ratchet/kdf.d.ts.map +1 -0
- package/dist/src/ratchet/kdf.js +91 -0
- package/dist/src/ratchet/session.d.ts +43 -0
- package/dist/src/ratchet/session.d.ts.map +1 -0
- package/dist/src/ratchet/session.js +139 -0
- package/dist/src/ratchet/types.d.ts +168 -0
- package/dist/src/ratchet/types.d.ts.map +1 -0
- package/dist/src/ratchet/types.js +27 -0
- package/dist/src/safeSessionSigner.d.ts +35 -0
- package/dist/src/safeSessionSigner.d.ts.map +1 -0
- package/dist/src/safeSessionSigner.js +59 -0
- package/dist/src/send.d.ts +32 -24
- package/dist/src/send.d.ts.map +1 -1
- package/dist/src/send.js +84 -39
- package/dist/src/types.d.ts +8 -13
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/utils/safeSessionSigner.d.ts +23 -0
- package/dist/src/utils/safeSessionSigner.d.ts.map +1 -0
- package/dist/src/utils/safeSessionSigner.js +59 -0
- package/dist/src/utils/txQueue.d.ts +12 -0
- package/dist/src/utils/txQueue.d.ts.map +1 -0
- package/dist/src/utils/txQueue.js +25 -0
- package/dist/src/utils.d.ts +2 -3
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +5 -5
- package/dist/src/verify.d.ts +9 -25
- package/dist/src/verify.d.ts.map +1 -1
- package/dist/src/verify.js +49 -50
- package/package.json +2 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// packages/sdk/src/handshake.ts
|
|
2
|
+
import { keccak256, toUtf8Bytes, hexlify, getBytes } from "ethers";
|
|
3
|
+
import nacl from 'tweetnacl';
|
|
4
|
+
import { encryptStructuredPayload } from './crypto.js';
|
|
5
|
+
import { serializeHandshakeContent, encodeUnifiedPubKeys, createHandshakeResponseContent, } from './payload.js';
|
|
6
|
+
import { computeHybridTagFromResponder } from './crypto.js';
|
|
7
|
+
import { kem } from './pq/kem.js';
|
|
8
|
+
/**
|
|
9
|
+
* Initiates an on-chain handshake with unified keys and mandatory identity proof.
|
|
10
|
+
* Executor-agnostic: works with EOA, UserOp, and Direct EntryPoint
|
|
11
|
+
*
|
|
12
|
+
* @returns Transaction, ephemeral keypair, and KEM keypair (must be persisted for session init)
|
|
13
|
+
*/
|
|
14
|
+
export async function initiateHandshake({ executor, recipientAddress, identityKeyPair, plaintextPayload, identityProof, }) {
|
|
15
|
+
if (!executor) {
|
|
16
|
+
throw new Error("Executor must be provided");
|
|
17
|
+
}
|
|
18
|
+
// Generate ephemeral keypair for this handshake
|
|
19
|
+
const ephemeralKeyPair = nacl.box.keyPair();
|
|
20
|
+
// Generate ML-KEM-768 keypair for PQ-hybrid key exchange
|
|
21
|
+
const kemKeyPair = kem.generateKeyPair();
|
|
22
|
+
const recipientHash = keccak256(toUtf8Bytes('contact:' + recipientAddress.toLowerCase()));
|
|
23
|
+
const handshakeContent = {
|
|
24
|
+
plaintextPayload,
|
|
25
|
+
identityProof
|
|
26
|
+
};
|
|
27
|
+
const serializedPayload = serializeHandshakeContent(handshakeContent);
|
|
28
|
+
const unifiedPubKeys = encodeUnifiedPubKeys(identityKeyPair.publicKey, identityKeyPair.signingPublicKey);
|
|
29
|
+
// Ephemeral public key now includes KEM public key (32 + 1184 = 1216 bytes)
|
|
30
|
+
const ephemeralWithKem = new Uint8Array(32 + kem.publicKeyBytes);
|
|
31
|
+
ephemeralWithKem.set(ephemeralKeyPair.publicKey, 0);
|
|
32
|
+
ephemeralWithKem.set(kemKeyPair.publicKey, 32);
|
|
33
|
+
const tx = await executor.initiateHandshake(recipientHash, hexlify(unifiedPubKeys), hexlify(ephemeralWithKem), toUtf8Bytes(serializedPayload));
|
|
34
|
+
return {
|
|
35
|
+
tx,
|
|
36
|
+
ephemeralKeyPair, // Caller must persist secretKey for ratchet session init
|
|
37
|
+
kemKeyPair, // Caller must also persist secretKey for KEM decapsulation
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Responds to a handshake with unified keys and mandatory identity proof.
|
|
42
|
+
* Executor-agnostic: works with EOA, UserOp, and Direct EntryPoint
|
|
43
|
+
*
|
|
44
|
+
* If initiator includes KEM public key, encapsulates a shared secret and includes ciphertext in response.
|
|
45
|
+
*
|
|
46
|
+
* @returns Transaction, tag, salt, ephemeral keys, and KEM secret
|
|
47
|
+
*/
|
|
48
|
+
export async function respondToHandshake({ executor, initiatorEphemeralPubKey, responderIdentityKeyPair, note, identityProof, }) {
|
|
49
|
+
if (!executor) {
|
|
50
|
+
throw new Error("Executor must be provided");
|
|
51
|
+
}
|
|
52
|
+
// =========================================================================
|
|
53
|
+
// TWO SEPARATE KEYPAIRS for unlinkability:
|
|
54
|
+
//
|
|
55
|
+
// 1. tagKeyPair (R, r): only for tag computation
|
|
56
|
+
// - R goes on-chain as responderEphemeralR
|
|
57
|
+
// - Used by Alice to verify the tag
|
|
58
|
+
// - not used for ratchet
|
|
59
|
+
//
|
|
60
|
+
// 2. ratchetKeyPair: For post-handshake encryption and first DH ratchet key
|
|
61
|
+
// - Public key goes inside encrypted payload
|
|
62
|
+
// - Becomes dhMySecretKey/dhMyPublicKey in ratchet session
|
|
63
|
+
//
|
|
64
|
+
// Why this matters: With a single keypair, the on-chain R would equal the
|
|
65
|
+
// first message's DH header, allowing observers to link HandshakeResponse
|
|
66
|
+
// to subsequent conversation. With two keypairs, there's no on-chain link.
|
|
67
|
+
// =========================================================================
|
|
68
|
+
const tagKeyPair = nacl.box.keyPair();
|
|
69
|
+
const ratchetKeyPair = nacl.box.keyPair();
|
|
70
|
+
// Check if initiator included KEM public key (extended format: 32 + 1184 = 1216 bytes)
|
|
71
|
+
const hasKem = initiatorEphemeralPubKey.length === 32 + kem.publicKeyBytes;
|
|
72
|
+
const initiatorX25519Pub = hasKem
|
|
73
|
+
? initiatorEphemeralPubKey.slice(0, 32)
|
|
74
|
+
: initiatorEphemeralPubKey;
|
|
75
|
+
// KEM encapsulation needed for hybrid tag
|
|
76
|
+
let kemCiphertext;
|
|
77
|
+
let kemSharedSecret;
|
|
78
|
+
if (hasKem) {
|
|
79
|
+
const initiatorKemPub = initiatorEphemeralPubKey.slice(32, 32 + kem.publicKeyBytes);
|
|
80
|
+
const { ciphertext, sharedSecret } = kem.encapsulate(initiatorKemPub);
|
|
81
|
+
kemCiphertext = ciphertext;
|
|
82
|
+
kemSharedSecret = sharedSecret;
|
|
83
|
+
}
|
|
84
|
+
if (!kemSharedSecret) {
|
|
85
|
+
throw new Error("KEM is required for PQ-secure handshake");
|
|
86
|
+
}
|
|
87
|
+
// Hybrid tag combines ECDH(r, viewPubA) + kemSecret
|
|
88
|
+
const inResponseTo = computeHybridTagFromResponder(tagKeyPair.secretKey, initiatorX25519Pub, kemSharedSecret);
|
|
89
|
+
const salt = getBytes(inResponseTo);
|
|
90
|
+
const responseContent = createHandshakeResponseContent(responderIdentityKeyPair.publicKey, responderIdentityKeyPair.signingPublicKey, ratchetKeyPair.publicKey, // first DH ratchet key inside payload
|
|
91
|
+
note, identityProof, kemCiphertext);
|
|
92
|
+
// Encrypt using ratchetKeyPair (the epk in encrypted payload = ratchetKeyPair.publicKey)
|
|
93
|
+
const payload = encryptStructuredPayload(responseContent, initiatorX25519Pub, ratchetKeyPair.secretKey, ratchetKeyPair.publicKey);
|
|
94
|
+
// tagKeyPair.publicKey goes on-chain, not ratchetKeyPair
|
|
95
|
+
const tx = await executor.respondToHandshake(inResponseTo, hexlify(tagKeyPair.publicKey), toUtf8Bytes(payload));
|
|
96
|
+
return {
|
|
97
|
+
tx,
|
|
98
|
+
salt,
|
|
99
|
+
tag: inResponseTo,
|
|
100
|
+
// Return ratchet keys for session initialization
|
|
101
|
+
responderEphemeralSecret: ratchetKeyPair.secretKey,
|
|
102
|
+
responderEphemeralPublic: ratchetKeyPair.publicKey,
|
|
103
|
+
kemSharedSecret,
|
|
104
|
+
};
|
|
105
|
+
}
|
package/dist/src/identity.d.ts
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
import { Signer } from "ethers";
|
|
2
|
-
import { IdentityProof } from "./types.js";
|
|
3
|
-
interface
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
import { IdentityContext, IdentityKeyPair, IdentityProof } from "./types.js";
|
|
3
|
+
export interface DerivedIdentityKeys {
|
|
4
|
+
/** VerbEth identity key pair (X25519 + Ed25519) */
|
|
5
|
+
keyPair: IdentityKeyPair;
|
|
6
|
+
/** Hex-encoded secp256k1 private key for session signer */
|
|
7
|
+
sessionPrivateKey: string;
|
|
8
|
+
/** Ethereum address of the session signer */
|
|
9
|
+
sessionAddress: string;
|
|
10
|
+
/** Public key hex strings for binding message */
|
|
11
|
+
pkX25519Hex: string;
|
|
12
|
+
pkEd25519Hex: string;
|
|
13
|
+
}
|
|
14
|
+
export interface DerivedIdentityWithProof extends DerivedIdentityKeys {
|
|
15
|
+
identityProof: IdentityProof;
|
|
8
16
|
}
|
|
9
17
|
/**
|
|
10
|
-
*
|
|
11
|
-
* Returns a proof binding the derived keypair to the wallet address.
|
|
18
|
+
* Derive all identity keys and session key from a single seed signature.
|
|
12
19
|
*/
|
|
13
|
-
export declare function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}>;
|
|
21
|
-
export declare function deriveIdentityWithUnifiedKeys(signer: Signer, address: string): Promise<{
|
|
20
|
+
export declare function deriveIdentityKeys(signer: any, address: string): Promise<DerivedIdentityKeys>;
|
|
21
|
+
/**
|
|
22
|
+
* Create the binding proof that ties the derived keys to the Safe address.
|
|
23
|
+
*/
|
|
24
|
+
export declare function createBindingProof(signer: any, address: string, derivedKeys: DerivedIdentityKeys, executorSafeAddress: string, ctx?: IdentityContext): Promise<IdentityProof>;
|
|
25
|
+
export declare function deriveIdentityKeyPairWithProof(signer: any, address: string, executorSafeAddress?: string, ctx?: IdentityContext): Promise<DerivedIdentityWithProof>;
|
|
26
|
+
export declare function deriveIdentityWithUnifiedKeys(signer: Signer, address: string, executorSafeAddress?: string, ctx?: IdentityContext): Promise<{
|
|
22
27
|
identityProof: IdentityProof;
|
|
23
28
|
identityPubKey: Uint8Array;
|
|
24
29
|
signingPubKey: Uint8Array;
|
|
25
30
|
unifiedPubKeys: Uint8Array;
|
|
31
|
+
sessionPrivateKey: string;
|
|
32
|
+
sessionAddress: string;
|
|
26
33
|
}>;
|
|
27
|
-
export {};
|
|
28
34
|
//# sourceMappingURL=identity.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/identity.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,EAAqC,MAAM,QAAQ,CAAC;AAGnE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAwE7E,MAAM,WAAW,mBAAmB;IAClC,mDAAmD;IACnD,OAAO,EAAE,eAAe,CAAC;IACzB,2DAA2D;IAC3D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,6CAA6C;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAyB,SAAQ,mBAAmB;IACnE,aAAa,EAAE,aAAa,CAAC;CAC9B;AAMD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CA8D9B;AAMD;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,mBAAmB,EAChC,mBAAmB,EAAE,MAAM,EAC3B,GAAG,CAAC,EAAE,eAAe,GACpB,OAAO,CAAC,aAAa,CAAC,CAsBxB;AAID,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,GAAG,EACX,OAAO,EAAE,MAAM,EACf,mBAAmB,CAAC,EAAE,MAAM,EAC5B,GAAG,CAAC,EAAE,eAAe,GACpB,OAAO,CAAC,wBAAwB,CAAC,CAcnC;AAED,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,mBAAmB,CAAC,EAAE,MAAM,EAC5B,GAAG,CAAC,EAAE,eAAe,GACpB,OAAO,CAAC;IACT,aAAa,EAAE,aAAa,CAAC;IAC7B,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC;IAC1B,cAAc,EAAE,UAAU,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC,CAqBD"}
|
package/dist/src/identity.js
CHANGED
|
@@ -1,22 +1,87 @@
|
|
|
1
|
+
// packages/sdk/src/utils/identity.ts
|
|
1
2
|
import { sha256 } from "@noble/hashes/sha2";
|
|
2
3
|
import { hkdf } from "@noble/hashes/hkdf";
|
|
3
|
-
import { concat, hexlify } from "ethers";
|
|
4
|
+
import { Wallet, concat, hexlify, getBytes } from "ethers";
|
|
4
5
|
import nacl from "tweetnacl";
|
|
5
6
|
import { encodeUnifiedPubKeys } from "./payload.js";
|
|
7
|
+
const SECP256K1_N = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
|
|
8
|
+
const SECP256K1_HALF_N = SECP256K1_N >> 1n;
|
|
9
|
+
function bytesToBigIntBE(bytes) {
|
|
10
|
+
let hex = "";
|
|
11
|
+
for (const b of bytes)
|
|
12
|
+
hex += b.toString(16).padStart(2, "0");
|
|
13
|
+
return hex.length ? BigInt("0x" + hex) : 0n;
|
|
14
|
+
}
|
|
15
|
+
function bigIntTo32BytesBE(x) {
|
|
16
|
+
let hex = x.toString(16);
|
|
17
|
+
if (hex.length > 64)
|
|
18
|
+
hex = hex.slice(hex.length - 64);
|
|
19
|
+
hex = hex.padStart(64, "0");
|
|
20
|
+
const out = new Uint8Array(32);
|
|
21
|
+
for (let i = 0; i < 32; i++) {
|
|
22
|
+
out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
6
26
|
/**
|
|
7
|
-
*
|
|
8
|
-
* Returns a proof binding the derived keypair to the wallet address.
|
|
27
|
+
* Canonicalize an Ethereum ECDSA signature (65 bytes) to low-s form (only used as KDF input)
|
|
9
28
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
29
|
+
function canonicalizeEcdsaSig65(sig) {
|
|
30
|
+
if (sig.length !== 65)
|
|
31
|
+
return sig;
|
|
32
|
+
const r = sig.slice(0, 32);
|
|
33
|
+
const s = sig.slice(32, 64);
|
|
34
|
+
const v = sig[64];
|
|
35
|
+
const sBig = bytesToBigIntBE(s);
|
|
36
|
+
if (sBig <= SECP256K1_HALF_N)
|
|
37
|
+
return sig;
|
|
38
|
+
const sLow = SECP256K1_N - sBig;
|
|
39
|
+
const out = new Uint8Array(65);
|
|
40
|
+
out.set(r, 0);
|
|
41
|
+
out.set(bigIntTo32BytesBE(sLow), 32);
|
|
42
|
+
out[64] = v;
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
function buildSeedMessage(addrLower) {
|
|
46
|
+
const lines = [
|
|
47
|
+
"VerbEth Identity Seed v1",
|
|
48
|
+
`Address: ${addrLower}`,
|
|
49
|
+
"Context: verbeth",
|
|
50
|
+
];
|
|
51
|
+
return lines.join("\n");
|
|
52
|
+
}
|
|
53
|
+
function buildBindingMessage(addrLower, pkEd25519Hex, pkX25519Hex, executorSafeAddress, ctx) {
|
|
54
|
+
const lines = [
|
|
55
|
+
"VerbEth Key Binding v1",
|
|
56
|
+
`Address: ${addrLower}`,
|
|
57
|
+
`PkEd25519: ${pkEd25519Hex}`,
|
|
58
|
+
`PkX25519: ${pkX25519Hex}`,
|
|
59
|
+
`ExecutorSafeAddress: ${executorSafeAddress ?? ""}`,
|
|
60
|
+
];
|
|
61
|
+
if (typeof ctx?.chainId === "number")
|
|
62
|
+
lines.push(`ChainId: ${ctx.chainId}`);
|
|
63
|
+
if (ctx?.rpId)
|
|
64
|
+
lines.push(`RpId: ${ctx.rpId}`);
|
|
65
|
+
return lines.join("\n");
|
|
66
|
+
}
|
|
67
|
+
// ============================================================================
|
|
68
|
+
// Derive all keys from seed signature
|
|
69
|
+
// ============================================================================
|
|
70
|
+
/**
|
|
71
|
+
* Derive all identity keys and session key from a single seed signature.
|
|
72
|
+
*/
|
|
73
|
+
export async function deriveIdentityKeys(signer, address) {
|
|
13
74
|
const enc = new TextEncoder();
|
|
14
75
|
const addrLower = address.toLowerCase();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
76
|
+
const seedMessage = buildSeedMessage(addrLower);
|
|
77
|
+
let seedSignature = await signer.signMessage(seedMessage);
|
|
78
|
+
const seedSigBytes = canonicalizeEcdsaSig65(getBytes(seedSignature));
|
|
79
|
+
seedSignature = "";
|
|
80
|
+
// IKM = HKDF( canonicalSig || H(seedMessage) || "verbeth/addr:" || address_lower )
|
|
81
|
+
const seedSalt = enc.encode("verbeth/seed-sig-v1");
|
|
18
82
|
const seedInfo = enc.encode("verbeth/ikm");
|
|
19
|
-
const
|
|
83
|
+
const seedMsgHash = sha256(enc.encode(seedMessage));
|
|
84
|
+
const ikmInput = getBytes(concat([seedSigBytes, seedMsgHash, enc.encode("verbeth/addr:" + addrLower)]));
|
|
20
85
|
const ikm = hkdf(sha256, ikmInput, seedSalt, seedInfo, 32);
|
|
21
86
|
// Derive X25519 (encryption)
|
|
22
87
|
const info_x25519 = enc.encode("verbeth-x25519-v1");
|
|
@@ -26,6 +91,22 @@ export async function deriveIdentityKeyPairWithProof(signer, address) {
|
|
|
26
91
|
const info_ed25519 = enc.encode("verbeth-ed25519-v1");
|
|
27
92
|
const ed25519_seed = hkdf(sha256, ikm, new Uint8Array(0), info_ed25519, 32);
|
|
28
93
|
const signKeyPair = nacl.sign.keyPair.fromSeed(ed25519_seed);
|
|
94
|
+
// Derive secp256k1 session key for txs via Safe module
|
|
95
|
+
const info_session = enc.encode("verbeth-session-secp256k1-v1");
|
|
96
|
+
const sessionSeed = hkdf(sha256, ikm, new Uint8Array(0), info_session, 32);
|
|
97
|
+
const sessionPrivateKey = hexlify(sessionSeed);
|
|
98
|
+
const sessionWallet = new Wallet(sessionPrivateKey);
|
|
99
|
+
const sessionAddress = sessionWallet.address;
|
|
100
|
+
try {
|
|
101
|
+
seedSigBytes.fill(0);
|
|
102
|
+
seedMsgHash.fill(0);
|
|
103
|
+
ikmInput.fill(0);
|
|
104
|
+
ikm.fill(0);
|
|
105
|
+
ed25519_seed.fill(0);
|
|
106
|
+
x25519_sk.fill(0);
|
|
107
|
+
sessionSeed.fill(0);
|
|
108
|
+
}
|
|
109
|
+
catch { }
|
|
29
110
|
const pkX25519Hex = hexlify(boxKeyPair.publicKey);
|
|
30
111
|
const pkEd25519Hex = hexlify(signKeyPair.publicKey);
|
|
31
112
|
const keyPair = {
|
|
@@ -34,37 +115,51 @@ export async function deriveIdentityKeyPairWithProof(signer, address) {
|
|
|
34
115
|
signingPublicKey: signKeyPair.publicKey,
|
|
35
116
|
signingSecretKey: signKeyPair.secretKey,
|
|
36
117
|
};
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
118
|
+
return {
|
|
119
|
+
keyPair,
|
|
120
|
+
sessionPrivateKey,
|
|
121
|
+
sessionAddress,
|
|
122
|
+
pkX25519Hex,
|
|
123
|
+
pkEd25519Hex,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// ============================================================================
|
|
127
|
+
// Create binding proof with Safe address
|
|
128
|
+
// ============================================================================
|
|
129
|
+
/**
|
|
130
|
+
* Create the binding proof that ties the derived keys to the Safe address.
|
|
131
|
+
*/
|
|
132
|
+
export async function createBindingProof(signer, address, derivedKeys, executorSafeAddress, ctx) {
|
|
133
|
+
const addrLower = address.toLowerCase();
|
|
134
|
+
const executorSafeAddressLower = executorSafeAddress.toLowerCase();
|
|
135
|
+
const message = buildBindingMessage(addrLower, derivedKeys.pkEd25519Hex, derivedKeys.pkX25519Hex, executorSafeAddressLower, ctx);
|
|
47
136
|
const signature = await signer.signMessage(message);
|
|
48
137
|
const messageRawHex = ("0x" +
|
|
49
138
|
Buffer.from(message, "utf-8").toString("hex"));
|
|
50
139
|
return {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
140
|
+
message,
|
|
141
|
+
signature,
|
|
142
|
+
messageRawHex,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// this is when the Safe address is known upfront
|
|
146
|
+
export async function deriveIdentityKeyPairWithProof(signer, address, executorSafeAddress, ctx) {
|
|
147
|
+
const derivedKeys = await deriveIdentityKeys(signer, address);
|
|
148
|
+
const identityProof = await createBindingProof(signer, address, derivedKeys, executorSafeAddress ?? "", ctx);
|
|
149
|
+
return {
|
|
150
|
+
...derivedKeys,
|
|
151
|
+
identityProof,
|
|
57
152
|
};
|
|
58
153
|
}
|
|
59
|
-
export async function deriveIdentityWithUnifiedKeys(signer, address) {
|
|
60
|
-
const result = await deriveIdentityKeyPairWithProof(signer, address);
|
|
61
|
-
const unifiedPubKeys = encodeUnifiedPubKeys(result.keyPair.publicKey,
|
|
62
|
-
result.keyPair.signingPublicKey // Ed25519
|
|
63
|
-
);
|
|
154
|
+
export async function deriveIdentityWithUnifiedKeys(signer, address, executorSafeAddress, ctx) {
|
|
155
|
+
const result = await deriveIdentityKeyPairWithProof(signer, address, executorSafeAddress, ctx);
|
|
156
|
+
const unifiedPubKeys = encodeUnifiedPubKeys(result.keyPair.publicKey, result.keyPair.signingPublicKey);
|
|
64
157
|
return {
|
|
65
158
|
identityProof: result.identityProof,
|
|
66
159
|
identityPubKey: result.keyPair.publicKey,
|
|
67
160
|
signingPubKey: result.keyPair.signingPublicKey,
|
|
68
161
|
unifiedPubKeys,
|
|
162
|
+
sessionPrivateKey: result.sessionPrivateKey,
|
|
163
|
+
sessionAddress: result.sessionAddress,
|
|
69
164
|
};
|
|
70
165
|
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
export * from './crypto.js';
|
|
2
2
|
export * from './payload.js';
|
|
3
|
-
export * from './
|
|
3
|
+
export * from './handshake.js';
|
|
4
4
|
export * from './verify.js';
|
|
5
5
|
export * from './types.js';
|
|
6
6
|
export * from './utils.js';
|
|
7
7
|
export * from './identity.js';
|
|
8
8
|
export * from './executor.js';
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export { encodeUnifiedPubKeys, decodeUnifiedPubKeys, createHandshakePayload, createHandshakeResponseContent, extractKeysFromHandshakePayload, extractKeysFromHandshakeResponse, parseHandshakeKeys } from './payload.js';
|
|
12
|
-
export { decryptAndExtractHandshakeKeys, decryptMessage, decryptHandshakeResponse } from './crypto.js';
|
|
9
|
+
export { encodeUnifiedPubKeys, decodeUnifiedPubKeys, createHandshakeResponseContent, extractKeysFromHandshakeResponse, parseHandshakeKeys } from './payload.js';
|
|
10
|
+
export { decryptAndExtractHandshakeKeys, decryptHandshakeResponse } from './crypto.js';
|
|
13
11
|
export { verifyIdentityProof, verifyAndExtractHandshakeKeys, verifyAndExtractHandshakeResponseKeys } from './verify.js';
|
|
14
12
|
export { deriveIdentityKeyPairWithProof, deriveIdentityWithUnifiedKeys } from './identity.js';
|
|
15
13
|
export { IExecutor, EOAExecutor, UserOpExecutor, DirectEntryPointExecutor, ExecutorFactory } from './executor.js';
|
|
16
|
-
export {
|
|
17
|
-
export type {
|
|
14
|
+
export { SafeSessionSigner } from "./utils/safeSessionSigner.js";
|
|
15
|
+
export type { SafeSessionSignerOptions } from "./utils/safeSessionSigner.js";
|
|
16
|
+
export { VerbethClient, VerbethClientBuilder, createVerbethClient, matchHsrToContact, } from './client/index.js';
|
|
17
|
+
export type { VerbethClientConfig, VerbethClientCallbacks, TopicRatchetEvent, MessageDecryptedEvent, HandshakeResult, HandshakeResponseResult, SessionStore, PendingStore, PreparedMessage, DecryptedMessage, PendingMessage, PendingStatus, SendResult, ConfirmResult, SerializedSessionInfo, HsrEventData, CreateInitiatorSessionFromHsrParams, CreateVerbethClientOptions, PendingContactEntry, } from './client/index.js';
|
|
18
|
+
export * from './ratchet/index.js';
|
|
19
|
+
export { dh, deriveTopic, hybridInitialSecret } from './ratchet/kdf.js';
|
|
20
|
+
export { kem } from './pq/kem.js';
|
|
18
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAE9B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,8BAA8B,EAC9B,gCAAgC,EAChC,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,8BAA8B,EAC9B,wBAAwB,EACzB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,qCAAqC,EACtC,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,8BAA8B,EAC9B,6BAA6B,EAC9B,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,SAAS,EACT,WAAW,EACX,cAAc,EACd,wBAAwB,EACxB,eAAe,EAChB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,YAAY,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAE7E,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,YAAY,EACV,mBAAmB,EACnB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,uBAAuB,EAEvB,YAAY,EACZ,YAAY,EAEZ,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,aAAa,EAEb,UAAU,EACV,aAAa,EAEb,qBAAqB,EACrB,YAAY,EACZ,mCAAmC,EACnC,0BAA0B,EAC1B,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAE3B,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAExE,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/src/index.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
+
// packages/sdk/src/index.ts
|
|
1
2
|
export * from './crypto.js';
|
|
2
3
|
export * from './payload.js';
|
|
3
|
-
export * from './
|
|
4
|
+
export * from './handshake.js';
|
|
4
5
|
export * from './verify.js';
|
|
5
6
|
export * from './types.js';
|
|
6
7
|
export * from './utils.js';
|
|
7
8
|
export * from './identity.js';
|
|
8
9
|
export * from './executor.js';
|
|
9
|
-
export {
|
|
10
|
-
export {
|
|
11
|
-
export { encodeUnifiedPubKeys, decodeUnifiedPubKeys, createHandshakePayload, createHandshakeResponseContent, extractKeysFromHandshakePayload, extractKeysFromHandshakeResponse, parseHandshakeKeys } from './payload.js';
|
|
12
|
-
export { decryptAndExtractHandshakeKeys, decryptMessage, decryptHandshakeResponse } from './crypto.js';
|
|
10
|
+
export { encodeUnifiedPubKeys, decodeUnifiedPubKeys, createHandshakeResponseContent, extractKeysFromHandshakeResponse, parseHandshakeKeys } from './payload.js';
|
|
11
|
+
export { decryptAndExtractHandshakeKeys, decryptHandshakeResponse } from './crypto.js';
|
|
13
12
|
export { verifyIdentityProof, verifyAndExtractHandshakeKeys, verifyAndExtractHandshakeResponseKeys } from './verify.js';
|
|
14
13
|
export { deriveIdentityKeyPairWithProof, deriveIdentityWithUnifiedKeys } from './identity.js';
|
|
15
14
|
export { EOAExecutor, UserOpExecutor, DirectEntryPointExecutor, ExecutorFactory } from './executor.js';
|
|
16
|
-
|
|
17
|
-
export { VerbethClient } from './client/index.js';
|
|
15
|
+
export { SafeSessionSigner } from "./utils/safeSessionSigner.js";
|
|
16
|
+
export { VerbethClient, VerbethClientBuilder, createVerbethClient, matchHsrToContact, } from './client/index.js';
|
|
17
|
+
export * from './ratchet/index.js';
|
|
18
|
+
export { dh, deriveTopic, hybridInitialSecret } from './ratchet/kdf.js';
|
|
19
|
+
export { kem } from './pq/kem.js';
|
package/dist/src/payload.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IdentityProof
|
|
1
|
+
import { IdentityProof } from './types.js';
|
|
2
2
|
export interface EncryptedPayload {
|
|
3
3
|
v: number;
|
|
4
4
|
epk: string;
|
|
@@ -6,14 +6,6 @@ export interface EncryptedPayload {
|
|
|
6
6
|
ct: string;
|
|
7
7
|
sig?: string;
|
|
8
8
|
}
|
|
9
|
-
export interface MessagePayload {
|
|
10
|
-
content: string;
|
|
11
|
-
timestamp?: number;
|
|
12
|
-
messageType?: 'text' | 'file' | 'media';
|
|
13
|
-
metadata?: Record<string, any>;
|
|
14
|
-
}
|
|
15
|
-
export interface HandshakeResponsePayload extends EncryptedPayload {
|
|
16
|
-
}
|
|
17
9
|
export interface HandshakeContent {
|
|
18
10
|
plaintextPayload: string;
|
|
19
11
|
identityProof: IdentityProof;
|
|
@@ -50,30 +42,11 @@ export interface HandshakePayload {
|
|
|
50
42
|
export interface HandshakeResponseContent {
|
|
51
43
|
unifiedPubKeys: Uint8Array;
|
|
52
44
|
ephemeralPubKey: Uint8Array;
|
|
45
|
+
kemCiphertext?: Uint8Array;
|
|
53
46
|
note?: string;
|
|
54
47
|
identityProof: IdentityProof;
|
|
55
|
-
topicInfo?: TopicInfoWire;
|
|
56
48
|
}
|
|
57
|
-
export declare function
|
|
58
|
-
export declare function decodeHandshakePayload(encoded: Uint8Array): HandshakePayload;
|
|
59
|
-
export declare function encodeHandshakeResponseContent(content: HandshakeResponseContent): Uint8Array;
|
|
60
|
-
export declare function decodeHandshakeResponseContent(encoded: Uint8Array): HandshakeResponseContent;
|
|
61
|
-
/**
|
|
62
|
-
* Creates HandshakePayload from separate identity keys
|
|
63
|
-
*/
|
|
64
|
-
export declare function createHandshakePayload(identityPubKey: Uint8Array, signingPubKey: Uint8Array, ephemeralPubKey: Uint8Array, plaintextPayload: string): HandshakePayload;
|
|
65
|
-
/**
|
|
66
|
-
* Creates HandshakeResponseContent from separate identity keys
|
|
67
|
-
*/
|
|
68
|
-
export declare function createHandshakeResponseContent(identityPubKey: Uint8Array, signingPubKey: Uint8Array, ephemeralPubKey: Uint8Array, note?: string, identityProof?: IdentityProof, topicInfo?: TopicInfoWire): HandshakeResponseContent;
|
|
69
|
-
/**
|
|
70
|
-
* Extracts individual keys from HandshakePayload
|
|
71
|
-
*/
|
|
72
|
-
export declare function extractKeysFromHandshakePayload(payload: HandshakePayload): {
|
|
73
|
-
identityPubKey: Uint8Array;
|
|
74
|
-
signingPubKey: Uint8Array;
|
|
75
|
-
ephemeralPubKey: Uint8Array;
|
|
76
|
-
} | null;
|
|
49
|
+
export declare function createHandshakeResponseContent(identityPubKey: Uint8Array, signingPubKey: Uint8Array, ephemeralPubKey: Uint8Array, note?: string, identityProof?: IdentityProof, kemCiphertext?: Uint8Array): HandshakeResponseContent;
|
|
77
50
|
/**
|
|
78
51
|
* Extracts individual keys from HandshakeResponseContent
|
|
79
52
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../src/payload.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"payload.d.ts","sourceRoot":"","sources":["../../src/payload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,wBAAgB,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,GAAG,gBAAgB,CAUhF;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAE3E;AAED,wBAAgB,aAAa,CAAC,eAAe,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,UAAU,GAAG,MAAM,CAS9H;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG;IAC3C,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;IAClB,UAAU,EAAE,UAAU,CAAC;IACvB,GAAG,CAAC,EAAE,UAAU,CAAA;CACjB,CAyBA;AAID,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,UAAU,CAQjE;AAGD,wBAAgB,uBAAuB,CAAC,CAAC,EACvC,OAAO,EAAE,UAAU,EACnB,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GACzB,CAAC,CAGH;AAID;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,UAAU,EAAG,oBAAoB;AACjD,aAAa,EAAE,UAAU,GACxB,UAAU,CAOZ;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,UAAU,GAAG;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC;CAC3B,GAAG,IAAI,CAkBP;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,UAAU,CAAC;IAC3B,eAAe,EAAE,UAAU,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,wBAAwB;IACvC,cAAc,EAAE,UAAU,CAAC;IAC3B,eAAe,EAAE,UAAU,CAAC;IAC5B,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;CAC9B;AAED,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,UAAU,EAC1B,aAAa,EAAE,UAAU,EACzB,eAAe,EAAE,UAAU,EAC3B,IAAI,CAAC,EAAE,MAAM,EACb,aAAa,CAAC,EAAE,aAAa,EAC7B,aAAa,CAAC,EAAE,UAAU,GACzB,wBAAwB,CAY1B;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,wBAAwB,GAAG;IACnF,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC;IAC1B,eAAe,EAAE,UAAU,CAAC;CAC7B,GAAG,IAAI,CASP;AAGD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG;IAC9D,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC;CAC3B,GAAG,IAAI,CAkBP"}
|
package/dist/src/payload.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// packages/sdk/src/payload.ts
|
|
1
2
|
export function parseHandshakePayload(plaintextPayload) {
|
|
2
3
|
try {
|
|
3
4
|
const parsed = JSON.parse(plaintextPayload);
|
|
@@ -80,7 +81,6 @@ signingPubKey // Ed25519 - 32 bytes
|
|
|
80
81
|
*/
|
|
81
82
|
export function decodeUnifiedPubKeys(pubKeys) {
|
|
82
83
|
if (pubKeys.length === 64) {
|
|
83
|
-
// Legacy
|
|
84
84
|
return {
|
|
85
85
|
version: 0,
|
|
86
86
|
identityPubKey: pubKeys.slice(0, 32),
|
|
@@ -88,7 +88,6 @@ export function decodeUnifiedPubKeys(pubKeys) {
|
|
|
88
88
|
};
|
|
89
89
|
}
|
|
90
90
|
if (pubKeys.length === 65 && pubKeys[0] === 0x01) {
|
|
91
|
-
// V1: with versioning
|
|
92
91
|
return {
|
|
93
92
|
version: 1,
|
|
94
93
|
identityPubKey: pubKeys.slice(1, 33),
|
|
@@ -97,89 +96,16 @@ export function decodeUnifiedPubKeys(pubKeys) {
|
|
|
97
96
|
}
|
|
98
97
|
return null;
|
|
99
98
|
}
|
|
100
|
-
export function
|
|
101
|
-
return new TextEncoder().encode(JSON.stringify({
|
|
102
|
-
unifiedPubKeys: Buffer.from(payload.unifiedPubKeys).toString('base64'),
|
|
103
|
-
ephemeralPubKey: Buffer.from(payload.ephemeralPubKey).toString('base64'),
|
|
104
|
-
plaintextPayload: payload.plaintextPayload
|
|
105
|
-
}));
|
|
106
|
-
}
|
|
107
|
-
export function decodeHandshakePayload(encoded) {
|
|
108
|
-
const json = new TextDecoder().decode(encoded);
|
|
109
|
-
const parsed = JSON.parse(json);
|
|
110
|
-
return {
|
|
111
|
-
unifiedPubKeys: Uint8Array.from(Buffer.from(parsed.unifiedPubKeys, 'base64')),
|
|
112
|
-
ephemeralPubKey: Uint8Array.from(Buffer.from(parsed.ephemeralPubKey, 'base64')),
|
|
113
|
-
plaintextPayload: parsed.plaintextPayload
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
export function encodeHandshakeResponseContent(content) {
|
|
117
|
-
return new TextEncoder().encode(JSON.stringify({
|
|
118
|
-
unifiedPubKeys: Buffer.from(content.unifiedPubKeys).toString('base64'),
|
|
119
|
-
ephemeralPubKey: Buffer.from(content.ephemeralPubKey).toString('base64'),
|
|
120
|
-
note: content.note,
|
|
121
|
-
identityProof: content.identityProof,
|
|
122
|
-
topicInfo: content.topicInfo ? {
|
|
123
|
-
out: content.topicInfo.out,
|
|
124
|
-
in: content.topicInfo.in,
|
|
125
|
-
chk: content.topicInfo.chk
|
|
126
|
-
} : undefined
|
|
127
|
-
}));
|
|
128
|
-
}
|
|
129
|
-
export function decodeHandshakeResponseContent(encoded) {
|
|
130
|
-
const json = new TextDecoder().decode(encoded);
|
|
131
|
-
const obj = JSON.parse(json);
|
|
132
|
-
if (!obj.identityProof) {
|
|
133
|
-
throw new Error("Invalid handshake response: missing identityProof");
|
|
134
|
-
}
|
|
135
|
-
return {
|
|
136
|
-
unifiedPubKeys: Uint8Array.from(Buffer.from(obj.unifiedPubKeys, 'base64')),
|
|
137
|
-
ephemeralPubKey: Uint8Array.from(Buffer.from(obj.ephemeralPubKey, 'base64')),
|
|
138
|
-
note: obj.note,
|
|
139
|
-
identityProof: obj.identityProof,
|
|
140
|
-
topicInfo: obj.topicInfo ? {
|
|
141
|
-
out: obj.topicInfo.out,
|
|
142
|
-
in: obj.topicInfo.in,
|
|
143
|
-
chk: obj.topicInfo.chk
|
|
144
|
-
} : undefined
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Creates HandshakePayload from separate identity keys
|
|
149
|
-
*/
|
|
150
|
-
export function createHandshakePayload(identityPubKey, signingPubKey, ephemeralPubKey, plaintextPayload) {
|
|
151
|
-
return {
|
|
152
|
-
unifiedPubKeys: encodeUnifiedPubKeys(identityPubKey, signingPubKey),
|
|
153
|
-
ephemeralPubKey,
|
|
154
|
-
plaintextPayload
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Creates HandshakeResponseContent from separate identity keys
|
|
159
|
-
*/
|
|
160
|
-
export function createHandshakeResponseContent(identityPubKey, signingPubKey, ephemeralPubKey, note, identityProof, topicInfo) {
|
|
99
|
+
export function createHandshakeResponseContent(identityPubKey, signingPubKey, ephemeralPubKey, note, identityProof, kemCiphertext) {
|
|
161
100
|
if (!identityProof) {
|
|
162
101
|
throw new Error("Identity proof is now mandatory for handshake responses");
|
|
163
102
|
}
|
|
164
103
|
return {
|
|
165
104
|
unifiedPubKeys: encodeUnifiedPubKeys(identityPubKey, signingPubKey),
|
|
166
105
|
ephemeralPubKey,
|
|
106
|
+
...(kemCiphertext && { kemCiphertext }),
|
|
167
107
|
note,
|
|
168
108
|
identityProof,
|
|
169
|
-
topicInfo
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Extracts individual keys from HandshakePayload
|
|
174
|
-
*/
|
|
175
|
-
export function extractKeysFromHandshakePayload(payload) {
|
|
176
|
-
const decoded = decodeUnifiedPubKeys(payload.unifiedPubKeys);
|
|
177
|
-
if (!decoded)
|
|
178
|
-
return null;
|
|
179
|
-
return {
|
|
180
|
-
identityPubKey: decoded.identityPubKey,
|
|
181
|
-
signingPubKey: decoded.signingPubKey,
|
|
182
|
-
ephemeralPubKey: payload.ephemeralPubKey
|
|
183
109
|
};
|
|
184
110
|
}
|
|
185
111
|
/**
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export declare const kem: {
|
|
2
|
+
publicKeyBytes: number;
|
|
3
|
+
ciphertextBytes: number;
|
|
4
|
+
sharedSecretBytes: number;
|
|
5
|
+
/**
|
|
6
|
+
* Generate a new ML-KEM-768 keypair.
|
|
7
|
+
*
|
|
8
|
+
* @returns Object containing publicKey and secretKey
|
|
9
|
+
*/
|
|
10
|
+
generateKeyPair(): {
|
|
11
|
+
publicKey: Uint8Array;
|
|
12
|
+
secretKey: Uint8Array;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Encapsulate a shared secret using the recipient's public key.
|
|
16
|
+
*
|
|
17
|
+
* @param publicKey - Recipient's ML-KEM-768 public key
|
|
18
|
+
* @returns Object containing ciphertext and sharedSecret
|
|
19
|
+
*/
|
|
20
|
+
encapsulate(publicKey: Uint8Array): {
|
|
21
|
+
ciphertext: Uint8Array;
|
|
22
|
+
sharedSecret: Uint8Array;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Decapsulate a ciphertext using the secret key to recover the shared secret.
|
|
26
|
+
*
|
|
27
|
+
* @param ciphertext - KEM ciphertext
|
|
28
|
+
* @param secretKey - Recipient's ML-KEM-768 secret key
|
|
29
|
+
* @returns Shared secret
|
|
30
|
+
*/
|
|
31
|
+
decapsulate(ciphertext: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=kem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kem.d.ts","sourceRoot":"","sources":["../../../src/pq/kem.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,GAAG;;;;IAKd;;;;OAIG;uBACgB;QAAE,SAAS,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,UAAU,CAAA;KAAE;IAInE;;;;;OAKG;2BACoB,UAAU,GAAG;QAAE,UAAU,EAAE,UAAU,CAAC;QAAC,YAAY,EAAE,UAAU,CAAA;KAAE;IAKxF;;;;;;OAMG;4BACqB,UAAU,aAAa,UAAU,GAAG,UAAU;CAGvE,CAAC"}
|