@calimero-network/mero-js 29.3.0 → 29.4.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/README.md +1 -1
- package/dist/cloud-v2/client.d.ts +52 -1
- package/dist/cloud-v2/client.js +45 -2
- package/dist/cloud-v2/index.d.ts +2 -1
- package/dist/cloud-v2/index.js +1 -0
- package/dist/cloud-v2/recovery.d.ts +23 -0
- package/dist/cloud-v2/recovery.js +70 -0
- package/dist/index.browser.mjs +3 -3
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +122 -7
- package/dist/index.cjs.map +4 -4
- package/dist/index.mjs +122 -7
- package/dist/index.mjs.map +4 -4
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -135,20 +135,20 @@ async function x25519KeyPair(secret) {
|
|
|
135
135
|
const publicKey = new Uint8Array(await crypto.subtle.exportKey("raw", pair.publicKey));
|
|
136
136
|
return { privateKey: pair.privateKey, publicKey };
|
|
137
137
|
}
|
|
138
|
-
const
|
|
138
|
+
const privateKey2 = await crypto.subtle.importKey(
|
|
139
139
|
"pkcs8",
|
|
140
140
|
concat(PKCS8_X25519_PREFIX, secret),
|
|
141
141
|
{ name: "X25519" },
|
|
142
142
|
true,
|
|
143
143
|
["deriveBits"]
|
|
144
144
|
);
|
|
145
|
-
const jwk = await crypto.subtle.exportKey("jwk",
|
|
146
|
-
return { privateKey, publicKey: base64UrlDecode(jwk.x) };
|
|
145
|
+
const jwk = await crypto.subtle.exportKey("jwk", privateKey2);
|
|
146
|
+
return { privateKey: privateKey2, publicKey: base64UrlDecode(jwk.x) };
|
|
147
147
|
}
|
|
148
|
-
async function dh(
|
|
148
|
+
async function dh(privateKey2, publicKey) {
|
|
149
149
|
const peer = await crypto.subtle.importKey("raw", publicKey, { name: "X25519" }, false, []);
|
|
150
150
|
return new Uint8Array(
|
|
151
|
-
await crypto.subtle.deriveBits({ name: "X25519", public: peer },
|
|
151
|
+
await crypto.subtle.deriveBits({ name: "X25519", public: peer }, privateKey2, 256)
|
|
152
152
|
);
|
|
153
153
|
}
|
|
154
154
|
function noiseNonce(n) {
|
|
@@ -1585,6 +1585,72 @@ function browserPasskey() {
|
|
|
1585
1585
|
};
|
|
1586
1586
|
}
|
|
1587
1587
|
|
|
1588
|
+
// src/cloud-v2/recovery.ts
|
|
1589
|
+
var RECOVERY_DOMAIN = "calimero.org-recovery.v1\0";
|
|
1590
|
+
var MAX_RECOVERY_NONCE_BYTES = 64;
|
|
1591
|
+
var PKCS8_ED25519 = Uint8Array.from([
|
|
1592
|
+
48,
|
|
1593
|
+
46,
|
|
1594
|
+
2,
|
|
1595
|
+
1,
|
|
1596
|
+
0,
|
|
1597
|
+
48,
|
|
1598
|
+
5,
|
|
1599
|
+
6,
|
|
1600
|
+
3,
|
|
1601
|
+
43,
|
|
1602
|
+
101,
|
|
1603
|
+
112,
|
|
1604
|
+
4,
|
|
1605
|
+
34,
|
|
1606
|
+
4,
|
|
1607
|
+
32
|
|
1608
|
+
]);
|
|
1609
|
+
function newRecoverySeed() {
|
|
1610
|
+
return crypto.getRandomValues(new Uint8Array(32));
|
|
1611
|
+
}
|
|
1612
|
+
function recoverySeedText(seed) {
|
|
1613
|
+
checkSeed(seed);
|
|
1614
|
+
const hex2 = Array.from(seed, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
1615
|
+
const groups = [];
|
|
1616
|
+
for (let i = 0; i < hex2.length; i += 8) groups.push(hex2.slice(i, i + 8));
|
|
1617
|
+
return groups.join("-");
|
|
1618
|
+
}
|
|
1619
|
+
function parseRecoverySeed(text) {
|
|
1620
|
+
const digits = text.replace(/[-\s]/g, "");
|
|
1621
|
+
if (!/^[0-9a-fA-F]{64}$/.test(digits)) throw new Error("a recovery seed is 64 hex digits");
|
|
1622
|
+
return Uint8Array.from({ length: 32 }, (_, i) => parseInt(digits.slice(2 * i, 2 * i + 2), 16));
|
|
1623
|
+
}
|
|
1624
|
+
function recoveryMessage(org, account, nonce2) {
|
|
1625
|
+
return new TextEncoder().encode(`${RECOVERY_DOMAIN}${org}\0${account}\0${nonce2}`);
|
|
1626
|
+
}
|
|
1627
|
+
async function recoveryPublicKey(seed) {
|
|
1628
|
+
const key = await privateKey(seed, true);
|
|
1629
|
+
const jwk = await crypto.subtle.exportKey("jwk", key);
|
|
1630
|
+
if (!jwk.x) throw new Error("this runtime cannot export an Ed25519 public key");
|
|
1631
|
+
return jwk.x;
|
|
1632
|
+
}
|
|
1633
|
+
async function signRecovery(seed, org, account, nonce2) {
|
|
1634
|
+
const nonceBytes = new TextEncoder().encode(nonce2).length;
|
|
1635
|
+
if (nonceBytes === 0 || nonceBytes > MAX_RECOVERY_NONCE_BYTES) {
|
|
1636
|
+
throw new Error(`a recovery nonce is 1 to ${MAX_RECOVERY_NONCE_BYTES} bytes`);
|
|
1637
|
+
}
|
|
1638
|
+
const key = await privateKey(seed, false);
|
|
1639
|
+
const sig = await crypto.subtle.sign("Ed25519", key, buf2(recoveryMessage(org, account, nonce2)));
|
|
1640
|
+
return b64url(new Uint8Array(sig));
|
|
1641
|
+
}
|
|
1642
|
+
function checkSeed(seed) {
|
|
1643
|
+
if (seed.length !== 32) throw new Error("a recovery seed is 32 bytes");
|
|
1644
|
+
}
|
|
1645
|
+
function privateKey(seed, extractable) {
|
|
1646
|
+
checkSeed(seed);
|
|
1647
|
+
const pkcs8 = new Uint8Array(PKCS8_ED25519.length + 32);
|
|
1648
|
+
pkcs8.set(PKCS8_ED25519);
|
|
1649
|
+
pkcs8.set(seed, PKCS8_ED25519.length);
|
|
1650
|
+
return crypto.subtle.importKey("pkcs8", buf2(pkcs8), { name: "Ed25519" }, extractable, ["sign"]);
|
|
1651
|
+
}
|
|
1652
|
+
var buf2 = (b) => b.buffer.slice(b.byteOffset, b.byteOffset + b.byteLength);
|
|
1653
|
+
|
|
1588
1654
|
// src/cloud-v2/approvals.ts
|
|
1589
1655
|
var MAX_REPORT_PAGE = 100;
|
|
1590
1656
|
function pageQuery(page = {}) {
|
|
@@ -2536,10 +2602,51 @@ var CalimeroClient = class _CalimeroClient {
|
|
|
2536
2602
|
await this.delete(`auth/aliases/${seg(alias)}`);
|
|
2537
2603
|
}
|
|
2538
2604
|
// ---------------------------------------------------------------- organizations
|
|
2539
|
-
|
|
2540
|
-
|
|
2605
|
+
/**
|
|
2606
|
+
* Create an organization. `recoveryKey` (from {@link recoveryPublicKey}) lets
|
|
2607
|
+
* whoever holds its seed recover the organization if its owner loses every
|
|
2608
|
+
* passkey; keep the seed offline ({@link recoverySeedText}).
|
|
2609
|
+
*/
|
|
2610
|
+
async createOrg(name, options = {}) {
|
|
2611
|
+
const body = { name };
|
|
2612
|
+
if (options.recoveryKey !== void 0) body.recoveryKey = options.recoveryKey;
|
|
2613
|
+
const { orgId } = await this.post("orgs", body);
|
|
2541
2614
|
return orgId;
|
|
2542
2615
|
}
|
|
2616
|
+
/**
|
|
2617
|
+
* Set or rotate (`publicKey`, from {@link recoveryPublicKey}) or remove
|
|
2618
|
+
* (`null`) the organization's recovery key: its owner only, approved with
|
|
2619
|
+
* `passkey`. A recovery under way is cancelled, and the old seed's
|
|
2620
|
+
* signatures are refused from then on.
|
|
2621
|
+
*/
|
|
2622
|
+
async setRecoveryKey(org, publicKey, passkey) {
|
|
2623
|
+
await this.postConfirmed(`orgs/${seg(org)}/recovery`, { publicKey }, passkey);
|
|
2624
|
+
}
|
|
2625
|
+
/**
|
|
2626
|
+
* Start handing `org` to the signed-in account with the recovery seed,
|
|
2627
|
+
* which signs here and never leaves this process. Finish with
|
|
2628
|
+
* {@link finishRecovery} once `readyAt` has passed; until then the owner may
|
|
2629
|
+
* cancel. A wrong seed (or no organization, or none set) is a `403`.
|
|
2630
|
+
*/
|
|
2631
|
+
async startRecovery(org, seed) {
|
|
2632
|
+
const account = (await this.me()).accountId;
|
|
2633
|
+
const nonce2 = b64url(crypto.getRandomValues(new Uint8Array(16)));
|
|
2634
|
+
const signature = await signRecovery(seed, org, account, nonce2);
|
|
2635
|
+
return this.post(`orgs/${seg(org)}/recovery/start`, { nonce: nonce2, signature });
|
|
2636
|
+
}
|
|
2637
|
+
/** Cancel the recovery under way: the organization's owner, approved with `passkey`. */
|
|
2638
|
+
async cancelRecovery(org, passkey) {
|
|
2639
|
+
await this.postConfirmed(`orgs/${seg(org)}/recovery/cancel`, {}, passkey);
|
|
2640
|
+
}
|
|
2641
|
+
/**
|
|
2642
|
+
* Finish a recovery this account started, once its delay passed
|
|
2643
|
+
* (`409 RECOVERY_NOT_READY` before, with `readyAt`), approved with this
|
|
2644
|
+
* account's `passkey`. The account becomes the owner; the previous owner
|
|
2645
|
+
* leaves the organization.
|
|
2646
|
+
*/
|
|
2647
|
+
finishRecovery(org, passkey) {
|
|
2648
|
+
return this.postConfirmed(`orgs/${seg(org)}/recovery/finish`, {}, passkey);
|
|
2649
|
+
}
|
|
2543
2650
|
/** The organization, with the teams this caller reaches or manages. */
|
|
2544
2651
|
getOrg(org) {
|
|
2545
2652
|
return this.get(`orgs/${seg(org)}`);
|
|
@@ -3386,6 +3493,7 @@ export {
|
|
|
3386
3493
|
IdempotencyMismatch,
|
|
3387
3494
|
KMS_RELEASE_SIGNER,
|
|
3388
3495
|
LoginRequired,
|
|
3496
|
+
MAX_RECOVERY_NONCE_BYTES,
|
|
3389
3497
|
MAX_REPORT_PAGE,
|
|
3390
3498
|
MOCK_QUOTE_HEADER,
|
|
3391
3499
|
NODE_RELEASE_SIGNER,
|
|
@@ -3395,6 +3503,7 @@ export {
|
|
|
3395
3503
|
OriginTaken,
|
|
3396
3504
|
PRODUCTION_PROFILE,
|
|
3397
3505
|
QuotaExceeded,
|
|
3506
|
+
RECOVERY_DOMAIN,
|
|
3398
3507
|
RELEASE_VERIFIER,
|
|
3399
3508
|
ReleaseDeadlineError,
|
|
3400
3509
|
SEALED_CONTENT_TYPE,
|
|
@@ -3428,18 +3537,24 @@ export {
|
|
|
3428
3537
|
fromB64url,
|
|
3429
3538
|
frontendManifest,
|
|
3430
3539
|
generateOwnerKey,
|
|
3540
|
+
newRecoverySeed,
|
|
3431
3541
|
orgCapabilities,
|
|
3432
3542
|
ownerPublicKey,
|
|
3433
3543
|
pageDeployment,
|
|
3434
3544
|
pageQuery,
|
|
3545
|
+
parseRecoverySeed,
|
|
3435
3546
|
pendingApprovalsFrom,
|
|
3436
3547
|
provisionAgent,
|
|
3437
3548
|
provisionInfo,
|
|
3438
3549
|
publisherBindingBytes,
|
|
3439
3550
|
publisherBindingMessage,
|
|
3551
|
+
recoveryMessage,
|
|
3552
|
+
recoveryPublicKey,
|
|
3553
|
+
recoverySeedText,
|
|
3440
3554
|
releaseAgent,
|
|
3441
3555
|
releaseConsent,
|
|
3442
3556
|
releaseNotesUrl,
|
|
3557
|
+
signRecovery,
|
|
3443
3558
|
stalledBy,
|
|
3444
3559
|
transportKeyBinding,
|
|
3445
3560
|
trustSignedRelease,
|