@reverbia/sdk 1.0.0-next.20251229084841 → 1.1.0-next.20251230221037
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/dist/expo/index.cjs +18 -0
- package/dist/expo/index.d.mts +12 -0
- package/dist/expo/index.d.ts +12 -0
- package/dist/expo/index.mjs +18 -0
- package/dist/react/chunk-KUFGQF6E.mjs +290 -0
- package/dist/react/chunk-T56Y62G7.mjs +410 -0
- package/dist/react/index.cjs +1617 -281
- package/dist/react/index.d.mts +205 -2
- package/dist/react/index.d.ts +205 -2
- package/dist/react/index.mjs +901 -279
- package/dist/react/storage-Z2NBANCK.mjs +29 -0
- package/dist/react/useEncryption-5RTXKDNZ.mjs +31 -0
- package/package.json +2 -1
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/lib/validation.ts
|
|
13
|
+
function isValidWalletAddress(address) {
|
|
14
|
+
if (!address || typeof address !== "string") return false;
|
|
15
|
+
const ethereumAddressRegex = /^0x[a-fA-F0-9]{40}$/;
|
|
16
|
+
return ethereumAddressRegex.test(address);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/react/useEncryption.ts
|
|
20
|
+
var SIGN_MESSAGE = "The app is asking you to sign this message to generate encryption keys, which will be used to encrypt data and for encryption with cloud services.";
|
|
21
|
+
var encryptionKeyStore = /* @__PURE__ */ new Map();
|
|
22
|
+
var keyPairStore = /* @__PURE__ */ new Map();
|
|
23
|
+
function getStoredKey(address) {
|
|
24
|
+
return encryptionKeyStore.get(address) ?? null;
|
|
25
|
+
}
|
|
26
|
+
function setStoredKey(address, keyHex) {
|
|
27
|
+
encryptionKeyStore.set(address, keyHex);
|
|
28
|
+
}
|
|
29
|
+
function clearEncryptionKey(address) {
|
|
30
|
+
encryptionKeyStore.delete(address);
|
|
31
|
+
}
|
|
32
|
+
function clearAllEncryptionKeys() {
|
|
33
|
+
encryptionKeyStore.clear();
|
|
34
|
+
}
|
|
35
|
+
function hexToBytes(hex) {
|
|
36
|
+
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
37
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
38
|
+
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
39
|
+
bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
|
|
40
|
+
}
|
|
41
|
+
return bytes;
|
|
42
|
+
}
|
|
43
|
+
function bytesToHex(bytes) {
|
|
44
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
45
|
+
}
|
|
46
|
+
async function deriveKeyFromSignature(signature) {
|
|
47
|
+
const sigBytes = hexToBytes(signature);
|
|
48
|
+
const hashBuffer = await crypto.subtle.digest(
|
|
49
|
+
"SHA-256",
|
|
50
|
+
sigBytes.buffer
|
|
51
|
+
);
|
|
52
|
+
const hashBytes = new Uint8Array(hashBuffer);
|
|
53
|
+
return bytesToHex(hashBytes);
|
|
54
|
+
}
|
|
55
|
+
function getStoredKeyPair(address) {
|
|
56
|
+
return keyPairStore.get(address) ?? null;
|
|
57
|
+
}
|
|
58
|
+
function setStoredKeyPair(address, keyPair) {
|
|
59
|
+
keyPairStore.set(address, keyPair);
|
|
60
|
+
}
|
|
61
|
+
async function deriveKeyPairFromSignature(signature, walletAddress) {
|
|
62
|
+
const sigBytes = hexToBytes(signature);
|
|
63
|
+
const seedBuffer = await crypto.subtle.digest(
|
|
64
|
+
"SHA-256",
|
|
65
|
+
sigBytes.buffer
|
|
66
|
+
);
|
|
67
|
+
const seed = new Uint8Array(seedBuffer);
|
|
68
|
+
const addressBytes = hexToBytes(walletAddress);
|
|
69
|
+
const hkdfKey = await crypto.subtle.importKey(
|
|
70
|
+
"raw",
|
|
71
|
+
seed.buffer,
|
|
72
|
+
{ name: "HKDF" },
|
|
73
|
+
false,
|
|
74
|
+
["deriveBits"]
|
|
75
|
+
);
|
|
76
|
+
const saltBytes = new Uint8Array(addressBytes);
|
|
77
|
+
const derivedBits = await crypto.subtle.deriveBits(
|
|
78
|
+
{
|
|
79
|
+
name: "HKDF",
|
|
80
|
+
hash: "SHA-256",
|
|
81
|
+
salt: saltBytes,
|
|
82
|
+
// Use wallet address as salt for deterministic derivation
|
|
83
|
+
info: new TextEncoder().encode("ECDH-P256-KeyPair")
|
|
84
|
+
// Context info
|
|
85
|
+
},
|
|
86
|
+
hkdfKey,
|
|
87
|
+
256
|
|
88
|
+
// 32 bytes = 256 bits
|
|
89
|
+
);
|
|
90
|
+
const privateKeyBytes = new Uint8Array(derivedBits);
|
|
91
|
+
if (privateKeyBytes.every((b) => b === 0)) {
|
|
92
|
+
privateKeyBytes[31] = 1;
|
|
93
|
+
}
|
|
94
|
+
const privateKey = await crypto.subtle.importKey(
|
|
95
|
+
"pkcs8",
|
|
96
|
+
await createPKCS8PrivateKey(privateKeyBytes),
|
|
97
|
+
{
|
|
98
|
+
name: "ECDH",
|
|
99
|
+
namedCurve: "P-256"
|
|
100
|
+
},
|
|
101
|
+
true,
|
|
102
|
+
// extractable so we can export to JWK
|
|
103
|
+
["deriveBits", "deriveKey"]
|
|
104
|
+
);
|
|
105
|
+
const privateKeyJwk = await crypto.subtle.exportKey("jwk", privateKey);
|
|
106
|
+
if (!privateKeyJwk.x || !privateKeyJwk.y) {
|
|
107
|
+
throw new Error("Failed to derive public key from private key");
|
|
108
|
+
}
|
|
109
|
+
const publicKey = await crypto.subtle.importKey(
|
|
110
|
+
"jwk",
|
|
111
|
+
{
|
|
112
|
+
kty: "EC",
|
|
113
|
+
crv: "P-256",
|
|
114
|
+
x: privateKeyJwk.x,
|
|
115
|
+
y: privateKeyJwk.y
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "ECDH",
|
|
119
|
+
namedCurve: "P-256"
|
|
120
|
+
},
|
|
121
|
+
true,
|
|
122
|
+
// extractable
|
|
123
|
+
[]
|
|
124
|
+
// no key usage needed for public key
|
|
125
|
+
);
|
|
126
|
+
return {
|
|
127
|
+
privateKey,
|
|
128
|
+
publicKey
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
async function createPKCS8PrivateKey(privateKeyBytes) {
|
|
132
|
+
const ecPublicKeyOID = new Uint8Array([
|
|
133
|
+
6,
|
|
134
|
+
7,
|
|
135
|
+
42,
|
|
136
|
+
134,
|
|
137
|
+
72,
|
|
138
|
+
206,
|
|
139
|
+
61,
|
|
140
|
+
2,
|
|
141
|
+
1
|
|
142
|
+
]);
|
|
143
|
+
const prime256v1OID = new Uint8Array([
|
|
144
|
+
6,
|
|
145
|
+
8,
|
|
146
|
+
42,
|
|
147
|
+
134,
|
|
148
|
+
72,
|
|
149
|
+
206,
|
|
150
|
+
61,
|
|
151
|
+
3,
|
|
152
|
+
1,
|
|
153
|
+
7
|
|
154
|
+
]);
|
|
155
|
+
const version = new Uint8Array([2, 1, 1]);
|
|
156
|
+
const privateKeyOctet = new Uint8Array([
|
|
157
|
+
4,
|
|
158
|
+
32,
|
|
159
|
+
// OCTET STRING, 32 bytes
|
|
160
|
+
...privateKeyBytes
|
|
161
|
+
]);
|
|
162
|
+
const ecPrivateKeyContent = new Uint8Array([
|
|
163
|
+
...version,
|
|
164
|
+
...privateKeyOctet
|
|
165
|
+
]);
|
|
166
|
+
const ecPrivateKeyLength = ecPrivateKeyContent.length;
|
|
167
|
+
const ecPrivateKeySeq = new Uint8Array([
|
|
168
|
+
48,
|
|
169
|
+
// SEQUENCE
|
|
170
|
+
ecPrivateKeyLength,
|
|
171
|
+
// Length
|
|
172
|
+
...ecPrivateKeyContent
|
|
173
|
+
]);
|
|
174
|
+
const wrappedPrivateKey = new Uint8Array([
|
|
175
|
+
4,
|
|
176
|
+
// OCTET STRING
|
|
177
|
+
ecPrivateKeySeq.length,
|
|
178
|
+
// Length
|
|
179
|
+
...ecPrivateKeySeq
|
|
180
|
+
]);
|
|
181
|
+
const algorithmIdContent = new Uint8Array([
|
|
182
|
+
...ecPublicKeyOID,
|
|
183
|
+
...prime256v1OID
|
|
184
|
+
]);
|
|
185
|
+
const algorithmIdLength = algorithmIdContent.length;
|
|
186
|
+
const algorithmIdSeq = new Uint8Array([
|
|
187
|
+
48,
|
|
188
|
+
// SEQUENCE
|
|
189
|
+
algorithmIdLength,
|
|
190
|
+
// Length
|
|
191
|
+
...algorithmIdContent
|
|
192
|
+
]);
|
|
193
|
+
const pkcs8Version = new Uint8Array([2, 1, 0]);
|
|
194
|
+
const pkcs8Content = new Uint8Array([
|
|
195
|
+
...pkcs8Version,
|
|
196
|
+
...algorithmIdSeq,
|
|
197
|
+
...wrappedPrivateKey
|
|
198
|
+
]);
|
|
199
|
+
const pkcs8ContentLength = pkcs8Content.length;
|
|
200
|
+
const pkcs8Seq = new Uint8Array([
|
|
201
|
+
48,
|
|
202
|
+
// SEQUENCE
|
|
203
|
+
pkcs8ContentLength,
|
|
204
|
+
// Length
|
|
205
|
+
...pkcs8Content
|
|
206
|
+
]);
|
|
207
|
+
return pkcs8Seq.buffer;
|
|
208
|
+
}
|
|
209
|
+
async function getEncryptionKey(address) {
|
|
210
|
+
const keyHex = getStoredKey(address);
|
|
211
|
+
if (!keyHex) {
|
|
212
|
+
throw new Error(
|
|
213
|
+
"Encryption key not found. Please sign a message to generate your encryption key."
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
const keyBytes = hexToBytes(keyHex);
|
|
217
|
+
return crypto.subtle.importKey(
|
|
218
|
+
"raw",
|
|
219
|
+
keyBytes.buffer,
|
|
220
|
+
{ name: "AES-GCM" },
|
|
221
|
+
false,
|
|
222
|
+
["encrypt", "decrypt"]
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
async function encryptData(plaintext, address) {
|
|
226
|
+
if (!isValidWalletAddress(address)) {
|
|
227
|
+
throw new Error(`Invalid wallet address: ${address}. Wallet address must be a valid Ethereum address (0x followed by 40 hex characters).`);
|
|
228
|
+
}
|
|
229
|
+
const key = await getEncryptionKey(address);
|
|
230
|
+
const plaintextBytes = typeof plaintext === "string" ? new TextEncoder().encode(plaintext) : plaintext;
|
|
231
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
232
|
+
const encryptedData = await crypto.subtle.encrypt(
|
|
233
|
+
{
|
|
234
|
+
name: "AES-GCM",
|
|
235
|
+
iv
|
|
236
|
+
},
|
|
237
|
+
key,
|
|
238
|
+
plaintextBytes.buffer
|
|
239
|
+
);
|
|
240
|
+
const encryptedBytes = new Uint8Array(encryptedData);
|
|
241
|
+
const combined = new Uint8Array(iv.length + encryptedBytes.length);
|
|
242
|
+
combined.set(iv, 0);
|
|
243
|
+
combined.set(encryptedBytes, iv.length);
|
|
244
|
+
return bytesToHex(combined);
|
|
245
|
+
}
|
|
246
|
+
async function decryptData(encryptedHex, address) {
|
|
247
|
+
if (!isValidWalletAddress(address)) {
|
|
248
|
+
throw new Error(`Invalid wallet address: ${address}. Wallet address must be a valid Ethereum address (0x followed by 40 hex characters).`);
|
|
249
|
+
}
|
|
250
|
+
const key = await getEncryptionKey(address);
|
|
251
|
+
const combined = hexToBytes(encryptedHex);
|
|
252
|
+
const iv = combined.slice(0, 12);
|
|
253
|
+
const encryptedData = combined.slice(12);
|
|
254
|
+
const decryptedData = await crypto.subtle.decrypt(
|
|
255
|
+
{
|
|
256
|
+
name: "AES-GCM",
|
|
257
|
+
iv
|
|
258
|
+
},
|
|
259
|
+
key,
|
|
260
|
+
encryptedData
|
|
261
|
+
);
|
|
262
|
+
return new TextDecoder().decode(decryptedData);
|
|
263
|
+
}
|
|
264
|
+
async function decryptDataBytes(encryptedHex, address) {
|
|
265
|
+
const key = await getEncryptionKey(address);
|
|
266
|
+
const combined = hexToBytes(encryptedHex);
|
|
267
|
+
const iv = combined.slice(0, 12);
|
|
268
|
+
const encryptedData = combined.slice(12);
|
|
269
|
+
const decryptedData = await crypto.subtle.decrypt(
|
|
270
|
+
{
|
|
271
|
+
name: "AES-GCM",
|
|
272
|
+
iv
|
|
273
|
+
},
|
|
274
|
+
key,
|
|
275
|
+
encryptedData
|
|
276
|
+
);
|
|
277
|
+
return new Uint8Array(decryptedData);
|
|
278
|
+
}
|
|
279
|
+
function hasEncryptionKey(address) {
|
|
280
|
+
return getStoredKey(address) !== null;
|
|
281
|
+
}
|
|
282
|
+
function isRateLimitError(error) {
|
|
283
|
+
if (!error) return false;
|
|
284
|
+
if (typeof error === "object") {
|
|
285
|
+
const err = error;
|
|
286
|
+
if (err.status === 429 || err.statusCode === 429) return true;
|
|
287
|
+
if (err.code === "429" || err.code === "RATE_LIMIT_EXCEEDED") return true;
|
|
288
|
+
if (err.message?.includes("429") || err.message?.toLowerCase().includes("rate limit")) return true;
|
|
289
|
+
}
|
|
290
|
+
if (typeof error === "string") {
|
|
291
|
+
return error.includes("429") || error.toLowerCase().includes("rate limit");
|
|
292
|
+
}
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
async function signMessageWithRetry(signMessage, message, maxRetries = 3, initialDelay = 1e3) {
|
|
296
|
+
let lastError;
|
|
297
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
298
|
+
try {
|
|
299
|
+
return await signMessage(message);
|
|
300
|
+
} catch (error) {
|
|
301
|
+
lastError = error;
|
|
302
|
+
if (!isRateLimitError(error)) {
|
|
303
|
+
throw error;
|
|
304
|
+
}
|
|
305
|
+
if (attempt === maxRetries) {
|
|
306
|
+
throw new Error(
|
|
307
|
+
`Rate limit exceeded: Unable to sign message after ${maxRetries + 1} attempts. Please wait a moment and try again. Privy allows 5 signatures per 60 seconds.`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
const delay = Math.min(
|
|
311
|
+
initialDelay * Math.pow(2, attempt),
|
|
312
|
+
6e4
|
|
313
|
+
// Max 60 seconds
|
|
314
|
+
);
|
|
315
|
+
if (process.env.NODE_ENV === "development") {
|
|
316
|
+
console.warn(
|
|
317
|
+
`[Encryption] Rate limit hit (attempt ${attempt + 1}/${maxRetries + 1}). Retrying in ${Math.ceil(delay / 1e3)}s...`
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
throw lastError;
|
|
324
|
+
}
|
|
325
|
+
async function requestEncryptionKey(walletAddress, signMessage) {
|
|
326
|
+
if (!isValidWalletAddress(walletAddress)) {
|
|
327
|
+
throw new Error(`Invalid wallet address: ${walletAddress}. Wallet address must be a valid Ethereum address (0x followed by 40 hex characters).`);
|
|
328
|
+
}
|
|
329
|
+
const existingKey = getStoredKey(walletAddress);
|
|
330
|
+
if (existingKey) {
|
|
331
|
+
try {
|
|
332
|
+
const { reEncryptUnencryptedTokens } = await import("./storage-Z2NBANCK.mjs");
|
|
333
|
+
await reEncryptUnencryptedTokens(walletAddress);
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const signature = await signMessageWithRetry(signMessage, SIGN_MESSAGE);
|
|
339
|
+
const encryptionKey = await deriveKeyFromSignature(signature);
|
|
340
|
+
setStoredKey(walletAddress, encryptionKey);
|
|
341
|
+
try {
|
|
342
|
+
const { reEncryptUnencryptedTokens } = await import("./storage-Z2NBANCK.mjs");
|
|
343
|
+
await reEncryptUnencryptedTokens(walletAddress);
|
|
344
|
+
} catch {
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
async function getKeyPair(address, signMessage) {
|
|
348
|
+
const existingKeyPair = getStoredKeyPair(address);
|
|
349
|
+
if (existingKeyPair) {
|
|
350
|
+
return existingKeyPair;
|
|
351
|
+
}
|
|
352
|
+
await requestKeyPair(address, signMessage);
|
|
353
|
+
const keyPair = getStoredKeyPair(address);
|
|
354
|
+
if (!keyPair) {
|
|
355
|
+
throw new Error(
|
|
356
|
+
"Key pair not found. Please sign a message to generate your key pair."
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
return keyPair;
|
|
360
|
+
}
|
|
361
|
+
async function requestKeyPair(walletAddress, signMessage) {
|
|
362
|
+
const existingKeyPair = getStoredKeyPair(walletAddress);
|
|
363
|
+
if (existingKeyPair) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
const signature = await signMessageWithRetry(signMessage, SIGN_MESSAGE);
|
|
367
|
+
const keyPair = await deriveKeyPairFromSignature(signature, walletAddress);
|
|
368
|
+
setStoredKeyPair(walletAddress, keyPair);
|
|
369
|
+
}
|
|
370
|
+
async function exportPublicKey(address, signMessage) {
|
|
371
|
+
const keyPair = await getKeyPair(address, signMessage);
|
|
372
|
+
const spkiBuffer = await crypto.subtle.exportKey("spki", keyPair.publicKey);
|
|
373
|
+
const spkiBytes = new Uint8Array(spkiBuffer);
|
|
374
|
+
return btoa(String.fromCharCode(...spkiBytes));
|
|
375
|
+
}
|
|
376
|
+
function hasKeyPair(address) {
|
|
377
|
+
return getStoredKeyPair(address) !== null;
|
|
378
|
+
}
|
|
379
|
+
function clearKeyPair(address) {
|
|
380
|
+
keyPairStore.delete(address);
|
|
381
|
+
}
|
|
382
|
+
function clearAllKeyPairs() {
|
|
383
|
+
keyPairStore.clear();
|
|
384
|
+
}
|
|
385
|
+
function useEncryption(signMessage) {
|
|
386
|
+
return {
|
|
387
|
+
requestEncryptionKey: (walletAddress) => requestEncryptionKey(walletAddress, signMessage),
|
|
388
|
+
requestKeyPair: (walletAddress) => requestKeyPair(walletAddress, signMessage),
|
|
389
|
+
exportPublicKey: (walletAddress) => exportPublicKey(walletAddress, signMessage),
|
|
390
|
+
hasKeyPair: (walletAddress) => hasKeyPair(walletAddress),
|
|
391
|
+
clearKeyPair: (walletAddress) => clearKeyPair(walletAddress)
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export {
|
|
396
|
+
__decorateClass,
|
|
397
|
+
clearEncryptionKey,
|
|
398
|
+
clearAllEncryptionKeys,
|
|
399
|
+
encryptData,
|
|
400
|
+
decryptData,
|
|
401
|
+
decryptDataBytes,
|
|
402
|
+
hasEncryptionKey,
|
|
403
|
+
requestEncryptionKey,
|
|
404
|
+
requestKeyPair,
|
|
405
|
+
exportPublicKey,
|
|
406
|
+
hasKeyPair,
|
|
407
|
+
clearKeyPair,
|
|
408
|
+
clearAllKeyPairs,
|
|
409
|
+
useEncryption
|
|
410
|
+
};
|