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

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 CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  TypeScript SDK for creating and accessing encrypted signals in the MORSE platform.
8
8
 
9
- **Version:** 0.1.0-beta.6 (Beta Release)
9
+ **Version:** 0.1.0-beta.8 (Beta Release)
10
10
 
11
11
  > ⚠️ **Beta Notice**: This is a beta release. The API is stable but may have minor changes before the 1.0.0 release. Please report any issues you encounter.
12
12
 
package/dist/index.d.mts CHANGED
@@ -73,6 +73,16 @@ interface CreateSignalOptionsEncrypted {
73
73
  enabled: boolean;
74
74
  network: string;
75
75
  };
76
+ /**
77
+ * Domain for key derivation (optional, defaults to "morseai.tech")
78
+ * Should match the domain used when opening the signal
79
+ */
80
+ domain?: string;
81
+ /**
82
+ * Chain ID for key derivation (optional, defaults to 8453 for Base)
83
+ * Should match the chain ID used when opening the signal
84
+ */
85
+ chainId?: number;
76
86
  /**
77
87
  * Specific expiration date and time (ISO 8601 format).
78
88
  * Use this for custom expiration dates.
package/dist/index.d.ts CHANGED
@@ -73,6 +73,16 @@ interface CreateSignalOptionsEncrypted {
73
73
  enabled: boolean;
74
74
  network: string;
75
75
  };
76
+ /**
77
+ * Domain for key derivation (optional, defaults to "morseai.tech")
78
+ * Should match the domain used when opening the signal
79
+ */
80
+ domain?: string;
81
+ /**
82
+ * Chain ID for key derivation (optional, defaults to 8453 for Base)
83
+ * Should match the chain ID used when opening the signal
84
+ */
85
+ chainId?: number;
76
86
  /**
77
87
  * Specific expiration date and time (ISO 8601 format).
78
88
  * Use this for custom expiration dates.
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ __export(crypto_x25519_exports, {
32
32
  deriveKeyPairFromWalletSignature: () => deriveKeyPairFromWalletSignature,
33
33
  generateSignalId: () => generateSignalId,
34
34
  openSharedSignal: () => openSharedSignal,
35
+ sealDataKey: () => sealDataKey,
35
36
  verifyKeyCertificate: () => verifyKeyCertificate
36
37
  });
37
38
  async function getSodium() {
@@ -291,6 +292,54 @@ async function openSharedSignal(encryptedPayloadBase64, payloadNonceBase64, seal
291
292
  );
292
293
  return payload;
293
294
  }
295
+ async function sealDataKey(dataKeyBytes, walletTarget, walletCreator, expiresAt, signalId, domain, chainId, signMessage) {
296
+ await getSodium();
297
+ const senderKeypair = sodium.crypto_box_keypair();
298
+ const recipientKeypair = await deriveKeyPairFromWalletSignature(
299
+ walletTarget,
300
+ domain,
301
+ chainId,
302
+ signMessage
303
+ );
304
+ const recipientPubKey = recipientKeypair.publicKey;
305
+ const sharedSecret = sodium.crypto_scalarmult(
306
+ senderKeypair.privateKey,
307
+ recipientPubKey
308
+ );
309
+ const salt = `MORSE_SEAL_${signalId}_v1`;
310
+ const info = "wrap_datakey";
311
+ const wrappingKey = await hkdfSha256(sharedSecret, salt, info, 32);
312
+ const sealedNonce = sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
313
+ const expiresAtInt = Math.floor(Number(expiresAt));
314
+ const abiCoder = ethers.ethers.AbiCoder.defaultAbiCoder();
315
+ const senderEphemeralPubKeyHex = ethers.ethers.zeroPadValue(ethers.ethers.hexlify(senderKeypair.publicKey), 32);
316
+ const aad = abiCoder.encode(
317
+ ["string", "address", "address", "uint64", "string", "bytes32"],
318
+ [
319
+ signalId,
320
+ walletTarget.toLowerCase(),
321
+ walletCreator.toLowerCase(),
322
+ expiresAtInt,
323
+ exports.X25519_CIPHER_VERSION,
324
+ senderEphemeralPubKeyHex
325
+ ]
326
+ );
327
+ const aadBytes = ethers.ethers.getBytes(aad);
328
+ const aadHash = ethers.ethers.keccak256(aad).slice(2);
329
+ const sealedDataKey = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
330
+ dataKeyBytes,
331
+ aadBytes,
332
+ null,
333
+ sealedNonce,
334
+ wrappingKey
335
+ );
336
+ return {
337
+ sealedDataKey: Buffer.from(sealedDataKey).toString("base64"),
338
+ sealedNonce: Buffer.from(sealedNonce).toString("base64"),
339
+ senderEphemeralPublicKey: Buffer.from(senderKeypair.publicKey).toString("base64"),
340
+ aadHash
341
+ };
342
+ }
294
343
  function generateSignalId() {
295
344
  const bytes = new Uint8Array(16);
296
345
  globalThis.crypto.getRandomValues(bytes);
@@ -1249,7 +1298,36 @@ var MorseSDKV1 = class {
1249
1298
  }
1250
1299
  async createPrivateSignalEncrypted(wallet, options) {
1251
1300
  const key = await generateKey();
1252
- await exportKey(key);
1301
+ const keyBase64 = await exportKey(key);
1302
+ const signalId = options.signalId || generateSignalId();
1303
+ let expiresAtMs;
1304
+ if (options.expiresAt) {
1305
+ expiresAtMs = new Date(options.expiresAt).getTime();
1306
+ } else if (options.expiresIn) {
1307
+ const match = options.expiresIn.match(/^(\d+)([smhd])$/);
1308
+ if (!match) throw new Error("Invalid expiresIn format");
1309
+ const value = parseInt(match[1], 10);
1310
+ const unit = match[2];
1311
+ const multipliers = { s: 1e3, m: 6e4, h: 36e5, d: 864e5 };
1312
+ expiresAtMs = Date.now() + value * multipliers[unit];
1313
+ } else {
1314
+ expiresAtMs = Date.now() + 24 * 60 * 60 * 1e3;
1315
+ }
1316
+ const walletTarget = wallet.address;
1317
+ const walletCreator = wallet.address;
1318
+ const domain = options.domain || "morseai.tech";
1319
+ const chainId = options.chainId ?? 8453;
1320
+ const keyBytes = this.base64ToUint8Array(keyBase64);
1321
+ const sealedBox = await sealDataKey(
1322
+ keyBytes,
1323
+ walletTarget.toLowerCase(),
1324
+ walletCreator.toLowerCase(),
1325
+ expiresAtMs,
1326
+ signalId,
1327
+ domain,
1328
+ chainId,
1329
+ wallet.signMessage
1330
+ );
1253
1331
  let encryptedText;
1254
1332
  let payloadNonce;
1255
1333
  let fileOptions;
@@ -1276,6 +1354,7 @@ var MorseSDKV1 = class {
1276
1354
  };
1277
1355
  }
1278
1356
  const signalOptions = {
1357
+ signalId,
1279
1358
  walletTarget: options.walletTarget,
1280
1359
  shareWithRecipient: options.shareWithRecipient,
1281
1360
  mode: options.mode,
@@ -1284,10 +1363,13 @@ var MorseSDKV1 = class {
1284
1363
  cipherVersion: getCipherVersion(),
1285
1364
  encryptedText,
1286
1365
  payloadNonce,
1366
+ sealedDataKey: sealedBox.sealedDataKey,
1367
+ sealedNonce: sealedBox.sealedNonce,
1368
+ senderEphemeralPublicKey: sealedBox.senderEphemeralPublicKey,
1369
+ aadHash: sealedBox.aadHash,
1287
1370
  file: fileOptions,
1288
1371
  onChainNotification: options.onChainNotification,
1289
- expiresAt: options.expiresAt,
1290
- expiresIn: options.expiresIn
1372
+ expiresAt: new Date(expiresAtMs).toISOString()
1291
1373
  };
1292
1374
  const result = await this.createSignal(wallet, signalOptions);
1293
1375
  const shareableLink = generateShareableLink(FRONTEND_BASE_URL, result.signalId);