@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,137 @@
|
|
|
1
|
+
// packages/sdk/src/crypto.ts
|
|
2
|
+
import nacl from 'tweetnacl';
|
|
3
|
+
import { keccak256, toUtf8Bytes, dataSlice } from 'ethers';
|
|
4
|
+
import { sha256 } from '@noble/hashes/sha2';
|
|
5
|
+
import { hkdf } from '@noble/hashes/hkdf';
|
|
6
|
+
import { encodePayload, decodePayload, encodeStructuredContent, decodeStructuredContent, extractKeysFromHandshakeResponse } from './payload.js';
|
|
7
|
+
/**
|
|
8
|
+
* Encrypts a structured payload (JSON-serializable objects)
|
|
9
|
+
*/
|
|
10
|
+
export function encryptStructuredPayload(payload, recipientPublicKey, ephemeralSecretKey, ephemeralPublicKey, staticSigningSecretKey, staticSigningPublicKey) {
|
|
11
|
+
// encode payload as binary JSON
|
|
12
|
+
const plaintext = encodeStructuredContent(payload);
|
|
13
|
+
const nonce = nacl.randomBytes(nacl.box.nonceLength);
|
|
14
|
+
const box = nacl.box(plaintext, nonce, recipientPublicKey, ephemeralSecretKey);
|
|
15
|
+
let sig;
|
|
16
|
+
if (staticSigningSecretKey && staticSigningPublicKey) {
|
|
17
|
+
const dataToSign = Buffer.concat([ephemeralPublicKey, nonce, box]);
|
|
18
|
+
sig = nacl.sign.detached(dataToSign, staticSigningSecretKey);
|
|
19
|
+
}
|
|
20
|
+
return encodePayload(ephemeralPublicKey, nonce, box, sig);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Decrypts a structured payload with converter function
|
|
24
|
+
*/
|
|
25
|
+
export function decryptStructuredPayload(payloadJson, recipientSecretKey, converter, staticSigningPublicKey) {
|
|
26
|
+
const { epk, nonce, ciphertext, sig } = decodePayload(payloadJson);
|
|
27
|
+
if (sig && staticSigningPublicKey) {
|
|
28
|
+
const dataToVerify = Buffer.concat([epk, nonce, ciphertext]);
|
|
29
|
+
const valid = nacl.sign.detached.verify(dataToVerify, sig, staticSigningPublicKey);
|
|
30
|
+
if (!valid)
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const box = nacl.box.open(ciphertext, nonce, epk, recipientSecretKey);
|
|
34
|
+
if (!box)
|
|
35
|
+
return null;
|
|
36
|
+
return decodeStructuredContent(box, converter);
|
|
37
|
+
}
|
|
38
|
+
// wrappers for encrypting and decrypting messages
|
|
39
|
+
export function encryptMessage(message, recipientPublicKey, ephemeralSecretKey, ephemeralPublicKey, staticSigningSecretKey, staticSigningPublicKey) {
|
|
40
|
+
const payload = { content: message };
|
|
41
|
+
return encryptStructuredPayload(payload, recipientPublicKey, ephemeralSecretKey, ephemeralPublicKey, staticSigningSecretKey, staticSigningPublicKey);
|
|
42
|
+
}
|
|
43
|
+
export function decryptMessage(payloadJson, recipientSecretKey, staticSigningPublicKey) {
|
|
44
|
+
const result = decryptStructuredPayload(payloadJson, recipientSecretKey, (obj) => obj, staticSigningPublicKey);
|
|
45
|
+
return result ? result.content : null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Decrypts handshake response and extracts individual keys from unified format
|
|
49
|
+
*/
|
|
50
|
+
export function decryptHandshakeResponse(payloadJson, initiatorEphemeralSecretKey) {
|
|
51
|
+
return decryptStructuredPayload(payloadJson, initiatorEphemeralSecretKey, (obj) => {
|
|
52
|
+
if (!obj.identityProof) {
|
|
53
|
+
throw new Error("Invalid handshake response: missing identityProof");
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
unifiedPubKeys: Uint8Array.from(Buffer.from(obj.unifiedPubKeys, 'base64')),
|
|
57
|
+
ephemeralPubKey: Uint8Array.from(Buffer.from(obj.ephemeralPubKey, 'base64')),
|
|
58
|
+
note: obj.note,
|
|
59
|
+
identityProof: obj.identityProof
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* helper to decrypt handshake response and extract individual keys
|
|
65
|
+
*/
|
|
66
|
+
export function decryptAndExtractHandshakeKeys(payloadJson, initiatorEphemeralSecretKey) {
|
|
67
|
+
const decrypted = decryptHandshakeResponse(payloadJson, initiatorEphemeralSecretKey);
|
|
68
|
+
if (!decrypted)
|
|
69
|
+
return null;
|
|
70
|
+
const extracted = extractKeysFromHandshakeResponse(decrypted);
|
|
71
|
+
if (!extracted)
|
|
72
|
+
return null;
|
|
73
|
+
return {
|
|
74
|
+
identityPubKey: extracted.identityPubKey,
|
|
75
|
+
signingPubKey: extracted.signingPubKey,
|
|
76
|
+
ephemeralPubKey: extracted.ephemeralPubKey,
|
|
77
|
+
note: decrypted.note,
|
|
78
|
+
identityProof: decrypted.identityProof
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* HKDF(sha256) on shared secret, info="verbeth:hsr", then Keccak-256 -> bytes32 (0x...)
|
|
83
|
+
*/
|
|
84
|
+
function finalizeHsrTag(shared) {
|
|
85
|
+
const okm = hkdf(sha256, shared, new Uint8Array(0), toUtf8Bytes("verbeth:hsr"), 32);
|
|
86
|
+
return keccak256(okm);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Responder: tag = H( KDF( ECDH(r, viewPubA), "verbeth:hsr"))
|
|
90
|
+
*/
|
|
91
|
+
export function computeTagFromResponder(rSecretKey, viewPubA) {
|
|
92
|
+
const shared = nacl.scalarMult(rSecretKey, viewPubA);
|
|
93
|
+
return finalizeHsrTag(shared);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Initiator: tag = H( KDF( ECDH(viewPrivA, R), "verbeth:hsr"))
|
|
97
|
+
*/
|
|
98
|
+
export function computeTagFromInitiator(viewPrivA, R) {
|
|
99
|
+
const shared = nacl.scalarMult(viewPrivA, R);
|
|
100
|
+
return finalizeHsrTag(shared);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Derives a bytes32 topic from the shared secret via HKDF(SHA256) + Keccak-256.
|
|
104
|
+
* - info: domain separation (e.g., "verbeth:topic-out:v1")
|
|
105
|
+
* - salt: recommended to use a tag as salt (stable and shareable)
|
|
106
|
+
*/
|
|
107
|
+
function deriveTopic(shared, info, salt) {
|
|
108
|
+
const okm = hkdf(sha256, shared, salt ?? new Uint8Array(0), new TextEncoder().encode(info), 32);
|
|
109
|
+
return keccak256(okm);
|
|
110
|
+
}
|
|
111
|
+
export function deriveLongTermShared(myIdentitySecretKey, theirIdentityPublicKey) {
|
|
112
|
+
return nacl.scalarMult(myIdentitySecretKey, theirIdentityPublicKey);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Directional duplex topics (Initiator-Responder, Responder-Initiator).
|
|
116
|
+
* Recommended salt: tag (bytes)
|
|
117
|
+
*/
|
|
118
|
+
export function deriveDuplexTopics(myIdentitySecretKey, theirIdentityPublicKey, salt) {
|
|
119
|
+
const shared = deriveLongTermShared(myIdentitySecretKey, theirIdentityPublicKey);
|
|
120
|
+
const topicOut = deriveTopic(shared, "verbeth:topic-out:v1", salt);
|
|
121
|
+
const topicIn = deriveTopic(shared, "verbeth:topic-in:v1", salt);
|
|
122
|
+
const chkFull = keccak256(Buffer.concat([
|
|
123
|
+
toUtf8Bytes("verbeth:topic-chk:v1"),
|
|
124
|
+
Buffer.from(topicOut.slice(2), 'hex'),
|
|
125
|
+
Buffer.from(topicIn.slice(2), 'hex'),
|
|
126
|
+
]));
|
|
127
|
+
const checksum = dataSlice(chkFull, 8);
|
|
128
|
+
return { topicOut, topicIn, checksum };
|
|
129
|
+
}
|
|
130
|
+
export function verifyDuplexTopicsChecksum(topicOut, topicIn, checksum) {
|
|
131
|
+
const chkFull = keccak256(Buffer.concat([
|
|
132
|
+
toUtf8Bytes("verbeth:topic-chk:v1"),
|
|
133
|
+
Buffer.from(topicOut.slice(2), 'hex'),
|
|
134
|
+
Buffer.from(topicIn.slice(2), 'hex'),
|
|
135
|
+
]));
|
|
136
|
+
return dataSlice(chkFull, 8) === checksum;
|
|
137
|
+
}
|
package/dist/src/crypto.d.ts
CHANGED
|
@@ -1,46 +1,24 @@
|
|
|
1
1
|
import { HandshakeResponseContent } from './payload.js';
|
|
2
2
|
import { IdentityProof } from './types.js';
|
|
3
3
|
/**
|
|
4
|
-
* Encrypts a structured payload (JSON-serializable objects)
|
|
4
|
+
* Encrypts a structured payload (JSON-serializable objects) using NaCl box.
|
|
5
|
+
* Used for handshake responses where ratchet is not yet established.
|
|
5
6
|
*/
|
|
6
7
|
export declare function encryptStructuredPayload<T>(payload: T, recipientPublicKey: Uint8Array, ephemeralSecretKey: Uint8Array, ephemeralPublicKey: Uint8Array, staticSigningSecretKey?: Uint8Array, staticSigningPublicKey?: Uint8Array): string;
|
|
7
8
|
/**
|
|
8
|
-
* Decrypts a structured payload with converter function
|
|
9
|
+
* Decrypts a structured payload with converter function.
|
|
10
|
+
* Used for handshake responses where ratchet is not yet established.
|
|
9
11
|
*/
|
|
10
12
|
export declare function decryptStructuredPayload<T>(payloadJson: string, recipientSecretKey: Uint8Array, converter: (obj: any) => T, staticSigningPublicKey?: Uint8Array): T | null;
|
|
11
|
-
export declare function encryptMessage(message: string, recipientPublicKey: Uint8Array, ephemeralSecretKey: Uint8Array, ephemeralPublicKey: Uint8Array, staticSigningSecretKey?: Uint8Array, staticSigningPublicKey?: Uint8Array): string;
|
|
12
|
-
export declare function decryptMessage(payloadJson: string, recipientSecretKey: Uint8Array, staticSigningPublicKey?: Uint8Array): string | null;
|
|
13
|
-
/**
|
|
14
|
-
* Decrypts handshake response and extracts individual keys from unified format
|
|
15
|
-
*/
|
|
16
13
|
export declare function decryptHandshakeResponse(payloadJson: string, initiatorEphemeralSecretKey: Uint8Array): HandshakeResponseContent | null;
|
|
17
|
-
/**
|
|
18
|
-
* helper to decrypt handshake response and extract individual keys
|
|
19
|
-
*/
|
|
20
14
|
export declare function decryptAndExtractHandshakeKeys(payloadJson: string, initiatorEphemeralSecretKey: Uint8Array): {
|
|
21
15
|
identityPubKey: Uint8Array;
|
|
22
16
|
signingPubKey: Uint8Array;
|
|
23
17
|
ephemeralPubKey: Uint8Array;
|
|
18
|
+
kemCiphertext?: Uint8Array;
|
|
24
19
|
note?: string;
|
|
25
20
|
identityProof: IdentityProof;
|
|
26
21
|
} | null;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
*/
|
|
30
|
-
export declare function computeTagFromResponder(rSecretKey: Uint8Array, viewPubA: Uint8Array): `0x${string}`;
|
|
31
|
-
/**
|
|
32
|
-
* Initiator: tag = H( KDF( ECDH(viewPrivA, R), "verbeth:hsr"))
|
|
33
|
-
*/
|
|
34
|
-
export declare function computeTagFromInitiator(viewPrivA: Uint8Array, R: Uint8Array): `0x${string}`;
|
|
35
|
-
export declare function deriveLongTermShared(myIdentitySecretKey: Uint8Array, theirIdentityPublicKey: Uint8Array): Uint8Array;
|
|
36
|
-
/**
|
|
37
|
-
* Directional duplex topics (Initiator-Responder, Responder-Initiator).
|
|
38
|
-
* Recommended salt: tag (bytes)
|
|
39
|
-
*/
|
|
40
|
-
export declare function deriveDuplexTopics(myIdentitySecretKey: Uint8Array, theirIdentityPublicKey: Uint8Array, salt?: Uint8Array): {
|
|
41
|
-
topicOut: `0x${string}`;
|
|
42
|
-
topicIn: `0x${string}`;
|
|
43
|
-
checksum: `0x${string}`;
|
|
44
|
-
};
|
|
45
|
-
export declare function verifyDuplexTopicsChecksum(topicOut: `0x${string}`, topicIn: `0x${string}`, checksum: `0x${string}`): boolean;
|
|
22
|
+
export declare function computeHybridTagFromResponder(rSecretKey: Uint8Array, viewPubA: Uint8Array, kemSecret: Uint8Array): `0x${string}`;
|
|
23
|
+
export declare function computeHybridTagFromInitiator(viewPrivA: Uint8Array, R: Uint8Array, kemSecret: Uint8Array): `0x${string}`;
|
|
46
24
|
//# sourceMappingURL=crypto.d.ts.map
|
package/dist/src/crypto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"AAkBA,OAAO,EAKL,wBAAwB,EAEzB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAM3C;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,OAAO,EAAE,CAAC,EACV,kBAAkB,EAAE,UAAU,EAC9B,kBAAkB,EAAE,UAAU,EAC9B,kBAAkB,EAAE,UAAU,EAC9B,sBAAsB,CAAC,EAAE,UAAU,EACnC,sBAAsB,CAAC,EAAE,UAAU,GAClC,MAAM,CAcR;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,UAAU,EAC9B,SAAS,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,EAC1B,sBAAsB,CAAC,EAAE,UAAU,GAClC,CAAC,GAAG,IAAI,CAaV;AAOD,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,2BAA2B,EAAE,UAAU,GACtC,wBAAwB,GAAG,IAAI,CAiBjC;AAGD,wBAAgB,8BAA8B,CAC5C,WAAW,EAAE,MAAM,EACnB,2BAA2B,EAAE,UAAU,GACtC;IACD,cAAc,EAAE,UAAU,CAAC;IAC3B,aAAa,EAAE,UAAU,CAAC;IAC1B,eAAe,EAAE,UAAU,CAAC;IAC5B,aAAa,CAAC,EAAE,UAAU,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;CAC9B,GAAG,IAAI,CAeP;AAWD,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,UAAU,EACpB,SAAS,EAAE,UAAU,GACpB,KAAK,MAAM,EAAE,CAGf;AAED,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,UAAU,EACrB,CAAC,EAAE,UAAU,EACb,SAAS,EAAE,UAAU,GACpB,KAAK,MAAM,EAAE,CAGf"}
|
package/dist/src/crypto.js
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
// packages/sdk/src/crypto.ts
|
|
2
|
+
/**
|
|
3
|
+
* This module handles:
|
|
4
|
+
* - Handshake encryption/decryption
|
|
5
|
+
* - Tag computation for handshake responses
|
|
6
|
+
*
|
|
7
|
+
* Post-handshake message encryption uses the ratchet module.
|
|
8
|
+
* See `ratchet/encrypt.ts` and `ratchet/decrypt.ts` for Double Ratchet.
|
|
9
|
+
*
|
|
10
|
+
* Topic derivation is handled entirely by the ratchet module.
|
|
11
|
+
* See `ratchet/kdf.ts` for `deriveTopicFromDH`.
|
|
12
|
+
*/
|
|
2
13
|
import nacl from 'tweetnacl';
|
|
3
|
-
import { keccak256, toUtf8Bytes
|
|
14
|
+
import { keccak256, toUtf8Bytes } from 'ethers';
|
|
4
15
|
import { sha256 } from '@noble/hashes/sha2';
|
|
5
16
|
import { hkdf } from '@noble/hashes/hkdf';
|
|
6
17
|
import { encodePayload, decodePayload, encodeStructuredContent, decodeStructuredContent, extractKeysFromHandshakeResponse } from './payload.js';
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Handshake Encryption
|
|
20
|
+
// =============================================================================
|
|
7
21
|
/**
|
|
8
|
-
* Encrypts a structured payload (JSON-serializable objects)
|
|
22
|
+
* Encrypts a structured payload (JSON-serializable objects) using NaCl box.
|
|
23
|
+
* Used for handshake responses where ratchet is not yet established.
|
|
9
24
|
*/
|
|
10
25
|
export function encryptStructuredPayload(payload, recipientPublicKey, ephemeralSecretKey, ephemeralPublicKey, staticSigningSecretKey, staticSigningPublicKey) {
|
|
11
26
|
// encode payload as binary JSON
|
|
@@ -20,7 +35,8 @@ export function encryptStructuredPayload(payload, recipientPublicKey, ephemeralS
|
|
|
20
35
|
return encodePayload(ephemeralPublicKey, nonce, box, sig);
|
|
21
36
|
}
|
|
22
37
|
/**
|
|
23
|
-
* Decrypts a structured payload with converter function
|
|
38
|
+
* Decrypts a structured payload with converter function.
|
|
39
|
+
* Used for handshake responses where ratchet is not yet established.
|
|
24
40
|
*/
|
|
25
41
|
export function decryptStructuredPayload(payloadJson, recipientSecretKey, converter, staticSigningPublicKey) {
|
|
26
42
|
const { epk, nonce, ciphertext, sig } = decodePayload(payloadJson);
|
|
@@ -35,18 +51,9 @@ export function decryptStructuredPayload(payloadJson, recipientSecretKey, conver
|
|
|
35
51
|
return null;
|
|
36
52
|
return decodeStructuredContent(box, converter);
|
|
37
53
|
}
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return encryptStructuredPayload(payload, recipientPublicKey, ephemeralSecretKey, ephemeralPublicKey, staticSigningSecretKey, staticSigningPublicKey);
|
|
42
|
-
}
|
|
43
|
-
export function decryptMessage(payloadJson, recipientSecretKey, staticSigningPublicKey) {
|
|
44
|
-
const result = decryptStructuredPayload(payloadJson, recipientSecretKey, (obj) => obj, staticSigningPublicKey);
|
|
45
|
-
return result ? result.content : null;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Decrypts handshake response and extracts individual keys from unified format
|
|
49
|
-
*/
|
|
54
|
+
// =============================================================================
|
|
55
|
+
// Handshake Response Decryption
|
|
56
|
+
// =============================================================================
|
|
50
57
|
export function decryptHandshakeResponse(payloadJson, initiatorEphemeralSecretKey) {
|
|
51
58
|
return decryptStructuredPayload(payloadJson, initiatorEphemeralSecretKey, (obj) => {
|
|
52
59
|
if (!obj.identityProof) {
|
|
@@ -55,14 +62,12 @@ export function decryptHandshakeResponse(payloadJson, initiatorEphemeralSecretKe
|
|
|
55
62
|
return {
|
|
56
63
|
unifiedPubKeys: Uint8Array.from(Buffer.from(obj.unifiedPubKeys, 'base64')),
|
|
57
64
|
ephemeralPubKey: Uint8Array.from(Buffer.from(obj.ephemeralPubKey, 'base64')),
|
|
65
|
+
...(obj.kemCiphertext && { kemCiphertext: Uint8Array.from(Buffer.from(obj.kemCiphertext, 'base64')) }),
|
|
58
66
|
note: obj.note,
|
|
59
|
-
identityProof: obj.identityProof
|
|
67
|
+
identityProof: obj.identityProof,
|
|
60
68
|
};
|
|
61
69
|
});
|
|
62
70
|
}
|
|
63
|
-
/**
|
|
64
|
-
* helper to decrypt handshake response and extract individual keys
|
|
65
|
-
*/
|
|
66
71
|
export function decryptAndExtractHandshakeKeys(payloadJson, initiatorEphemeralSecretKey) {
|
|
67
72
|
const decrypted = decryptHandshakeResponse(payloadJson, initiatorEphemeralSecretKey);
|
|
68
73
|
if (!decrypted)
|
|
@@ -74,64 +79,23 @@ export function decryptAndExtractHandshakeKeys(payloadJson, initiatorEphemeralSe
|
|
|
74
79
|
identityPubKey: extracted.identityPubKey,
|
|
75
80
|
signingPubKey: extracted.signingPubKey,
|
|
76
81
|
ephemeralPubKey: extracted.ephemeralPubKey,
|
|
82
|
+
kemCiphertext: decrypted.kemCiphertext,
|
|
77
83
|
note: decrypted.note,
|
|
78
84
|
identityProof: decrypted.identityProof
|
|
79
85
|
};
|
|
80
86
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
function
|
|
85
|
-
const okm = hkdf(sha256,
|
|
87
|
+
// =============================================================================
|
|
88
|
+
// Hybrid Tag Computation (PQ-Secure)
|
|
89
|
+
// =============================================================================
|
|
90
|
+
function finalizeHybridHsrTag(kemSecret, ecdhShared) {
|
|
91
|
+
const okm = hkdf(sha256, kemSecret, ecdhShared, toUtf8Bytes("verbeth:hsr-hybrid:v1"), 32);
|
|
86
92
|
return keccak256(okm);
|
|
87
93
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
export function computeTagFromResponder(rSecretKey, viewPubA) {
|
|
92
|
-
const shared = nacl.scalarMult(rSecretKey, viewPubA);
|
|
93
|
-
return finalizeHsrTag(shared);
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Initiator: tag = H( KDF( ECDH(viewPrivA, R), "verbeth:hsr"))
|
|
97
|
-
*/
|
|
98
|
-
export function computeTagFromInitiator(viewPrivA, R) {
|
|
99
|
-
const shared = nacl.scalarMult(viewPrivA, R);
|
|
100
|
-
return finalizeHsrTag(shared);
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Derives a bytes32 topic from the shared secret via HKDF(SHA256) + Keccak-256.
|
|
104
|
-
* - info: domain separation (e.g., "verbeth:topic-out:v1")
|
|
105
|
-
* - salt: recommended to use a tag as salt (stable and shareable)
|
|
106
|
-
*/
|
|
107
|
-
function deriveTopic(shared, info, salt) {
|
|
108
|
-
const okm = hkdf(sha256, shared, salt ?? new Uint8Array(0), new TextEncoder().encode(info), 32);
|
|
109
|
-
return keccak256(okm);
|
|
110
|
-
}
|
|
111
|
-
export function deriveLongTermShared(myIdentitySecretKey, theirIdentityPublicKey) {
|
|
112
|
-
return nacl.scalarMult(myIdentitySecretKey, theirIdentityPublicKey);
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Directional duplex topics (Initiator-Responder, Responder-Initiator).
|
|
116
|
-
* Recommended salt: tag (bytes)
|
|
117
|
-
*/
|
|
118
|
-
export function deriveDuplexTopics(myIdentitySecretKey, theirIdentityPublicKey, salt) {
|
|
119
|
-
const shared = deriveLongTermShared(myIdentitySecretKey, theirIdentityPublicKey);
|
|
120
|
-
const topicOut = deriveTopic(shared, "verbeth:topic-out:v1", salt);
|
|
121
|
-
const topicIn = deriveTopic(shared, "verbeth:topic-in:v1", salt);
|
|
122
|
-
const chkFull = keccak256(Buffer.concat([
|
|
123
|
-
toUtf8Bytes("verbeth:topic-chk:v1"),
|
|
124
|
-
Buffer.from(topicOut.slice(2), 'hex'),
|
|
125
|
-
Buffer.from(topicIn.slice(2), 'hex'),
|
|
126
|
-
]));
|
|
127
|
-
const checksum = dataSlice(chkFull, 8);
|
|
128
|
-
return { topicOut, topicIn, checksum };
|
|
94
|
+
export function computeHybridTagFromResponder(rSecretKey, viewPubA, kemSecret) {
|
|
95
|
+
const ecdhShared = nacl.scalarMult(rSecretKey, viewPubA);
|
|
96
|
+
return finalizeHybridHsrTag(kemSecret, ecdhShared);
|
|
129
97
|
}
|
|
130
|
-
export function
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
Buffer.from(topicOut.slice(2), 'hex'),
|
|
134
|
-
Buffer.from(topicIn.slice(2), 'hex'),
|
|
135
|
-
]));
|
|
136
|
-
return dataSlice(chkFull, 8) === checksum;
|
|
98
|
+
export function computeHybridTagFromInitiator(viewPrivA, R, kemSecret) {
|
|
99
|
+
const ecdhShared = nacl.scalarMult(viewPrivA, R);
|
|
100
|
+
return finalizeHybridHsrTag(kemSecret, ecdhShared);
|
|
137
101
|
}
|
package/dist/src/executor.d.ts
CHANGED
|
@@ -20,8 +20,7 @@ export declare class BaseSmartAccountExecutor implements IExecutor {
|
|
|
20
20
|
private subAccountAddress?;
|
|
21
21
|
private logChainInterface;
|
|
22
22
|
private chainId;
|
|
23
|
-
constructor(baseAccountProvider: any, logChainAddress: string, chainId?: number,
|
|
24
|
-
paymasterServiceUrl?: string | undefined, subAccountAddress?: string | undefined);
|
|
23
|
+
constructor(baseAccountProvider: any, logChainAddress: string, chainId?: number, paymasterServiceUrl?: string | undefined, subAccountAddress?: string | undefined);
|
|
25
24
|
sendMessage(ciphertext: Uint8Array, topic: string, timestamp: number, nonce: bigint): Promise<any>;
|
|
26
25
|
initiateHandshake(recipientHash: string, pubKeys: string, ephemeralPubKey: string, plaintextPayload: Uint8Array): Promise<any>;
|
|
27
26
|
respondToHandshake(inResponseTo: string, responderEphemeralR: string, ciphertext: Uint8Array): Promise<any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/executor.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,MAAM,EACN,QAAQ,EAER,YAAY,EAGb,MAAM,QAAQ,CAAC;AAOhB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/executor.ts"],"names":[],"mappings":"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"}
|
package/dist/src/executor.js
CHANGED
|
@@ -3,7 +3,6 @@ import { Interface, toBeHex, zeroPadValue, } from "ethers";
|
|
|
3
3
|
function pack128x128(high, low) {
|
|
4
4
|
return (high << 128n) | (low & ((1n << 128n) - 1n));
|
|
5
5
|
}
|
|
6
|
-
// Unpack a packed 256-bit value into two 128-bit values
|
|
7
6
|
export function split128x128(word) {
|
|
8
7
|
const lowMask = (1n << 128n) - 1n;
|
|
9
8
|
return [word >> 128n, word & lowMask];
|
|
@@ -47,8 +46,7 @@ export class EOAExecutor {
|
|
|
47
46
|
}
|
|
48
47
|
// Base Smart Account Executor - Uses wallet_sendCalls for sponsored transactions
|
|
49
48
|
export class BaseSmartAccountExecutor {
|
|
50
|
-
constructor(baseAccountProvider, logChainAddress, chainId = 8453,
|
|
51
|
-
paymasterServiceUrl, subAccountAddress) {
|
|
49
|
+
constructor(baseAccountProvider, logChainAddress, chainId = 8453, paymasterServiceUrl, subAccountAddress) {
|
|
52
50
|
this.baseAccountProvider = baseAccountProvider;
|
|
53
51
|
this.logChainAddress = logChainAddress;
|
|
54
52
|
this.paymasterServiceUrl = paymasterServiceUrl;
|
|
@@ -58,7 +56,6 @@ export class BaseSmartAccountExecutor {
|
|
|
58
56
|
"function initiateHandshake(bytes32 recipientHash, bytes pubKeys, bytes ephemeralPubKey, bytes plaintextPayload)",
|
|
59
57
|
"function respondToHandshake(bytes32 inResponseTo, bytes32 responderEphemeralR, bytes ciphertext)",
|
|
60
58
|
]);
|
|
61
|
-
// Convert chainId to hex
|
|
62
59
|
this.chainId =
|
|
63
60
|
chainId === 8453
|
|
64
61
|
? "0x2105" // Base mainnet
|
|
@@ -103,7 +100,6 @@ export class BaseSmartAccountExecutor {
|
|
|
103
100
|
}
|
|
104
101
|
async executeCalls(calls) {
|
|
105
102
|
try {
|
|
106
|
-
//console.log("DEBUG: Sub account address:", this.subAccountAddress);
|
|
107
103
|
const requestParams = {
|
|
108
104
|
version: "1.0",
|
|
109
105
|
chainId: this.chainId,
|
|
@@ -112,7 +108,6 @@ export class BaseSmartAccountExecutor {
|
|
|
112
108
|
//** WORK IN PROGRESS */
|
|
113
109
|
if (this.subAccountAddress) {
|
|
114
110
|
requestParams.from = this.subAccountAddress;
|
|
115
|
-
//console.log("DEBUG: Using sub account for transaction");
|
|
116
111
|
}
|
|
117
112
|
if (this.paymasterServiceUrl) {
|
|
118
113
|
requestParams.capabilities = {
|
|
@@ -120,19 +115,15 @@ export class BaseSmartAccountExecutor {
|
|
|
120
115
|
url: this.paymasterServiceUrl,
|
|
121
116
|
},
|
|
122
117
|
};
|
|
123
|
-
//console.log("DEBUG: Using paymaster for gas sponsorship");
|
|
124
118
|
}
|
|
125
|
-
//console.log("DEBUG: Request params:", requestParams);
|
|
126
119
|
const result = await this.baseAccountProvider.request({
|
|
127
120
|
method: "wallet_sendCalls",
|
|
128
121
|
params: [requestParams],
|
|
129
122
|
});
|
|
130
|
-
// first 32 bytes are the actual userop hash
|
|
131
123
|
if (typeof result === "string" &&
|
|
132
124
|
result.startsWith("0x") &&
|
|
133
125
|
result.length > 66) {
|
|
134
|
-
const actualTxHash = "0x" + result.slice(2, 66);
|
|
135
|
-
//console.log("DEBUG: extracted tx hash:", actualTxHash);
|
|
126
|
+
const actualTxHash = "0x" + result.slice(2, 66);
|
|
136
127
|
return { hash: actualTxHash };
|
|
137
128
|
}
|
|
138
129
|
return result;
|
|
@@ -155,7 +146,6 @@ export class UserOpExecutor {
|
|
|
155
146
|
"function initiateHandshake(bytes32 recipientHash, bytes pubKeys, bytes ephemeralPubKey, bytes plaintextPayload)",
|
|
156
147
|
"function respondToHandshake(bytes32 inResponseTo, bytes32 responderEphemeralR, bytes ciphertext)",
|
|
157
148
|
]);
|
|
158
|
-
// Smart account interface for executing calls to other contracts
|
|
159
149
|
this.smartAccountInterface = new Interface([
|
|
160
150
|
"function execute(address target, uint256 value, bytes calldata data) returns (bytes)",
|
|
161
151
|
]);
|
|
@@ -164,7 +154,7 @@ export class UserOpExecutor {
|
|
|
164
154
|
const logChainCallData = this.logChainInterface.encodeFunctionData("sendMessage", [ciphertext, topic, timestamp, nonce]);
|
|
165
155
|
const smartAccountCallData = this.smartAccountInterface.encodeFunctionData("execute", [
|
|
166
156
|
this.logChainAddress,
|
|
167
|
-
0,
|
|
157
|
+
0,
|
|
168
158
|
logChainCallData,
|
|
169
159
|
]);
|
|
170
160
|
return this.executeUserOp(smartAccountCallData);
|
|
@@ -173,7 +163,7 @@ export class UserOpExecutor {
|
|
|
173
163
|
const logChainCallData = this.logChainInterface.encodeFunctionData("initiateHandshake", [recipientHash, pubKeys, ephemeralPubKey, plaintextPayload]);
|
|
174
164
|
const smartAccountCallData = this.smartAccountInterface.encodeFunctionData("execute", [
|
|
175
165
|
this.logChainAddress,
|
|
176
|
-
0,
|
|
166
|
+
0,
|
|
177
167
|
logChainCallData,
|
|
178
168
|
]);
|
|
179
169
|
return this.executeUserOp(smartAccountCallData);
|
|
@@ -182,7 +172,7 @@ export class UserOpExecutor {
|
|
|
182
172
|
const logChainCallData = this.logChainInterface.encodeFunctionData("respondToHandshake", [inResponseTo, responderEphemeralR, ciphertext]);
|
|
183
173
|
const smartAccountCallData = this.smartAccountInterface.encodeFunctionData("execute", [
|
|
184
174
|
this.logChainAddress,
|
|
185
|
-
0,
|
|
175
|
+
0,
|
|
186
176
|
logChainCallData,
|
|
187
177
|
]);
|
|
188
178
|
return this.executeUserOp(smartAccountCallData);
|
|
@@ -209,7 +199,7 @@ export class UserOpExecutor {
|
|
|
209
199
|
return receipt;
|
|
210
200
|
}
|
|
211
201
|
}
|
|
212
|
-
// Direct EntryPoint Executor
|
|
202
|
+
// Direct EntryPoint Executor (bypasses bundler for local testing)
|
|
213
203
|
export class DirectEntryPointExecutor {
|
|
214
204
|
constructor(smartAccountAddress, entryPointContract, logChainAddress, smartAccountClient, signer) {
|
|
215
205
|
this.smartAccountAddress = smartAccountAddress;
|
|
@@ -221,7 +211,6 @@ export class DirectEntryPointExecutor {
|
|
|
221
211
|
"function initiateHandshake(bytes32 recipientHash, bytes pubKeys, bytes ephemeralPubKey, bytes plaintextPayload)",
|
|
222
212
|
"function respondToHandshake(bytes32 inResponseTo, bytes32 responderEphemeralR, bytes ciphertext)",
|
|
223
213
|
]);
|
|
224
|
-
// Smart account interface for executing calls to other contracts
|
|
225
214
|
this.smartAccountInterface = new Interface([
|
|
226
215
|
"function execute(address target, uint256 value, bytes calldata data) returns (bytes)",
|
|
227
216
|
]);
|
|
@@ -241,7 +230,7 @@ export class DirectEntryPointExecutor {
|
|
|
241
230
|
const logChainCallData = this.logChainInterface.encodeFunctionData("initiateHandshake", [recipientHash, pubKeys, ephemeralPubKey, plaintextPayload]);
|
|
242
231
|
const smartAccountCallData = this.smartAccountInterface.encodeFunctionData("execute", [
|
|
243
232
|
this.logChainAddress,
|
|
244
|
-
0,
|
|
233
|
+
0,
|
|
245
234
|
logChainCallData,
|
|
246
235
|
]);
|
|
247
236
|
return this.executeDirectUserOp(smartAccountCallData);
|
|
@@ -250,7 +239,7 @@ export class DirectEntryPointExecutor {
|
|
|
250
239
|
const logChainCallData = this.logChainInterface.encodeFunctionData("respondToHandshake", [inResponseTo, responderEphemeralR, ciphertext]);
|
|
251
240
|
const smartAccountCallData = this.smartAccountInterface.encodeFunctionData("execute", [
|
|
252
241
|
this.logChainAddress,
|
|
253
|
-
0,
|
|
242
|
+
0,
|
|
254
243
|
logChainCallData,
|
|
255
244
|
]);
|
|
256
245
|
return this.executeDirectUserOp(smartAccountCallData);
|
|
@@ -290,11 +279,8 @@ export class DirectEntryPointExecutor {
|
|
|
290
279
|
signature: "0x",
|
|
291
280
|
};
|
|
292
281
|
}
|
|
293
|
-
// Pad bigints, bytes32 before signing
|
|
294
282
|
const paddedUserOp = padBigints(userOp);
|
|
295
|
-
//console.log("Padded UserOp:", paddedUserOp);
|
|
296
283
|
const signed = await this.smartAccountClient.signUserOperation(paddedUserOp);
|
|
297
|
-
// Direct submit to EntryPoint
|
|
298
284
|
const tx = await this.entryPointContract.handleOps([signed], await this.signer.getAddress());
|
|
299
285
|
return tx;
|
|
300
286
|
}
|
|
@@ -320,7 +306,6 @@ export class ExecutorFactory {
|
|
|
320
306
|
try {
|
|
321
307
|
const provider = signerOrAccount?.provider || signerOrAccount;
|
|
322
308
|
if (provider && typeof provider.request === "function") {
|
|
323
|
-
// test if provider supports wallet_sendCalls
|
|
324
309
|
const capabilities = await provider
|
|
325
310
|
.request({
|
|
326
311
|
method: "wallet_getCapabilities",
|
|
@@ -347,7 +332,6 @@ export class ExecutorFactory {
|
|
|
347
332
|
return new UserOpExecutor(signerOrAccount.address, options.logChainAddress, options.bundlerClient, signerOrAccount);
|
|
348
333
|
}
|
|
349
334
|
}
|
|
350
|
-
// default to EOA executor
|
|
351
335
|
return new EOAExecutor(contract);
|
|
352
336
|
}
|
|
353
337
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Signer } from "ethers";
|
|
2
|
+
import nacl from 'tweetnacl';
|
|
3
|
+
import { IdentityKeyPair, IdentityProof } from './types.js';
|
|
4
|
+
import { IExecutor } from './executor.js';
|
|
5
|
+
export interface KemKeyPair {
|
|
6
|
+
publicKey: Uint8Array;
|
|
7
|
+
secretKey: Uint8Array;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Initiates an on-chain handshake with unified keys and mandatory identity proof.
|
|
11
|
+
* Executor-agnostic: works with EOA, UserOp, and Direct EntryPoint
|
|
12
|
+
*
|
|
13
|
+
* @returns Transaction, ephemeral keypair, and KEM keypair (must be persisted for session init)
|
|
14
|
+
*/
|
|
15
|
+
export declare function initiateHandshake({ executor, recipientAddress, identityKeyPair, plaintextPayload, identityProof, }: {
|
|
16
|
+
executor: IExecutor;
|
|
17
|
+
recipientAddress: string;
|
|
18
|
+
identityKeyPair: IdentityKeyPair;
|
|
19
|
+
plaintextPayload: string;
|
|
20
|
+
identityProof: IdentityProof;
|
|
21
|
+
signer?: Signer;
|
|
22
|
+
}): Promise<{
|
|
23
|
+
tx: any;
|
|
24
|
+
ephemeralKeyPair: nacl.BoxKeyPair;
|
|
25
|
+
kemKeyPair: KemKeyPair;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Responds to a handshake with unified keys and mandatory identity proof.
|
|
29
|
+
* Executor-agnostic: works with EOA, UserOp, and Direct EntryPoint
|
|
30
|
+
*
|
|
31
|
+
* If initiator includes KEM public key, encapsulates a shared secret and includes ciphertext in response.
|
|
32
|
+
*
|
|
33
|
+
* @returns Transaction, tag, salt, ephemeral keys, and KEM secret
|
|
34
|
+
*/
|
|
35
|
+
export declare function respondToHandshake({ executor, initiatorEphemeralPubKey, responderIdentityKeyPair, note, identityProof, }: {
|
|
36
|
+
executor: IExecutor;
|
|
37
|
+
/** Initiator's ephemeral key (32 bytes X25519) OR extended key (1216 bytes: X25519 + ML-KEM) */
|
|
38
|
+
initiatorEphemeralPubKey: Uint8Array;
|
|
39
|
+
responderIdentityKeyPair: IdentityKeyPair;
|
|
40
|
+
note?: string;
|
|
41
|
+
identityProof: IdentityProof;
|
|
42
|
+
signer?: Signer;
|
|
43
|
+
}): Promise<{
|
|
44
|
+
tx: any;
|
|
45
|
+
salt: Uint8Array;
|
|
46
|
+
tag: `0x${string}`;
|
|
47
|
+
responderEphemeralSecret: Uint8Array;
|
|
48
|
+
responderEphemeralPublic: Uint8Array;
|
|
49
|
+
kemSharedSecret?: Uint8Array;
|
|
50
|
+
}>;
|
|
51
|
+
//# sourceMappingURL=handshake.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../../src/handshake.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,MAAM,EAEP,MAAM,QAAQ,CAAC;AAChB,OAAO,IAAI,MAAM,WAAW,CAAC;AAQ7B,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAI1C,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,UAAU,CAAC;IACtB,SAAS,EAAE,UAAU,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,EACtC,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,aAAa,GACd,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,eAAe,CAAC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IACV,EAAE,EAAE,GAAG,CAAC;IACR,gBAAgB,EAAE,IAAI,CAAC,UAAU,CAAC;IAClC,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC,CA4CD;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,QAAQ,EACR,wBAAwB,EACxB,wBAAwB,EACxB,IAAI,EACJ,aAAa,GACd,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,gGAAgG;IAChG,wBAAwB,EAAE,UAAU,CAAC;IACrC,wBAAwB,EAAE,eAAe,CAAC;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,aAAa,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC;IACV,EAAE,EAAE,GAAG,CAAC;IACR,IAAI,EAAE,UAAU,CAAC;IACjB,GAAG,EAAE,KAAK,MAAM,EAAE,CAAC;IAEnB,wBAAwB,EAAE,UAAU,CAAC;IAErC,wBAAwB,EAAE,UAAU,CAAC;IAErC,eAAe,CAAC,EAAE,UAAU,CAAC;CAC9B,CAAC,CAuFD"}
|