@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.d.ts CHANGED
@@ -5,7 +5,6 @@ export * from './auth-api/index.js';
5
5
  export * from './admin-api/index.js';
6
6
  export { parseAuthCallback, buildAuthLoginUrl } from './auth/index.js';
7
7
  export type { AuthCallbackResult, AuthLoginOptions } from './auth/index.js';
8
- export { toAccountHex, toAccountBase58, sameAccount, } from './account/index.js';
9
8
  export { MemoryTokenStore, LocalStorageTokenStore } from './token-store/index.js';
10
9
  export type { TokenStore } from './token-store/index.js';
11
10
  export { RpcClient, RpcError } from './rpc/index.js';
@@ -17,6 +16,8 @@ export { EphemeralClient, jsonCodec } from './ephemeral/index.js';
17
16
  export type { Codec, EphemeralEntry } from './ephemeral/index.js';
18
17
  export * from './cloud/index.js';
19
18
  export { signWarrant, intentHash } from './warrant/index.js';
19
+ export { signDeviceCert, mintDeviceId, deviceCertPayload, accountForRoot, } from './device-cert/index.js';
20
+ export type { DeviceCertInput } from './device-cert/index.js';
20
21
  export type { WarrantInput } from './warrant/index.js';
21
22
  export * from './capabilities.js';
22
23
  export { DEFAULT_LOCAL_NODE_PORTS, localNodeUrl, nodeEndpoint, probeNodeHealth, discoverLocalNodes, } from './nodeDiscovery.js';
package/dist/index.js CHANGED
@@ -10,8 +10,6 @@ export * from './auth-api/index.js';
10
10
  export * from './admin-api/index.js';
11
11
  // Auth utilities
12
12
  export { parseAuthCallback, buildAuthLoginUrl } from './auth/index.js';
13
- // Account id encoding (hex everywhere core spells one; an app may re-encode)
14
- export { toAccountHex, toAccountBase58, sameAccount, } from './account/index.js';
15
13
  // Token store
16
14
  export { MemoryTokenStore, LocalStorageTokenStore } from './token-store/index.js';
17
15
  // RPC client
@@ -24,6 +22,7 @@ export { EphemeralClient, jsonCodec } from './ephemeral/index.js';
24
22
  export * from './cloud/index.js';
25
23
  // Warrant signing — mint the author's consent for a relay to run one intent
26
24
  export { signWarrant, intentHash } from './warrant/index.js';
25
+ export { signDeviceCert, mintDeviceId, deviceCertPayload, accountForRoot, } from './device-cert/index.js';
27
26
  // Member capability bitmask constants & helpers
28
27
  export * from './capabilities.js';
29
28
  // Utilities
package/dist/index.mjs CHANGED
@@ -2441,93 +2441,6 @@ function createMeroJs(config) {
2441
2441
  return new MeroJs(config);
2442
2442
  }
2443
2443
 
2444
- // src/account/index.ts
2445
- var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
2446
- var HEX_ACCOUNT = /^[0-9a-f]{64}$/i;
2447
- function encodeBase58(bytes) {
2448
- if (bytes.length === 0) return "";
2449
- const digits = [0];
2450
- for (const byte of bytes) {
2451
- let carry = byte;
2452
- for (let i = 0; i < digits.length; i++) {
2453
- carry += digits[i] << 8;
2454
- digits[i] = carry % 58;
2455
- carry = carry / 58 | 0;
2456
- }
2457
- while (carry > 0) {
2458
- digits.push(carry % 58);
2459
- carry = carry / 58 | 0;
2460
- }
2461
- }
2462
- let out = "";
2463
- for (const byte of bytes) {
2464
- if (byte !== 0) break;
2465
- out += BASE58_ALPHABET[0];
2466
- }
2467
- for (let i = digits.length - 1; i >= 0; i--) {
2468
- out += BASE58_ALPHABET[digits[i]];
2469
- }
2470
- return out;
2471
- }
2472
- function decodeBase58(value) {
2473
- if (value.length === 0) return null;
2474
- const bytes = [0];
2475
- for (const char of value) {
2476
- const index = BASE58_ALPHABET.indexOf(char);
2477
- if (index < 0) return null;
2478
- let carry = index;
2479
- for (let i = 0; i < bytes.length; i++) {
2480
- carry += bytes[i] * 58;
2481
- bytes[i] = carry & 255;
2482
- carry >>= 8;
2483
- }
2484
- while (carry > 0) {
2485
- bytes.push(carry & 255);
2486
- carry >>= 8;
2487
- }
2488
- }
2489
- let leadingZeros = 0;
2490
- for (const char of value) {
2491
- if (char !== BASE58_ALPHABET[0]) break;
2492
- leadingZeros++;
2493
- }
2494
- return Uint8Array.from([
2495
- ...new Array(leadingZeros).fill(0),
2496
- ...bytes.reverse()
2497
- ]);
2498
- }
2499
- function toHex(bytes) {
2500
- let out = "";
2501
- for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
2502
- return out;
2503
- }
2504
- function fromHex(hex2) {
2505
- const bytes = new Uint8Array(hex2.length / 2);
2506
- for (let i = 0; i < bytes.length; i++) {
2507
- bytes[i] = parseInt(hex2.slice(i * 2, i * 2 + 2), 16);
2508
- }
2509
- return bytes;
2510
- }
2511
- function toAccountHex(id) {
2512
- const trimmed = (id ?? "").trim();
2513
- if (!trimmed) return "";
2514
- if (HEX_ACCOUNT.test(trimmed)) return trimmed.toLowerCase();
2515
- const decoded = decodeBase58(trimmed);
2516
- if (!decoded || decoded.length !== 32) return trimmed;
2517
- return toHex(decoded);
2518
- }
2519
- function toAccountBase58(id) {
2520
- const trimmed = (id ?? "").trim();
2521
- if (!trimmed) return "";
2522
- if (!HEX_ACCOUNT.test(trimmed)) return trimmed;
2523
- return encodeBase58(fromHex(trimmed));
2524
- }
2525
- function sameAccount(a, b) {
2526
- const left = toAccountHex(a);
2527
- const right = toAccountHex(b);
2528
- return HEX_ACCOUNT.test(left) && left === right;
2529
- }
2530
-
2531
2444
  // src/token-store/index.ts
2532
2445
  var MemoryTokenStore = class {
2533
2446
  constructor() {
@@ -2609,9 +2522,7 @@ var CloudClient = class {
2609
2522
  }
2610
2523
  };
2611
2524
 
2612
- // src/warrant/warrant.ts
2613
- var SIGN_DOMAIN = new TextEncoder().encode("calimero.warrant.v1");
2614
- var INTENT_DOMAIN = new TextEncoder().encode("calimero.warrant.intent.v1");
2525
+ // src/crypto/internal.ts
2615
2526
  var PKCS8_ED25519_PREFIX = new Uint8Array([
2616
2527
  48,
2617
2528
  46,
@@ -2630,10 +2541,15 @@ var PKCS8_ED25519_PREFIX = new Uint8Array([
2630
2541
  4,
2631
2542
  32
2632
2543
  ]);
2544
+ function u32le(value) {
2545
+ const out = new Uint8Array(4);
2546
+ new DataView(out.buffer).setUint32(0, value, true);
2547
+ return out;
2548
+ }
2633
2549
  function hex(bytes) {
2634
2550
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
2635
2551
  }
2636
- function fromHex2(value, label, expectedBytes) {
2552
+ function fromHex(value, label, expectedBytes) {
2637
2553
  const clean = value.trim();
2638
2554
  if (!/^[0-9a-fA-F]*$/.test(clean) || clean.length !== expectedBytes * 2) {
2639
2555
  throw new Error(
@@ -2669,12 +2585,8 @@ async function domainHash(domain, parts) {
2669
2585
  await crypto.subtle.digest("SHA-256", concat(...chunks))
2670
2586
  );
2671
2587
  }
2672
- async function intentHash(method, argsJson) {
2673
- const args = new TextEncoder().encode(JSON.stringify(argsJson));
2674
- return domainHash(INTENT_DOMAIN, [new TextEncoder().encode(method), args]);
2675
- }
2676
2588
  async function importSigningKey(secretHex) {
2677
- const seed = fromHex2(secretHex, "deviceSecret", 32);
2589
+ const seed = fromHex(secretHex, "deviceSecret", 32);
2678
2590
  const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2679
2591
  try {
2680
2592
  return await crypto.subtle.importKey(
@@ -2691,10 +2603,35 @@ async function importSigningKey(secretHex) {
2691
2603
  );
2692
2604
  }
2693
2605
  }
2606
+ async function derivePublicKey(secretHex) {
2607
+ const seed = fromHex(secretHex, "deviceSecret", 32);
2608
+ const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2609
+ const key = await crypto.subtle.importKey(
2610
+ "pkcs8",
2611
+ pkcs8,
2612
+ { name: "Ed25519" },
2613
+ true,
2614
+ ["sign"]
2615
+ );
2616
+ const jwk = await crypto.subtle.exportKey("jwk", key);
2617
+ const raw = jwk.x;
2618
+ return Uint8Array.from(
2619
+ atob(raw.replace(/-/g, "+").replace(/_/g, "/")),
2620
+ (c) => c.charCodeAt(0)
2621
+ );
2622
+ }
2623
+
2624
+ // src/warrant/warrant.ts
2625
+ var SIGN_DOMAIN = new TextEncoder().encode("calimero.warrant.v1");
2626
+ var INTENT_DOMAIN = new TextEncoder().encode("calimero.warrant.intent.v1");
2627
+ async function intentHash(method, argsJson) {
2628
+ const args = new TextEncoder().encode(JSON.stringify(argsJson));
2629
+ return domainHash(INTENT_DOMAIN, [new TextEncoder().encode(method), args]);
2630
+ }
2694
2631
  async function signWarrant(input) {
2695
- const context = fromHex2(input.context, "context", 32);
2696
- const authorAccount = fromHex2(input.authorAccount, "authorAccount", 32);
2697
- const executor = fromHex2(input.executor, "executor", 32);
2632
+ const context = fromHex(input.context, "context", 32);
2633
+ const authorAccount = fromHex(input.authorAccount, "authorAccount", 32);
2634
+ const executor = fromHex(input.executor, "executor", 32);
2698
2635
  const key = await importSigningKey(input.deviceSecret);
2699
2636
  const publicKey = await derivePublicKey(input.deviceSecret);
2700
2637
  const commitment = await intentHash(input.method, input.argsJson);
@@ -2725,21 +2662,72 @@ async function signWarrant(input) {
2725
2662
  )
2726
2663
  );
2727
2664
  }
2728
- async function derivePublicKey(secretHex) {
2729
- const seed = fromHex2(secretHex, "deviceSecret", 32);
2730
- const pkcs8 = concat(PKCS8_ED25519_PREFIX, seed);
2731
- const key = await crypto.subtle.importKey(
2732
- "pkcs8",
2733
- pkcs8,
2734
- { name: "Ed25519" },
2735
- true,
2736
- ["sign"]
2665
+
2666
+ // src/device-cert/device-cert.ts
2667
+ var CERT_DOMAIN = new TextEncoder().encode("calimero.device.cert.v1");
2668
+ var ACCOUNT_ID_DOMAIN = new TextEncoder().encode("calimero.account.genesis.v1");
2669
+ var DEVICE_ID_DOMAIN = new TextEncoder().encode("calimero.device.id.v1");
2670
+ var ACCOUNT_GENESIS_VERSION = 1;
2671
+ async function mintDeviceId(account, nonce) {
2672
+ if (nonce.length !== 16) {
2673
+ throw new Error(`nonce must be 16 bytes, got ${nonce.length}`);
2674
+ }
2675
+ const accountBytes = fromHex(account, "account", 32);
2676
+ return hex(await domainHash(DEVICE_ID_DOMAIN, [accountBytes, nonce]));
2677
+ }
2678
+ async function deviceCertPayload(input) {
2679
+ return domainHash(CERT_DOMAIN, [
2680
+ fromHex(input.account, "account", 32),
2681
+ fromHex(input.device, "device", 32),
2682
+ fromHex(input.signPublicKey, "signPublicKey", 32),
2683
+ fromHex(input.kemPublicKey, "kemPublicKey", 32),
2684
+ u32le(input.keyEpoch),
2685
+ u32le(input.deviceEpoch)
2686
+ ]);
2687
+ }
2688
+ async function signDeviceCert(input) {
2689
+ const rootPublicKey = await derivePublicKey(input.rootSecret);
2690
+ const account = hex(
2691
+ await domainHash(ACCOUNT_ID_DOMAIN, [
2692
+ concat(new Uint8Array([ACCOUNT_GENESIS_VERSION]), rootPublicKey)
2693
+ ])
2737
2694
  );
2738
- const jwk = await crypto.subtle.exportKey("jwk", key);
2739
- const raw = jwk.x;
2740
- return Uint8Array.from(
2741
- atob(raw.replace(/-/g, "+").replace(/_/g, "/")),
2742
- (c) => c.charCodeAt(0)
2695
+ const keyEpoch = 0;
2696
+ const payload = await deviceCertPayload({
2697
+ account,
2698
+ device: input.device,
2699
+ signPublicKey: input.signPublicKey,
2700
+ kemPublicKey: input.kemPublicKey,
2701
+ keyEpoch,
2702
+ deviceEpoch: input.deviceEpoch
2703
+ });
2704
+ const key = await importSigningKey(input.rootSecret);
2705
+ const signature = new Uint8Array(
2706
+ await crypto.subtle.sign("Ed25519", key, payload)
2707
+ );
2708
+ const credential = concat(
2709
+ // AccountGenesis
2710
+ new Uint8Array([ACCOUNT_GENESIS_VERSION]),
2711
+ rootPublicKey,
2712
+ // chain: Vec<RootKeyHandoff>, empty
2713
+ u32le(0),
2714
+ // DeviceCert
2715
+ fromHex(account, "account", 32),
2716
+ fromHex(input.device, "device", 32),
2717
+ fromHex(input.signPublicKey, "signPublicKey", 32),
2718
+ fromHex(input.kemPublicKey, "kemPublicKey", 32),
2719
+ u32le(keyEpoch),
2720
+ u32le(input.deviceEpoch),
2721
+ signature
2722
+ );
2723
+ return hex(credential);
2724
+ }
2725
+ async function accountForRoot(rootSecret) {
2726
+ const rootPublicKey = await derivePublicKey(rootSecret);
2727
+ return hex(
2728
+ await domainHash(ACCOUNT_ID_DOMAIN, [
2729
+ concat(new Uint8Array([ACCOUNT_GENESIS_VERSION]), rootPublicKey)
2730
+ ])
2743
2731
  );
2744
2732
  }
2745
2733
 
@@ -2835,6 +2823,7 @@ export {
2835
2823
  SseClient,
2836
2824
  WebHttpClient,
2837
2825
  WsClient,
2826
+ accountForRoot,
2838
2827
  buildAuthLoginUrl,
2839
2828
  combineSignals,
2840
2829
  compareSemver,
@@ -2853,18 +2842,18 @@ export {
2853
2842
  createRetryableMethod,
2854
2843
  createTimeoutSignal,
2855
2844
  createUniversalHttpClient,
2845
+ deviceCertPayload,
2856
2846
  discoverLocalNodes,
2857
2847
  hasCap,
2858
2848
  intentHash,
2859
2849
  jsonCodec,
2860
2850
  localNodeUrl,
2851
+ mintDeviceId,
2861
2852
  nodeEndpoint,
2862
2853
  parseAuthCallback,
2863
2854
  probeNodeHealth,
2864
- sameAccount,
2855
+ signDeviceCert,
2865
2856
  signWarrant,
2866
- toAccountBase58,
2867
- toAccountHex,
2868
2857
  withCap,
2869
2858
  withRetry,
2870
2859
  withoutCap