@morseai/sdk 0.1.0-beta.6 → 0.1.0-beta.7

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/index.mjs CHANGED
@@ -26,6 +26,7 @@ __export(crypto_x25519_exports, {
26
26
  deriveKeyPairFromWalletSignature: () => deriveKeyPairFromWalletSignature,
27
27
  generateSignalId: () => generateSignalId,
28
28
  openSharedSignal: () => openSharedSignal,
29
+ sealDataKey: () => sealDataKey,
29
30
  verifyKeyCertificate: () => verifyKeyCertificate
30
31
  });
31
32
  async function getSodium() {
@@ -285,6 +286,54 @@ async function openSharedSignal(encryptedPayloadBase64, payloadNonceBase64, seal
285
286
  );
286
287
  return payload;
287
288
  }
289
+ async function sealDataKey(dataKeyBytes, walletTarget, walletCreator, expiresAt, signalId, domain, chainId, signMessage) {
290
+ await getSodium();
291
+ const senderKeypair = sodium.crypto_box_keypair();
292
+ const recipientKeypair = await deriveKeyPairFromWalletSignature(
293
+ walletTarget,
294
+ domain,
295
+ chainId,
296
+ signMessage
297
+ );
298
+ const recipientPubKey = recipientKeypair.publicKey;
299
+ const sharedSecret = sodium.crypto_scalarmult(
300
+ senderKeypair.privateKey,
301
+ recipientPubKey
302
+ );
303
+ const salt = `MORSE_SEAL_${signalId}_v1`;
304
+ const info = "wrap_datakey";
305
+ const wrappingKey = await hkdfSha256(sharedSecret, salt, info, 32);
306
+ const sealedNonce = sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
307
+ const expiresAtInt = Math.floor(Number(expiresAt));
308
+ const abiCoder = ethers.AbiCoder.defaultAbiCoder();
309
+ const senderEphemeralPubKeyHex = ethers.zeroPadValue(ethers.hexlify(senderKeypair.publicKey), 32);
310
+ const aad = abiCoder.encode(
311
+ ["string", "address", "address", "uint64", "string", "bytes32"],
312
+ [
313
+ signalId,
314
+ walletTarget.toLowerCase(),
315
+ walletCreator.toLowerCase(),
316
+ expiresAtInt,
317
+ X25519_CIPHER_VERSION,
318
+ senderEphemeralPubKeyHex
319
+ ]
320
+ );
321
+ const aadBytes = ethers.getBytes(aad);
322
+ const aadHash = ethers.keccak256(aad).slice(2);
323
+ const sealedDataKey = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
324
+ dataKeyBytes,
325
+ aadBytes,
326
+ null,
327
+ sealedNonce,
328
+ wrappingKey
329
+ );
330
+ return {
331
+ sealedDataKey: Buffer.from(sealedDataKey).toString("base64"),
332
+ sealedNonce: Buffer.from(sealedNonce).toString("base64"),
333
+ senderEphemeralPublicKey: Buffer.from(senderKeypair.publicKey).toString("base64"),
334
+ aadHash
335
+ };
336
+ }
288
337
  function generateSignalId() {
289
338
  const bytes = new Uint8Array(16);
290
339
  globalThis.crypto.getRandomValues(bytes);
@@ -1243,7 +1292,36 @@ var MorseSDKV1 = class {
1243
1292
  }
1244
1293
  async createPrivateSignalEncrypted(wallet, options) {
1245
1294
  const key = await generateKey();
1246
- await exportKey(key);
1295
+ const keyBase64 = await exportKey(key);
1296
+ const signalId = options.signalId || generateSignalId();
1297
+ let expiresAtMs;
1298
+ if (options.expiresAt) {
1299
+ expiresAtMs = new Date(options.expiresAt).getTime();
1300
+ } else if (options.expiresIn) {
1301
+ const match = options.expiresIn.match(/^(\d+)([smhd])$/);
1302
+ if (!match) throw new Error("Invalid expiresIn format");
1303
+ const value = parseInt(match[1], 10);
1304
+ const unit = match[2];
1305
+ const multipliers = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
1306
+ expiresAtMs = Date.now() + value * multipliers[unit];
1307
+ } else {
1308
+ expiresAtMs = Date.now() + 24 * 60 * 60 * 1e3;
1309
+ }
1310
+ const walletTarget = wallet.address;
1311
+ const walletCreator = wallet.address;
1312
+ const domain = "morse.app";
1313
+ const chainId = 8453;
1314
+ const keyBytes = this.base64ToUint8Array(keyBase64);
1315
+ const sealedBox = await sealDataKey(
1316
+ keyBytes,
1317
+ walletTarget.toLowerCase(),
1318
+ walletCreator.toLowerCase(),
1319
+ expiresAtMs,
1320
+ signalId,
1321
+ domain,
1322
+ chainId,
1323
+ wallet.signMessage
1324
+ );
1247
1325
  let encryptedText;
1248
1326
  let payloadNonce;
1249
1327
  let fileOptions;
@@ -1270,6 +1348,7 @@ var MorseSDKV1 = class {
1270
1348
  };
1271
1349
  }
1272
1350
  const signalOptions = {
1351
+ signalId,
1273
1352
  walletTarget: options.walletTarget,
1274
1353
  shareWithRecipient: options.shareWithRecipient,
1275
1354
  mode: options.mode,
@@ -1278,10 +1357,13 @@ var MorseSDKV1 = class {
1278
1357
  cipherVersion: getCipherVersion(),
1279
1358
  encryptedText,
1280
1359
  payloadNonce,
1360
+ sealedDataKey: sealedBox.sealedDataKey,
1361
+ sealedNonce: sealedBox.sealedNonce,
1362
+ senderEphemeralPublicKey: sealedBox.senderEphemeralPublicKey,
1363
+ aadHash: sealedBox.aadHash,
1281
1364
  file: fileOptions,
1282
1365
  onChainNotification: options.onChainNotification,
1283
- expiresAt: options.expiresAt,
1284
- expiresIn: options.expiresIn
1366
+ expiresAt: new Date(expiresAtMs).toISOString()
1285
1367
  };
1286
1368
  const result = await this.createSignal(wallet, signalOptions);
1287
1369
  const shareableLink = generateShareableLink(FRONTEND_BASE_URL, result.signalId);