@calimero-network/mero-js 14.0.0 → 15.1.0

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.cjs CHANGED
@@ -35,6 +35,7 @@ __export(index_exports, {
35
35
  SseClient: () => SseClient,
36
36
  WebHttpClient: () => WebHttpClient,
37
37
  WsClient: () => WsClient,
38
+ accountForRoot: () => accountForRoot,
38
39
  buildAuthLoginUrl: () => buildAuthLoginUrl,
39
40
  combineSignals: () => combineSignals,
40
41
  compareSemver: () => compareSemver,
@@ -53,18 +54,18 @@ __export(index_exports, {
53
54
  createRetryableMethod: () => createRetryableMethod,
54
55
  createTimeoutSignal: () => createTimeoutSignal,
55
56
  createUniversalHttpClient: () => createUniversalHttpClient,
57
+ deviceCertPayload: () => deviceCertPayload,
56
58
  discoverLocalNodes: () => discoverLocalNodes,
57
59
  hasCap: () => hasCap,
58
60
  intentHash: () => intentHash,
59
61
  jsonCodec: () => jsonCodec,
60
62
  localNodeUrl: () => localNodeUrl,
63
+ mintDeviceId: () => mintDeviceId,
61
64
  nodeEndpoint: () => nodeEndpoint,
62
65
  parseAuthCallback: () => parseAuthCallback,
63
66
  probeNodeHealth: () => probeNodeHealth,
64
- sameAccount: () => sameAccount,
67
+ signDeviceCert: () => signDeviceCert,
65
68
  signWarrant: () => signWarrant,
66
- toAccountBase58: () => toAccountBase58,
67
- toAccountHex: () => toAccountHex,
68
69
  withCap: () => withCap,
69
70
  withRetry: () => withRetry,
70
71
  withoutCap: () => withoutCap
@@ -2514,93 +2515,6 @@ function createMeroJs(config) {
2514
2515
  return new MeroJs(config);
2515
2516
  }
2516
2517
 
2517
- // src/account/index.ts
2518
- var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
2519
- var HEX_ACCOUNT = /^[0-9a-f]{64}$/i;
2520
- function encodeBase58(bytes) {
2521
- if (bytes.length === 0) return "";
2522
- const digits = [0];
2523
- for (const byte of bytes) {
2524
- let carry = byte;
2525
- for (let i = 0; i < digits.length; i++) {
2526
- carry += digits[i] << 8;
2527
- digits[i] = carry % 58;
2528
- carry = carry / 58 | 0;
2529
- }
2530
- while (carry > 0) {
2531
- digits.push(carry % 58);
2532
- carry = carry / 58 | 0;
2533
- }
2534
- }
2535
- let out = "";
2536
- for (const byte of bytes) {
2537
- if (byte !== 0) break;
2538
- out += BASE58_ALPHABET[0];
2539
- }
2540
- for (let i = digits.length - 1; i >= 0; i--) {
2541
- out += BASE58_ALPHABET[digits[i]];
2542
- }
2543
- return out;
2544
- }
2545
- function decodeBase58(value) {
2546
- if (value.length === 0) return null;
2547
- const bytes = [0];
2548
- for (const char of value) {
2549
- const index = BASE58_ALPHABET.indexOf(char);
2550
- if (index < 0) return null;
2551
- let carry = index;
2552
- for (let i = 0; i < bytes.length; i++) {
2553
- carry += bytes[i] * 58;
2554
- bytes[i] = carry & 255;
2555
- carry >>= 8;
2556
- }
2557
- while (carry > 0) {
2558
- bytes.push(carry & 255);
2559
- carry >>= 8;
2560
- }
2561
- }
2562
- let leadingZeros = 0;
2563
- for (const char of value) {
2564
- if (char !== BASE58_ALPHABET[0]) break;
2565
- leadingZeros++;
2566
- }
2567
- return Uint8Array.from([
2568
- ...new Array(leadingZeros).fill(0),
2569
- ...bytes.reverse()
2570
- ]);
2571
- }
2572
- function toHex(bytes) {
2573
- let out = "";
2574
- for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
2575
- return out;
2576
- }
2577
- function fromHex(hex2) {
2578
- const bytes = new Uint8Array(hex2.length / 2);
2579
- for (let i = 0; i < bytes.length; i++) {
2580
- bytes[i] = parseInt(hex2.slice(i * 2, i * 2 + 2), 16);
2581
- }
2582
- return bytes;
2583
- }
2584
- function toAccountHex(id) {
2585
- const trimmed = (id ?? "").trim();
2586
- if (!trimmed) return "";
2587
- if (HEX_ACCOUNT.test(trimmed)) return trimmed.toLowerCase();
2588
- const decoded = decodeBase58(trimmed);
2589
- if (!decoded || decoded.length !== 32) return trimmed;
2590
- return toHex(decoded);
2591
- }
2592
- function toAccountBase58(id) {
2593
- const trimmed = (id ?? "").trim();
2594
- if (!trimmed) return "";
2595
- if (!HEX_ACCOUNT.test(trimmed)) return trimmed;
2596
- return encodeBase58(fromHex(trimmed));
2597
- }
2598
- function sameAccount(a, b) {
2599
- const left = toAccountHex(a);
2600
- const right = toAccountHex(b);
2601
- return HEX_ACCOUNT.test(left) && left === right;
2602
- }
2603
-
2604
2518
  // src/token-store/index.ts
2605
2519
  var MemoryTokenStore = class {
2606
2520
  constructor() {
@@ -2682,9 +2596,7 @@ var CloudClient = class {
2682
2596
  }
2683
2597
  };
2684
2598
 
2685
- // src/warrant/warrant.ts
2686
- var SIGN_DOMAIN = new TextEncoder().encode("calimero.warrant.v1");
2687
- var INTENT_DOMAIN = new TextEncoder().encode("calimero.warrant.intent.v1");
2599
+ // src/crypto/internal.ts
2688
2600
  var PKCS8_ED25519_PREFIX = new Uint8Array([
2689
2601
  48,
2690
2602
  46,
@@ -2703,10 +2615,15 @@ var PKCS8_ED25519_PREFIX = new Uint8Array([
2703
2615
  4,
2704
2616
  32
2705
2617
  ]);
2618
+ function u32le(value) {
2619
+ const out = new Uint8Array(4);
2620
+ new DataView(out.buffer).setUint32(0, value, true);
2621
+ return out;
2622
+ }
2706
2623
  function hex(bytes) {
2707
2624
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
2708
2625
  }
2709
- function fromHex2(value, label, expectedBytes) {
2626
+ function fromHex(value, label, expectedBytes) {
2710
2627
  const clean = value.trim();
2711
2628
  if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length !== expectedBytes * 2) {
2712
2629
  throw new Error(
@@ -2742,12 +2659,8 @@ async function domainHash(domain, parts) {
2742
2659
  await crypto.subtle.digest("SHA-256", concat(...chunks))
2743
2660
  );
2744
2661
  }
2745
- async function intentHash(method, argsJson) {
2746
- const args = new TextEncoder().encode(JSON.stringify(argsJson));
2747
- return domainHash(INTENT_DOMAIN, [new TextEncoder().encode(method), args]);
2748
- }
2749
2662
  async function importSigningKey(secretHex) {
2750
- const seed = fromHex2(secretHex, "deviceSecret", 32);
2663
+ const seed = fromHex(secretHex, "deviceSecret", 32);
2751
2664
  const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2752
2665
  try {
2753
2666
  return await crypto.subtle.importKey(
@@ -2764,10 +2677,35 @@ async function importSigningKey(secretHex) {
2764
2677
  );
2765
2678
  }
2766
2679
  }
2680
+ async function derivePublicKey(secretHex) {
2681
+ const seed = fromHex(secretHex, "deviceSecret", 32);
2682
+ const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2683
+ const key = await crypto.subtle.importKey(
2684
+ "pkcs8",
2685
+ pkcs8,
2686
+ { name: "Ed25519" },
2687
+ true,
2688
+ ["sign"]
2689
+ );
2690
+ const jwk = await crypto.subtle.exportKey("jwk", key);
2691
+ const raw = jwk.x;
2692
+ return Uint8Array.from(
2693
+ atob(raw.replace(/-/g, "+").replace(/_/g, "/")),
2694
+ (c) => c.charCodeAt(0)
2695
+ );
2696
+ }
2697
+
2698
+ // src/warrant/warrant.ts
2699
+ var SIGN_DOMAIN = new TextEncoder().encode("calimero.warrant.v1");
2700
+ var INTENT_DOMAIN = new TextEncoder().encode("calimero.warrant.intent.v1");
2701
+ async function intentHash(method, argsJson) {
2702
+ const args = new TextEncoder().encode(JSON.stringify(argsJson));
2703
+ return domainHash(INTENT_DOMAIN, [new TextEncoder().encode(method), args]);
2704
+ }
2767
2705
  async function signWarrant(input) {
2768
- const context = fromHex2(input.context, "context", 32);
2769
- const authorAccount = fromHex2(input.authorAccount, "authorAccount", 32);
2770
- const executor = fromHex2(input.executor, "executor", 32);
2706
+ const context = fromHex(input.context, "context", 32);
2707
+ const authorAccount = fromHex(input.authorAccount, "authorAccount", 32);
2708
+ const executor = fromHex(input.executor, "executor", 32);
2771
2709
  const key = await importSigningKey(input.deviceSecret);
2772
2710
  const publicKey = await derivePublicKey(input.deviceSecret);
2773
2711
  const commitment = await intentHash(input.method, input.argsJson);
@@ -2798,21 +2736,72 @@ async function signWarrant(input) {
2798
2736
  )
2799
2737
  );
2800
2738
  }
2801
- async function derivePublicKey(secretHex) {
2802
- const seed = fromHex2(secretHex, "deviceSecret", 32);
2803
- const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2804
- const key = await crypto.subtle.importKey(
2805
- "pkcs8",
2806
- pkcs8,
2807
- { name: "Ed25519" },
2808
- true,
2809
- ["sign"]
2739
+
2740
+ // src/device-cert/device-cert.ts
2741
+ var CERT_DOMAIN = new TextEncoder().encode("calimero.device.cert.v1");
2742
+ var ACCOUNT_ID_DOMAIN = new TextEncoder().encode("calimero.account.genesis.v1");
2743
+ var DEVICE_ID_DOMAIN = new TextEncoder().encode("calimero.device.id.v1");
2744
+ var ACCOUNT_GENESIS_VERSION = 1;
2745
+ async function mintDeviceId(account, nonce) {
2746
+ if (nonce.length !== 16) {
2747
+ throw new Error(`nonce must be 16 bytes, got ${nonce.length}`);
2748
+ }
2749
+ const accountBytes = fromHex(account, "account", 32);
2750
+ return hex(await domainHash(DEVICE_ID_DOMAIN, [accountBytes, nonce]));
2751
+ }
2752
+ async function deviceCertPayload(input) {
2753
+ return domainHash(CERT_DOMAIN, [
2754
+ fromHex(input.account, "account", 32),
2755
+ fromHex(input.device, "device", 32),
2756
+ fromHex(input.signPublicKey, "signPublicKey", 32),
2757
+ fromHex(input.kemPublicKey, "kemPublicKey", 32),
2758
+ u32le(input.keyEpoch),
2759
+ u32le(input.deviceEpoch)
2760
+ ]);
2761
+ }
2762
+ async function signDeviceCert(input) {
2763
+ const rootPublicKey = await derivePublicKey(input.rootSecret);
2764
+ const account = hex(
2765
+ await domainHash(ACCOUNT_ID_DOMAIN, [
2766
+ concat(new Uint8Array([ACCOUNT_GENESIS_VERSION]), rootPublicKey)
2767
+ ])
2810
2768
  );
2811
- const jwk = await crypto.subtle.exportKey("jwk", key);
2812
- const raw = jwk.x;
2813
- return Uint8Array.from(
2814
- atob(raw.replace(/-/g, "+").replace(/_/g, "/")),
2815
- (c) => c.charCodeAt(0)
2769
+ const keyEpoch = 0;
2770
+ const payload = await deviceCertPayload({
2771
+ account,
2772
+ device: input.device,
2773
+ signPublicKey: input.signPublicKey,
2774
+ kemPublicKey: input.kemPublicKey,
2775
+ keyEpoch,
2776
+ deviceEpoch: input.deviceEpoch
2777
+ });
2778
+ const key = await importSigningKey(input.rootSecret);
2779
+ const signature = new Uint8Array(
2780
+ await crypto.subtle.sign("Ed25519", key, payload)
2781
+ );
2782
+ const credential = concat(
2783
+ // AccountGenesis
2784
+ new Uint8Array([ACCOUNT_GENESIS_VERSION]),
2785
+ rootPublicKey,
2786
+ // chain: Vec<RootKeyHandoff>, empty
2787
+ u32le(0),
2788
+ // DeviceCert
2789
+ fromHex(account, "account", 32),
2790
+ fromHex(input.device, "device", 32),
2791
+ fromHex(input.signPublicKey, "signPublicKey", 32),
2792
+ fromHex(input.kemPublicKey, "kemPublicKey", 32),
2793
+ u32le(keyEpoch),
2794
+ u32le(input.deviceEpoch),
2795
+ signature
2796
+ );
2797
+ return hex(credential);
2798
+ }
2799
+ async function accountForRoot(rootSecret) {
2800
+ const rootPublicKey = await derivePublicKey(rootSecret);
2801
+ return hex(
2802
+ await domainHash(ACCOUNT_ID_DOMAIN, [
2803
+ concat(new Uint8Array([ACCOUNT_GENESIS_VERSION]), rootPublicKey)
2804
+ ])
2816
2805
  );
2817
2806
  }
2818
2807