@calimero-network/mero-js 22.8.0 → 22.9.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/admin-api/admin-client.d.ts +17 -1
- package/dist/admin-api/admin-client.js +18 -0
- package/dist/admin-api/admin-types.d.ts +34 -0
- package/dist/device-cert/device-cert.d.ts +11 -0
- package/dist/device-cert/device-cert.js +1 -2
- package/dist/device-cert/device-scope.d.ts +58 -0
- package/dist/device-cert/device-scope.js +76 -0
- package/dist/device-cert/index.d.ts +2 -0
- package/dist/device-cert/index.js +1 -0
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +177 -19
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -0
- package/dist/index.mjs +177 -19
- package/dist/index.mjs.map +4 -4
- package/dist/invitation/index.d.ts +2 -0
- package/dist/invitation/index.js +2 -0
- package/dist/invitation/invitation.d.ts +106 -0
- package/dist/invitation/invitation.js +104 -0
- package/dist/namespace-op/namespace-op.js +4 -14
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -28,6 +28,10 @@ export { generateAccountRoot, accountRootFromPhrase, accountRootFromSecret, crea
|
|
|
28
28
|
export type { AccountRoot, RecoverableAccountRoot, AccountRootSigner, NewAccountRootSigner, AccountLinkInput, AccountLoginInput, RootSource, ResolvedRoot, } from "./account/index.js";
|
|
29
29
|
export { signDeviceCert, mintDeviceId, deviceCertPayload, accountForRoot, accountForRootPublicKey, parseDeviceCredential, verifyDeviceCredential, } from "./device-cert/index.js";
|
|
30
30
|
export type { DeviceCertInput, DeviceCredential } from "./device-cert/index.js";
|
|
31
|
+
export { signDeviceScope, deviceScopePayload } from "./device-cert/index.js";
|
|
32
|
+
export type { DeviceScopeInput } from "./device-cert/index.js";
|
|
33
|
+
export { signGroupInvitation, encodeGroupInvitation, groupInvitationHash, defaultAdmitters, MAX_INVITATION_VALIDITY_SECS, INVITED_ROLE, } from "./invitation/index.js";
|
|
34
|
+
export type { GroupInvitationInput } from "./invitation/index.js";
|
|
31
35
|
export { signMemberJoinOp, encodeSignedInvitation, SIGNED_NAMESPACE_OP_SCHEMA_VERSION, } from "./namespace-op/index.js";
|
|
32
36
|
export type { SignMemberJoinInput } from "./namespace-op/index.js";
|
|
33
37
|
export type { WarrantInput, CreationWarrantInput, CreationWarrantFields, SignedCreationWarrant, GovernanceWarrantInput, GovernanceWarrantFields, GovernanceOp, GovernanceOpKind, GovernanceMemberRole, GroupCreatedInput, NamespaceCreatedInput, } from "./warrant/index.js";
|
package/dist/index.js
CHANGED
|
@@ -46,6 +46,11 @@ export { generateAccountRoot, accountRootFromPhrase, accountRootFromSecret, crea
|
|
|
46
46
|
// name it in a signature only by re-declaring it.
|
|
47
47
|
resolveRoot, resolveRootPair, } from "./account/index.js";
|
|
48
48
|
export { signDeviceCert, mintDeviceId, deviceCertPayload, accountForRoot, accountForRootPublicKey, parseDeviceCredential, verifyDeviceCredential, } from "./device-cert/index.js";
|
|
49
|
+
// A device's scope — signed by the root beside its certificate, and what a relay
|
|
50
|
+
// needs with it to bind the device of an account that has no node.
|
|
51
|
+
export { signDeviceScope, deviceScopePayload } from "./device-cert/index.js";
|
|
52
|
+
// Invitations minted off-node, signed by a bound device key.
|
|
53
|
+
export { signGroupInvitation, encodeGroupInvitation, groupInvitationHash, defaultAdmitters, MAX_INVITATION_VALIDITY_SECS, INVITED_ROLE, } from "./invitation/index.js";
|
|
49
54
|
export { signMemberJoinOp, encodeSignedInvitation, SIGNED_NAMESPACE_OP_SCHEMA_VERSION, } from "./namespace-op/index.js";
|
|
50
55
|
// Login-statement signing — the device's half of a password-free session
|
|
51
56
|
export { requestBodyHash, signRequest, callerProof, createProofSigner, } from "./request/index.js";
|
package/dist/index.mjs
CHANGED
|
@@ -1810,6 +1810,29 @@ var AdminApiClient = class {
|
|
|
1810
1810
|
)
|
|
1811
1811
|
);
|
|
1812
1812
|
}
|
|
1813
|
+
/**
|
|
1814
|
+
* Carry the device link of an account **this node does not hold**, so the
|
|
1815
|
+
* namespace binds that device's key to the account.
|
|
1816
|
+
*
|
|
1817
|
+
* The relay half of a nodeless account minting invitations. An account added
|
|
1818
|
+
* to a group by account holds its grants but has no device bound, so peers
|
|
1819
|
+
* refuse whatever its device signs as "bound to no account". The credential
|
|
1820
|
+
* and scope are root-signed and self-certifying; this node adds its own
|
|
1821
|
+
* member endorsement and publishes the `AccountDeviceLinked`. After that the
|
|
1822
|
+
* device signs invitations itself — see `signGroupInvitation`.
|
|
1823
|
+
*
|
|
1824
|
+
* Refusals: `400` when a proof does not verify or the scope does not reach
|
|
1825
|
+
* this namespace (re-sign it), `403` when the device was revoked here or the
|
|
1826
|
+
* account is a member of nothing in the namespace (add it first).
|
|
1827
|
+
*/
|
|
1828
|
+
async linkAccountDevice(namespaceId, request) {
|
|
1829
|
+
return unwrap(
|
|
1830
|
+
await this.httpClient.post(
|
|
1831
|
+
`/admin-api/namespaces/${namespaceId}/account/link-device`,
|
|
1832
|
+
request
|
|
1833
|
+
)
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1813
1836
|
// ---- Group Management ----
|
|
1814
1837
|
async getGroupInfo(groupId) {
|
|
1815
1838
|
return unwrap(await this.httpClient.get(`/admin-api/groups/${groupId}`));
|
|
@@ -3495,6 +3518,54 @@ async function accountForRootPublicKey(rootPublicKey) {
|
|
|
3495
3518
|
);
|
|
3496
3519
|
}
|
|
3497
3520
|
|
|
3521
|
+
// src/device-cert/device-scope.ts
|
|
3522
|
+
var SCOPE_DOMAIN = new TextEncoder().encode("calimero.device.scope.v1");
|
|
3523
|
+
async function deviceScopePayload(input) {
|
|
3524
|
+
return domainHash(SCOPE_DOMAIN, [
|
|
3525
|
+
fromHex(input.account, "account", 32),
|
|
3526
|
+
fromHex(input.device, "device", 32),
|
|
3527
|
+
u32le(input.scopeEpoch),
|
|
3528
|
+
u32le(input.keyEpoch),
|
|
3529
|
+
...input.applications.map((a, i) => fromHex(a, `applications[${i}]`, 32))
|
|
3530
|
+
]);
|
|
3531
|
+
}
|
|
3532
|
+
async function signDeviceScope(input) {
|
|
3533
|
+
const signer = await resolveSigner(input.rootSecret, input.signer, "rootSecret");
|
|
3534
|
+
const rootPublicKey = fromHex(signer.publicKey, "signer.publicKey", 32);
|
|
3535
|
+
const account = await accountForRootPublicKey(hex(rootPublicKey));
|
|
3536
|
+
const applications = input.applications ?? [];
|
|
3537
|
+
const scopeEpoch = input.scopeEpoch ?? 0;
|
|
3538
|
+
if (!Number.isInteger(scopeEpoch) || scopeEpoch < 0 || scopeEpoch > 4294967295) {
|
|
3539
|
+
throw new Error(`scopeEpoch must be a u32, got ${scopeEpoch}`);
|
|
3540
|
+
}
|
|
3541
|
+
const keyEpoch = 0;
|
|
3542
|
+
const payload = await deviceScopePayload({
|
|
3543
|
+
account,
|
|
3544
|
+
device: input.device,
|
|
3545
|
+
applications,
|
|
3546
|
+
scopeEpoch,
|
|
3547
|
+
keyEpoch
|
|
3548
|
+
});
|
|
3549
|
+
const signature = await signer.sign(payload);
|
|
3550
|
+
if (signature.length !== 64) {
|
|
3551
|
+
throw new Error(`signer returned ${signature.length} bytes, expected a 64-byte Ed25519 signature`);
|
|
3552
|
+
}
|
|
3553
|
+
return hex(
|
|
3554
|
+
concat(
|
|
3555
|
+
new Uint8Array([ACCOUNT_GENESIS_VERSION]),
|
|
3556
|
+
rootPublicKey,
|
|
3557
|
+
u32le(0),
|
|
3558
|
+
fromHex(account, "account", 32),
|
|
3559
|
+
fromHex(input.device, "device", 32),
|
|
3560
|
+
u32le(applications.length),
|
|
3561
|
+
...applications.map((a, i) => fromHex(a, `applications[${i}]`, 32)),
|
|
3562
|
+
u32le(scopeEpoch),
|
|
3563
|
+
u32le(keyEpoch),
|
|
3564
|
+
signature
|
|
3565
|
+
)
|
|
3566
|
+
);
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3498
3569
|
// src/account/wordlist.ts
|
|
3499
3570
|
var ENGLISH_WORDLIST = [
|
|
3500
3571
|
"abandon",
|
|
@@ -10103,6 +10174,101 @@ function splitVersion2(version) {
|
|
|
10103
10174
|
return [[Number(match[1]), Number(match[2]), Number(match[3])], match[4]];
|
|
10104
10175
|
}
|
|
10105
10176
|
|
|
10177
|
+
// src/invitation/invitation.ts
|
|
10178
|
+
var MAX_INVITATION_VALIDITY_SECS = 24 * 60 * 60;
|
|
10179
|
+
var INVITED_ROLE = { Admin: 0, Member: 1, ReadOnly: 2 };
|
|
10180
|
+
function encodeGroupInvitation(body) {
|
|
10181
|
+
const admitters = body.admitters ?? [];
|
|
10182
|
+
return concat(
|
|
10183
|
+
// `SignerId` and `ContextGroupId` cross JSON as 32 numbers; `AccountId`
|
|
10184
|
+
// crosses as hex. Same width, different spelling.
|
|
10185
|
+
bytes32(body.inviter_identity, "inviter_identity"),
|
|
10186
|
+
bytes32(body.group_id, "group_id"),
|
|
10187
|
+
u64le(body.expiration_timestamp),
|
|
10188
|
+
bytes32(body.secret_salt, "secret_salt"),
|
|
10189
|
+
new Uint8Array([body.invited_role]),
|
|
10190
|
+
u32le(admitters.length),
|
|
10191
|
+
...admitters.map((a, i) => fromHex(a, `admitters[${i}]`, 32))
|
|
10192
|
+
);
|
|
10193
|
+
}
|
|
10194
|
+
async function groupInvitationHash(body) {
|
|
10195
|
+
return new Uint8Array(
|
|
10196
|
+
await crypto.subtle.digest("SHA-256", encodeGroupInvitation(body))
|
|
10197
|
+
);
|
|
10198
|
+
}
|
|
10199
|
+
function defaultAdmitters(members) {
|
|
10200
|
+
const admins = new Set(
|
|
10201
|
+
members.filter((m) => m.role === "Admin").map((m) => m.identity.toLowerCase())
|
|
10202
|
+
);
|
|
10203
|
+
return [...admins].sort();
|
|
10204
|
+
}
|
|
10205
|
+
async function signGroupInvitation(input) {
|
|
10206
|
+
const signer = await resolveSigner(input.deviceSecret, input.signer, "deviceSecret");
|
|
10207
|
+
const inviterKey = fromHex(signer.publicKey, "signer.publicKey", 32);
|
|
10208
|
+
const groupId = fromHex(input.groupId, "groupId", 32);
|
|
10209
|
+
fromHex(input.inviterAccount, "inviterAccount", 32);
|
|
10210
|
+
let admitters = [...input.admitters ?? []].map((a) => a.trim().toLowerCase());
|
|
10211
|
+
if (admitters.length === 0) {
|
|
10212
|
+
if (!input.members) {
|
|
10213
|
+
throw new Error(
|
|
10214
|
+
"name the admitters, or pass the group members to default them to its admins: an invitation with no admitters is claimable by broadcast"
|
|
10215
|
+
);
|
|
10216
|
+
}
|
|
10217
|
+
admitters = defaultAdmitters(input.members);
|
|
10218
|
+
if (admitters.length === 0) {
|
|
10219
|
+
throw new Error(
|
|
10220
|
+
"the member list names no admin, so there is nobody to default the admitters to"
|
|
10221
|
+
);
|
|
10222
|
+
}
|
|
10223
|
+
}
|
|
10224
|
+
admitters.forEach((a, i) => fromHex(a, `admitters[${i}]`, 32));
|
|
10225
|
+
const invitedRole = input.invitedRole ?? INVITED_ROLE.Member;
|
|
10226
|
+
if (![0, 1, 2].includes(invitedRole)) {
|
|
10227
|
+
throw new Error(`invitedRole must be 0 (Admin), 1 (Member) or 2 (ReadOnly), got ${invitedRole}`);
|
|
10228
|
+
}
|
|
10229
|
+
const now = input.now ?? Math.floor(Date.now() / 1e3);
|
|
10230
|
+
const validFor = Math.min(
|
|
10231
|
+
input.validForSecs ?? MAX_INVITATION_VALIDITY_SECS,
|
|
10232
|
+
MAX_INVITATION_VALIDITY_SECS
|
|
10233
|
+
);
|
|
10234
|
+
if (!Number.isInteger(validFor) || validFor <= 0) {
|
|
10235
|
+
throw new Error(`validForSecs must be a positive whole number, got ${input.validForSecs}`);
|
|
10236
|
+
}
|
|
10237
|
+
const nonce2 = input.nonce ?? crypto.getRandomValues(new Uint8Array(32));
|
|
10238
|
+
if (nonce2.length !== 32) {
|
|
10239
|
+
throw new Error(`nonce must be 32 bytes, got ${nonce2.length}`);
|
|
10240
|
+
}
|
|
10241
|
+
const invitation = {
|
|
10242
|
+
inviter_identity: Array.from(inviterKey),
|
|
10243
|
+
group_id: Array.from(groupId),
|
|
10244
|
+
expiration_timestamp: now + validFor,
|
|
10245
|
+
secret_salt: Array.from(nonce2),
|
|
10246
|
+
invited_role: invitedRole,
|
|
10247
|
+
admitters
|
|
10248
|
+
};
|
|
10249
|
+
const signature = await signer.sign(await groupInvitationHash(invitation));
|
|
10250
|
+
if (signature.length !== 64) {
|
|
10251
|
+
throw new Error(`signer returned ${signature.length} bytes, expected a 64-byte Ed25519 signature`);
|
|
10252
|
+
}
|
|
10253
|
+
const signed = {
|
|
10254
|
+
invitation,
|
|
10255
|
+
inviter_signature: hex(signature),
|
|
10256
|
+
inviter_account: input.inviterAccount.trim().toLowerCase()
|
|
10257
|
+
};
|
|
10258
|
+
if (input.admitterAddrs?.length) signed.admitter_addrs = [...input.admitterAddrs];
|
|
10259
|
+
if (input.applicationId) {
|
|
10260
|
+
signed.application_id = Array.from(fromHex(input.applicationId, "applicationId", 32));
|
|
10261
|
+
}
|
|
10262
|
+
if (input.appKey) signed.app_key = Array.from(fromHex(input.appKey, "appKey", 32));
|
|
10263
|
+
return signed;
|
|
10264
|
+
}
|
|
10265
|
+
function bytes32(value, label2) {
|
|
10266
|
+
if (value.length !== 32) {
|
|
10267
|
+
throw new Error(`${label2} must be 32 bytes, got ${value.length}`);
|
|
10268
|
+
}
|
|
10269
|
+
return new Uint8Array(value);
|
|
10270
|
+
}
|
|
10271
|
+
|
|
10106
10272
|
// src/namespace-op/namespace-op.ts
|
|
10107
10273
|
var SIGNED_NAMESPACE_OP_SCHEMA_VERSION = 16;
|
|
10108
10274
|
var NAMESPACE_SIGN_DOMAIN = new TextEncoder().encode("calimero.namespace.v1");
|
|
@@ -10120,12 +10286,6 @@ var ROOT_OP2 = {
|
|
|
10120
10286
|
NamespaceCreatedV2: 9,
|
|
10121
10287
|
MemberJoinedViaTeeAttestation: 10
|
|
10122
10288
|
};
|
|
10123
|
-
function bytes32(value, label2) {
|
|
10124
|
-
if (value.length !== 32) {
|
|
10125
|
-
throw new Error(`${label2} must be 32 bytes, got ${value.length}`);
|
|
10126
|
-
}
|
|
10127
|
-
return new Uint8Array(value);
|
|
10128
|
-
}
|
|
10129
10289
|
function optionalBytes32(value, label2) {
|
|
10130
10290
|
if (value === null || value === void 0) {
|
|
10131
10291
|
return new Uint8Array([0]);
|
|
@@ -10141,21 +10301,11 @@ function borshString2(value) {
|
|
|
10141
10301
|
}
|
|
10142
10302
|
function encodeSignedInvitation(signed) {
|
|
10143
10303
|
const body = signed.invitation;
|
|
10144
|
-
const admitters = body.admitters ?? [];
|
|
10145
10304
|
const admitterAddrs = signed.admitter_addrs ?? [];
|
|
10146
10305
|
return concat(
|
|
10147
|
-
// GroupInvitationFromAdmin
|
|
10148
|
-
//
|
|
10149
|
-
|
|
10150
|
-
// crosses as hex. Same width, different spelling, and reading either the
|
|
10151
|
-
// wrong way yields 32 bytes that encode cleanly and verify as nothing.
|
|
10152
|
-
bytes32(body.inviter_identity, "inviter_identity"),
|
|
10153
|
-
bytes32(body.group_id, "group_id"),
|
|
10154
|
-
u64le(body.expiration_timestamp),
|
|
10155
|
-
bytes32(body.secret_salt, "secret_salt"),
|
|
10156
|
-
new Uint8Array([body.invited_role]),
|
|
10157
|
-
u32le(admitters.length),
|
|
10158
|
-
...admitters.map((a, i) => fromHex(a, `admitters[${i}]`, 32)),
|
|
10306
|
+
// GroupInvitationFromAdmin, from the one encoder the SDK's own invitation
|
|
10307
|
+
// signer uses: the bytes signed and the bytes carried must not drift.
|
|
10308
|
+
encodeGroupInvitation(body),
|
|
10159
10309
|
// ...then the enclosing SignedGroupOpenInvitation, in declaration order.
|
|
10160
10310
|
// `inviter_account` sits between the signature and the addresses; putting it
|
|
10161
10311
|
// anywhere else still encodes 32 bytes and still verifies as nothing.
|
|
@@ -10370,8 +10520,10 @@ export {
|
|
|
10370
10520
|
EphemeralClient,
|
|
10371
10521
|
HANDSHAKE_PATH,
|
|
10372
10522
|
HTTPError,
|
|
10523
|
+
INVITED_ROLE,
|
|
10373
10524
|
IntentRefusedError,
|
|
10374
10525
|
LocalStorageTokenStore,
|
|
10526
|
+
MAX_INVITATION_VALIDITY_SECS,
|
|
10375
10527
|
MemoryTokenStore,
|
|
10376
10528
|
MeroClient,
|
|
10377
10529
|
MeroJs,
|
|
@@ -10445,11 +10597,14 @@ export {
|
|
|
10445
10597
|
createTimeoutSignal,
|
|
10446
10598
|
createUniversalHttpClient,
|
|
10447
10599
|
creationInitHash,
|
|
10600
|
+
defaultAdmitters,
|
|
10448
10601
|
defaultAudience,
|
|
10449
10602
|
defaultCapabilitiesSetOp,
|
|
10450
10603
|
deviceCertPayload,
|
|
10451
10604
|
deviceEnrolmentUrl,
|
|
10605
|
+
deviceScopePayload,
|
|
10452
10606
|
discoverLocalNodes,
|
|
10607
|
+
encodeGroupInvitation,
|
|
10453
10608
|
encodeSignedInvitation,
|
|
10454
10609
|
fetchAttestedTransportKey,
|
|
10455
10610
|
fetchNodeRelease,
|
|
@@ -10460,6 +10615,7 @@ export {
|
|
|
10460
10615
|
governanceOpHash,
|
|
10461
10616
|
groupCreatedOp,
|
|
10462
10617
|
groupDeletedOp,
|
|
10618
|
+
groupInvitationHash,
|
|
10463
10619
|
groupReparentedOp,
|
|
10464
10620
|
hasCap,
|
|
10465
10621
|
hexEncodeUtf8,
|
|
@@ -10495,7 +10651,9 @@ export {
|
|
|
10495
10651
|
signAccountLogin,
|
|
10496
10652
|
signCreationWarrant,
|
|
10497
10653
|
signDeviceCert,
|
|
10654
|
+
signDeviceScope,
|
|
10498
10655
|
signGovernanceWarrant,
|
|
10656
|
+
signGroupInvitation,
|
|
10499
10657
|
signLoginStatement,
|
|
10500
10658
|
signMemberJoinOp,
|
|
10501
10659
|
signRequest,
|