@totemsdk/identity 0.1.0 → 0.1.1
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/LICENSE +21 -0
- package/README.md +306 -0
- package/dist/canonical.js +6 -2
- package/dist/claims.js +15 -9
- package/dist/constants.js +4 -1
- package/dist/document.js +12 -8
- package/dist/guards.js +12 -5
- package/dist/index.js +32 -10
- package/dist/manifest-binding.js +11 -7
- package/dist/resolver.js +6 -3
- package/dist/revocation.js +6 -3
- package/dist/rotation.js +6 -3
- package/dist/signing.js +16 -12
- package/dist/types.js +2 -1
- package/dist/verify.js +13 -10
- package/package.json +29 -6
- package/src/__tests__/identity.test.ts +0 -618
- package/src/canonical.ts +0 -27
- package/src/claims.ts +0 -108
- package/src/constants.ts +0 -1
- package/src/document.ts +0 -35
- package/src/guards.ts +0 -75
- package/src/index.ts +0 -55
- package/src/manifest-binding.ts +0 -163
- package/src/resolver.ts +0 -171
- package/src/revocation.ts +0 -25
- package/src/rotation.ts +0 -23
- package/src/signing.ts +0 -38
- package/src/types.ts +0 -147
- package/src/verify.ts +0 -90
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Totem SDK Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# @totemsdk/identity
|
|
2
|
+
|
|
3
|
+
Canonical identity and claims layer for Totem Edge — who controls a manifest, device, or agent.
|
|
4
|
+
|
|
5
|
+
No network. No DHT. No blockchain submission. Pure schema + cryptographic binding.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @totemsdk/identity
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
`@totemsdk/identity` ties together manifests, device identifiers, and agent credentials into a single coherent identity model. It provides:
|
|
16
|
+
|
|
17
|
+
- **Identity documents** — a `TotemIdentityDocument` naming a root Minima address and a current controller address
|
|
18
|
+
- **Claims** — typed assertions linking identities (delegation, payment routing, service endpoints, rotation, revocation)
|
|
19
|
+
- **Sign / verify** — WOTS-signed claim envelopes with self-contained verification
|
|
20
|
+
- **Graph resolution** — walk a set of signed claims to produce a `ResolvedIdentity` showing active delegates, payment recipients, service endpoints, and rotation/revocation status
|
|
21
|
+
- **Manifest binding** — cryptographically bind a `SignedManifest` to an identity document
|
|
22
|
+
|
|
23
|
+
## All exports
|
|
24
|
+
|
|
25
|
+
### Document
|
|
26
|
+
|
|
27
|
+
| Export | Description |
|
|
28
|
+
|--------|-------------|
|
|
29
|
+
| `createIdentityDocument(opts)` | Create a new `TotemIdentityDocument` |
|
|
30
|
+
| `computeIdentityId(kind, rootAddress)` | SHA3-256 over stable identity fields — returns `totem:id:<kind>:<hash>` URI |
|
|
31
|
+
|
|
32
|
+
### Claims
|
|
33
|
+
|
|
34
|
+
| Export | Description |
|
|
35
|
+
|--------|-------------|
|
|
36
|
+
| `createIdentityClaim(opts)` | Create a generic `IdentityClaim` |
|
|
37
|
+
| `createDelegationClaim(opts)` | `delegates_to` claim — authorize another address |
|
|
38
|
+
| `createPaymentRecipientClaim(opts)` | `payment_recipient` claim — register a payment address |
|
|
39
|
+
| `createServiceEndpointClaim(opts)` | `service_endpoint` claim — register a service URI |
|
|
40
|
+
|
|
41
|
+
### Sign / Verify
|
|
42
|
+
|
|
43
|
+
| Export | Description |
|
|
44
|
+
|--------|-------------|
|
|
45
|
+
| `signIdentityClaim(claim, seed, keyIndex)` | WOTS-sign a claim; returns `SignedIdentityClaim` |
|
|
46
|
+
| `verifyIdentityClaim(signed)` | Verify the WOTS signature; returns `IdentityVerifyResult` |
|
|
47
|
+
|
|
48
|
+
### Rotation and Revocation
|
|
49
|
+
|
|
50
|
+
| Export | Description |
|
|
51
|
+
|--------|-------------|
|
|
52
|
+
| `rotateIdentity(doc, newAddress, seed, keyIndex)` | Issue a signed `rotates_to` claim |
|
|
53
|
+
| `revokeIdentity(doc, reason, seed, keyIndex)` | Issue a signed `revokes` claim |
|
|
54
|
+
|
|
55
|
+
### Resolution
|
|
56
|
+
|
|
57
|
+
| Export | Description |
|
|
58
|
+
|--------|-------------|
|
|
59
|
+
| `resolveIdentityGraph(graph)` | Walk an `IdentityGraph` to produce `IdentityResolutionResult` |
|
|
60
|
+
|
|
61
|
+
### Manifest Binding
|
|
62
|
+
|
|
63
|
+
| Export | Description |
|
|
64
|
+
|--------|-------------|
|
|
65
|
+
| `bindManifestToIdentity(signedManifest, graph)` | Verify a `SignedManifest` against an `IdentityGraph`; returns `ManifestIdentityBinding` |
|
|
66
|
+
| `verifyManifestIdentity(signedManifest, graph)` | Core verification logic (called internally by `bindManifestToIdentity`) |
|
|
67
|
+
|
|
68
|
+
### Type Guards
|
|
69
|
+
|
|
70
|
+
| Export | Description |
|
|
71
|
+
|--------|-------------|
|
|
72
|
+
| `isTotemIdentityDocument(x)` | Narrows to `TotemIdentityDocument` |
|
|
73
|
+
| `isIdentityClaim(x)` | Narrows to `IdentityClaim` |
|
|
74
|
+
| `isSignedIdentityClaim(x)` | Narrows to `SignedIdentityClaim` |
|
|
75
|
+
| `isRotationClaim(x)` | Narrows to `RotationClaim` |
|
|
76
|
+
| `isRevocationClaim(x)` | Narrows to `RevocationClaim` |
|
|
77
|
+
|
|
78
|
+
## Type reference
|
|
79
|
+
|
|
80
|
+
### `TotemIdentityDocument`
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
interface TotemIdentityDocument {
|
|
84
|
+
id: string; // computeIdentityId(kind, rootAddress) — totem:id:... URI
|
|
85
|
+
kind: IdentityKind;
|
|
86
|
+
version: number;
|
|
87
|
+
rootAddress: string; // Minima address — permanent, never changes
|
|
88
|
+
controllerAddress: string; // Current active controlling address
|
|
89
|
+
createdAt: number; // Unix ms
|
|
90
|
+
metadata?: Record<string, unknown>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
type IdentityKind =
|
|
94
|
+
| 'person' | 'device' | 'agent' | 'service'
|
|
95
|
+
| 'organization' | 'sensor' | 'robot' | 'gateway';
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### `IdentityClaim`
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
interface IdentityClaim {
|
|
102
|
+
id: string;
|
|
103
|
+
type: IdentityClaimType;
|
|
104
|
+
issuer: string; // Minima address of the claim issuer
|
|
105
|
+
subject: string; // identityId this claim is about
|
|
106
|
+
object: string; // target address or identityId
|
|
107
|
+
issuedAt: number; // Unix ms
|
|
108
|
+
expiresAt?: number; // Unix ms — absent means no expiry
|
|
109
|
+
payload: Record<string, unknown>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
type IdentityClaimType =
|
|
113
|
+
| 'delegates_to'
|
|
114
|
+
| 'payment_recipient'
|
|
115
|
+
| 'service_endpoint'
|
|
116
|
+
| 'rotates_to'
|
|
117
|
+
| 'revokes';
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `SignedIdentityClaim`
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
interface SignedIdentityClaim {
|
|
124
|
+
claim: IdentityClaim;
|
|
125
|
+
proof: {
|
|
126
|
+
address: string; // Minima address that signed
|
|
127
|
+
publicKey: string; // Full WOTS public key hex
|
|
128
|
+
signature: string; // WOTS signature hex
|
|
129
|
+
message?: string; // Optional human-readable signing context
|
|
130
|
+
};
|
|
131
|
+
rootIdentityProof?: string;
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Concrete claim types
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
interface DelegationClaim {
|
|
139
|
+
claimId: string;
|
|
140
|
+
issuer: string;
|
|
141
|
+
subject: string;
|
|
142
|
+
delegatedAddress: string; // Hot-key address authorized to act on behalf of root
|
|
143
|
+
scopes: string[]; // e.g. ['manifest:sign', 'proof:create']
|
|
144
|
+
issuedAt: number;
|
|
145
|
+
expiresAt?: number;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
interface PaymentRecipientClaim {
|
|
149
|
+
claimId: string;
|
|
150
|
+
issuer: string;
|
|
151
|
+
address: string; // Minima address for receiving payments
|
|
152
|
+
label?: string;
|
|
153
|
+
issuedAt: number;
|
|
154
|
+
expiresAt?: number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
interface ServiceEndpointClaim {
|
|
158
|
+
claimId: string;
|
|
159
|
+
issuer: string;
|
|
160
|
+
endpointType: string; // e.g. 'mqtt', 'https', 'hyperswarm'
|
|
161
|
+
uri: string;
|
|
162
|
+
issuedAt: number;
|
|
163
|
+
expiresAt?: number;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
interface RotationClaim {
|
|
167
|
+
claimId: string;
|
|
168
|
+
issuer: string;
|
|
169
|
+
subject: string;
|
|
170
|
+
newAddress: string; // Address the identity has rotated to
|
|
171
|
+
issuedAt: number;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
interface RevocationClaim {
|
|
175
|
+
claimId: string;
|
|
176
|
+
issuer: string;
|
|
177
|
+
subject: string;
|
|
178
|
+
reason?: string;
|
|
179
|
+
issuedAt: number;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### `ResolvedIdentity`
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
interface ResolvedIdentity {
|
|
187
|
+
document: TotemIdentityDocument;
|
|
188
|
+
status: 'active' | 'rotated' | 'revoked';
|
|
189
|
+
rootAddress: string;
|
|
190
|
+
controllerAddress: string;
|
|
191
|
+
controlledAddresses: string[];
|
|
192
|
+
authorizedAddresses: string[];
|
|
193
|
+
delegates: DelegationClaim[];
|
|
194
|
+
paymentRecipients: PaymentRecipientClaim[];
|
|
195
|
+
serviceEndpoints: ServiceEndpointClaim[];
|
|
196
|
+
rotationTarget?: string;
|
|
197
|
+
revokedAt?: number;
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### `IdentityVerifyResult`
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
interface IdentityVerifyResult {
|
|
205
|
+
valid: boolean;
|
|
206
|
+
reason?: string;
|
|
207
|
+
signerAddress?: string;
|
|
208
|
+
rootAddress?: string;
|
|
209
|
+
provenAddresses?: string[];
|
|
210
|
+
metadata?: Record<string, unknown>;
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `ManifestIdentityBinding`
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
interface ManifestIdentityBinding {
|
|
218
|
+
valid: boolean;
|
|
219
|
+
reason?: string;
|
|
220
|
+
manifestId: string;
|
|
221
|
+
identityId: string;
|
|
222
|
+
signerAddress: string;
|
|
223
|
+
resolvedStatus: 'active' | 'rotated' | 'revoked';
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Usage: create → sign → resolve → bind
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
import {
|
|
231
|
+
createIdentityDocument,
|
|
232
|
+
computeIdentityId,
|
|
233
|
+
createDelegationClaim,
|
|
234
|
+
signIdentityClaim,
|
|
235
|
+
verifyIdentityClaim,
|
|
236
|
+
resolveIdentityGraph,
|
|
237
|
+
bindManifestToIdentity,
|
|
238
|
+
} from '@totemsdk/identity';
|
|
239
|
+
import { signManifest } from '@totemsdk/manifest';
|
|
240
|
+
|
|
241
|
+
// 1. Create identity document
|
|
242
|
+
const doc = createIdentityDocument({
|
|
243
|
+
kind: 'service',
|
|
244
|
+
rootAddress: 'MxABC123...',
|
|
245
|
+
controllerAddress: 'MxABC123...',
|
|
246
|
+
});
|
|
247
|
+
// computeIdentityId(kind, rootAddress) matches doc.id — use to derive it independently
|
|
248
|
+
console.log('Identity ID:', computeIdentityId('service', 'MxABC123...'));
|
|
249
|
+
|
|
250
|
+
// 2. Delegate a hot key
|
|
251
|
+
const delegationClaim = createDelegationClaim({
|
|
252
|
+
issuer: doc.rootAddress,
|
|
253
|
+
subject: doc.id,
|
|
254
|
+
delegatedAddress: 'MxHOT456...',
|
|
255
|
+
scopes: ['manifest:sign'],
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// seed = 32-byte WOTS seed; keyIndex = reserved via @totemsdk/wots-lease
|
|
259
|
+
const signedDelegation = await signIdentityClaim(delegationClaim, seed, keyIndex);
|
|
260
|
+
|
|
261
|
+
// 3. Verify immediately
|
|
262
|
+
const verifyResult = await verifyIdentityClaim(signedDelegation);
|
|
263
|
+
console.assert(verifyResult.valid, 'Claim signature invalid');
|
|
264
|
+
|
|
265
|
+
// 4. Resolve the full graph
|
|
266
|
+
const { resolved, errors } = resolveIdentityGraph({
|
|
267
|
+
document: doc,
|
|
268
|
+
claims: [signedDelegation],
|
|
269
|
+
});
|
|
270
|
+
console.log('Status:', resolved?.status); // 'active'
|
|
271
|
+
console.log('Delegates:', resolved?.delegates); // [DelegationClaim]
|
|
272
|
+
|
|
273
|
+
// 5. Bind a manifest — bindManifestToIdentity takes an IdentityGraph, not a document
|
|
274
|
+
const signedManifest = await signManifest(edgeServiceManifest, seed, keyIndex2);
|
|
275
|
+
const binding = await bindManifestToIdentity(signedManifest, {
|
|
276
|
+
document: doc,
|
|
277
|
+
claims: [signedDelegation],
|
|
278
|
+
});
|
|
279
|
+
console.assert(binding.valid, 'Manifest not bound to identity');
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Security notes
|
|
283
|
+
|
|
284
|
+
### Issuer verification
|
|
285
|
+
|
|
286
|
+
`verifyIdentityClaim` only checks that the WOTS signature is valid for `proof.address`. It does **not** check whether the issuer is trusted — that is the role of `resolveIdentityGraph`.
|
|
287
|
+
|
|
288
|
+
Always resolve the full graph before acting on claims:
|
|
289
|
+
- A claim where `issuer` is not the `rootAddress` or a currently authorized address is silently excluded by the resolver.
|
|
290
|
+
- `rotates_to` and `revokes` claims from unauthorized issuers are ignored.
|
|
291
|
+
- An attacker who signs a claim but sets `issuer = rootAddress` will be caught: the resolver checks that `proof.address` matches the declared `issuer`, not just that the signature is structurally valid.
|
|
292
|
+
|
|
293
|
+
### One-time WOTS keys
|
|
294
|
+
|
|
295
|
+
Each claim signature consumes one WOTS key index. Use `@totemsdk/wots-lease` to reserve indexes before calling `signIdentityClaim`. Reusing an index exposes the private key.
|
|
296
|
+
|
|
297
|
+
## Related packages
|
|
298
|
+
|
|
299
|
+
- [`@totemsdk/manifest`](https://www.npmjs.com/package/@totemsdk/manifest) — signed declaration format for Totem Edge entities
|
|
300
|
+
- [`@totemsdk/edge`](https://www.npmjs.com/package/@totemsdk/edge) — compose identity into an edge runtime
|
|
301
|
+
- [`@totemsdk/core`](https://www.npmjs.com/package/@totemsdk/core) — WOTS signing primitives used internally
|
|
302
|
+
- [`@totemsdk/root-identity`](https://www.npmjs.com/package/@totemsdk/root-identity) — multi-address root identity SDK
|
|
303
|
+
|
|
304
|
+
## License
|
|
305
|
+
|
|
306
|
+
MIT
|
package/dist/canonical.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toHex = toHex;
|
|
4
|
+
exports.canonicalJson = canonicalJson;
|
|
1
5
|
/**
|
|
2
6
|
* Plain hex encoder (no 0x prefix) for use in ID URIs and deterministic identifiers.
|
|
3
7
|
* bytesToHex from @totemsdk/core adds a 0x prefix — use this for URI-safe hex IDs.
|
|
4
8
|
*/
|
|
5
|
-
|
|
9
|
+
function toHex(bytes) {
|
|
6
10
|
return Array.from(bytes)
|
|
7
11
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
8
12
|
.join('');
|
|
@@ -12,7 +16,7 @@ export function toHex(bytes) {
|
|
|
12
16
|
* Recursively sorts object keys before serializing — used for hashing and signing.
|
|
13
17
|
* Never use bare JSON.stringify on objects passed to hash or sign operations.
|
|
14
18
|
*/
|
|
15
|
-
|
|
19
|
+
function canonicalJson(value) {
|
|
16
20
|
if (value === null || typeof value !== 'object') {
|
|
17
21
|
return JSON.stringify(value);
|
|
18
22
|
}
|
package/dist/claims.js
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Claim creation helpers.
|
|
3
4
|
*
|
|
4
5
|
* Claim IDs are deterministic: SHA3-256 of claim fields + canonical payload hash.
|
|
5
6
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.createIdentityClaim = createIdentityClaim;
|
|
9
|
+
exports.createDelegationClaim = createDelegationClaim;
|
|
10
|
+
exports.createPaymentRecipientClaim = createPaymentRecipientClaim;
|
|
11
|
+
exports.createServiceEndpointClaim = createServiceEndpointClaim;
|
|
12
|
+
const sha3_js_1 = require("@noble/hashes/sha3.js");
|
|
13
|
+
const canonical_js_1 = require("./canonical.js");
|
|
8
14
|
function computeClaimId(type, issuer, subject, object, issuedAt, payload) {
|
|
9
|
-
const canonical = canonicalJson({ type, issuer, subject, object, issuedAt, payload });
|
|
10
|
-
const hash = sha3_256(new TextEncoder().encode(canonical));
|
|
11
|
-
return toHex(hash);
|
|
15
|
+
const canonical = (0, canonical_js_1.canonicalJson)({ type, issuer, subject, object, issuedAt, payload });
|
|
16
|
+
const hash = (0, sha3_js_1.sha3_256)(new TextEncoder().encode(canonical));
|
|
17
|
+
return (0, canonical_js_1.toHex)(hash);
|
|
12
18
|
}
|
|
13
|
-
|
|
19
|
+
function createIdentityClaim(opts) {
|
|
14
20
|
const { type, issuer, subject, object, payload, expiresAt } = opts;
|
|
15
21
|
const issuedAt = opts.issuedAt ?? Date.now();
|
|
16
22
|
const id = computeClaimId(type, issuer, subject, object, issuedAt, payload);
|
|
@@ -25,7 +31,7 @@ export function createIdentityClaim(opts) {
|
|
|
25
31
|
payload,
|
|
26
32
|
};
|
|
27
33
|
}
|
|
28
|
-
|
|
34
|
+
function createDelegationClaim(opts) {
|
|
29
35
|
const { issuer, subject, delegatedAddress, scopes, expiresAt } = opts;
|
|
30
36
|
return createIdentityClaim({
|
|
31
37
|
type: 'delegates_to',
|
|
@@ -37,7 +43,7 @@ export function createDelegationClaim(opts) {
|
|
|
37
43
|
expiresAt,
|
|
38
44
|
});
|
|
39
45
|
}
|
|
40
|
-
|
|
46
|
+
function createPaymentRecipientClaim(opts) {
|
|
41
47
|
const { issuer, subject, address, label, expiresAt } = opts;
|
|
42
48
|
const payload = {};
|
|
43
49
|
if (label !== undefined)
|
|
@@ -52,7 +58,7 @@ export function createPaymentRecipientClaim(opts) {
|
|
|
52
58
|
expiresAt,
|
|
53
59
|
});
|
|
54
60
|
}
|
|
55
|
-
|
|
61
|
+
function createServiceEndpointClaim(opts) {
|
|
56
62
|
const { issuer, subject, endpointType, uri, expiresAt } = opts;
|
|
57
63
|
return createIdentityClaim({
|
|
58
64
|
type: 'service_endpoint',
|
package/dist/constants.js
CHANGED
package/dist/document.js
CHANGED
|
@@ -1,23 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Identity document creation and ID computation.
|
|
3
4
|
*
|
|
4
5
|
* computeIdentityId: deterministic SHA3-256 of "totem-identity" + kind + rootAddress.
|
|
5
6
|
* Version is NOT part of the ID hash — schema version upgrades must not change the identity ID.
|
|
6
7
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.computeIdentityId = computeIdentityId;
|
|
10
|
+
exports.createIdentityDocument = createIdentityDocument;
|
|
11
|
+
const sha3_js_1 = require("@noble/hashes/sha3.js");
|
|
12
|
+
const constants_js_1 = require("./constants.js");
|
|
13
|
+
const canonical_js_1 = require("./canonical.js");
|
|
14
|
+
function computeIdentityId(kind, rootAddress) {
|
|
11
15
|
const input = `totem-identity\0${kind}\0${rootAddress}`;
|
|
12
|
-
const hash = sha3_256(new TextEncoder().encode(input));
|
|
13
|
-
return `totem:id:${kind}:${toHex(hash)}`;
|
|
16
|
+
const hash = (0, sha3_js_1.sha3_256)(new TextEncoder().encode(input));
|
|
17
|
+
return `totem:id:${kind}:${(0, canonical_js_1.toHex)(hash)}`;
|
|
14
18
|
}
|
|
15
|
-
|
|
19
|
+
function createIdentityDocument(opts) {
|
|
16
20
|
const { kind, rootAddress, controllerAddress, metadata } = opts;
|
|
17
21
|
return {
|
|
18
22
|
id: computeIdentityId(kind, rootAddress),
|
|
19
23
|
kind,
|
|
20
|
-
version: IDENTITY_VERSION,
|
|
24
|
+
version: constants_js_1.IDENTITY_VERSION,
|
|
21
25
|
rootAddress,
|
|
22
26
|
controllerAddress,
|
|
23
27
|
createdAt: Date.now(),
|
package/dist/guards.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Type guards for identity types.
|
|
3
4
|
*/
|
|
4
|
-
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isTotemIdentityDocument = isTotemIdentityDocument;
|
|
7
|
+
exports.isIdentityClaim = isIdentityClaim;
|
|
8
|
+
exports.isSignedIdentityClaim = isSignedIdentityClaim;
|
|
9
|
+
exports.isRotationClaim = isRotationClaim;
|
|
10
|
+
exports.isRevocationClaim = isRevocationClaim;
|
|
11
|
+
function isTotemIdentityDocument(value) {
|
|
5
12
|
if (!value || typeof value !== 'object')
|
|
6
13
|
return false;
|
|
7
14
|
const v = value;
|
|
@@ -12,7 +19,7 @@ export function isTotemIdentityDocument(value) {
|
|
|
12
19
|
typeof v.controllerAddress === 'string' &&
|
|
13
20
|
typeof v.createdAt === 'number');
|
|
14
21
|
}
|
|
15
|
-
|
|
22
|
+
function isIdentityClaim(value) {
|
|
16
23
|
if (!value || typeof value !== 'object')
|
|
17
24
|
return false;
|
|
18
25
|
const v = value;
|
|
@@ -25,7 +32,7 @@ export function isIdentityClaim(value) {
|
|
|
25
32
|
typeof v.payload === 'object' &&
|
|
26
33
|
v.payload !== null);
|
|
27
34
|
}
|
|
28
|
-
|
|
35
|
+
function isSignedIdentityClaim(value) {
|
|
29
36
|
if (!value || typeof value !== 'object')
|
|
30
37
|
return false;
|
|
31
38
|
const v = value;
|
|
@@ -38,7 +45,7 @@ export function isSignedIdentityClaim(value) {
|
|
|
38
45
|
typeof p.publicKey === 'string' &&
|
|
39
46
|
typeof p.signature === 'string');
|
|
40
47
|
}
|
|
41
|
-
|
|
48
|
+
function isRotationClaim(value) {
|
|
42
49
|
if (!value || typeof value !== 'object')
|
|
43
50
|
return false;
|
|
44
51
|
const v = value;
|
|
@@ -48,7 +55,7 @@ export function isRotationClaim(value) {
|
|
|
48
55
|
typeof v.newAddress === 'string' &&
|
|
49
56
|
typeof v.issuedAt === 'number');
|
|
50
57
|
}
|
|
51
|
-
|
|
58
|
+
function isRevocationClaim(value) {
|
|
52
59
|
if (!value || typeof value !== 'object')
|
|
53
60
|
return false;
|
|
54
61
|
const v = value;
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* @module @totemsdk/identity
|
|
3
4
|
*
|
|
4
5
|
* Canonical identity and claims layer for Totem Edge.
|
|
5
6
|
* Pure package — no network, no DHT, no blockchain submission.
|
|
6
7
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.isRevocationClaim = exports.isRotationClaim = exports.isSignedIdentityClaim = exports.isIdentityClaim = exports.isTotemIdentityDocument = exports.verifyManifestIdentity = exports.bindManifestToIdentity = exports.resolveIdentityGraph = exports.revokeIdentity = exports.rotateIdentity = exports.verifyIdentityClaim = exports.signIdentityClaim = exports.createServiceEndpointClaim = exports.createPaymentRecipientClaim = exports.createDelegationClaim = exports.createIdentityClaim = exports.createIdentityDocument = exports.computeIdentityId = exports.IDENTITY_VERSION = void 0;
|
|
10
|
+
var constants_js_1 = require("./constants.js");
|
|
11
|
+
Object.defineProperty(exports, "IDENTITY_VERSION", { enumerable: true, get: function () { return constants_js_1.IDENTITY_VERSION; } });
|
|
12
|
+
var document_js_1 = require("./document.js");
|
|
13
|
+
Object.defineProperty(exports, "computeIdentityId", { enumerable: true, get: function () { return document_js_1.computeIdentityId; } });
|
|
14
|
+
Object.defineProperty(exports, "createIdentityDocument", { enumerable: true, get: function () { return document_js_1.createIdentityDocument; } });
|
|
15
|
+
var claims_js_1 = require("./claims.js");
|
|
16
|
+
Object.defineProperty(exports, "createIdentityClaim", { enumerable: true, get: function () { return claims_js_1.createIdentityClaim; } });
|
|
17
|
+
Object.defineProperty(exports, "createDelegationClaim", { enumerable: true, get: function () { return claims_js_1.createDelegationClaim; } });
|
|
18
|
+
Object.defineProperty(exports, "createPaymentRecipientClaim", { enumerable: true, get: function () { return claims_js_1.createPaymentRecipientClaim; } });
|
|
19
|
+
Object.defineProperty(exports, "createServiceEndpointClaim", { enumerable: true, get: function () { return claims_js_1.createServiceEndpointClaim; } });
|
|
20
|
+
var signing_js_1 = require("./signing.js");
|
|
21
|
+
Object.defineProperty(exports, "signIdentityClaim", { enumerable: true, get: function () { return signing_js_1.signIdentityClaim; } });
|
|
22
|
+
var verify_js_1 = require("./verify.js");
|
|
23
|
+
Object.defineProperty(exports, "verifyIdentityClaim", { enumerable: true, get: function () { return verify_js_1.verifyIdentityClaim; } });
|
|
24
|
+
var rotation_js_1 = require("./rotation.js");
|
|
25
|
+
Object.defineProperty(exports, "rotateIdentity", { enumerable: true, get: function () { return rotation_js_1.rotateIdentity; } });
|
|
26
|
+
var revocation_js_1 = require("./revocation.js");
|
|
27
|
+
Object.defineProperty(exports, "revokeIdentity", { enumerable: true, get: function () { return revocation_js_1.revokeIdentity; } });
|
|
28
|
+
var resolver_js_1 = require("./resolver.js");
|
|
29
|
+
Object.defineProperty(exports, "resolveIdentityGraph", { enumerable: true, get: function () { return resolver_js_1.resolveIdentityGraph; } });
|
|
30
|
+
var manifest_binding_js_1 = require("./manifest-binding.js");
|
|
31
|
+
Object.defineProperty(exports, "bindManifestToIdentity", { enumerable: true, get: function () { return manifest_binding_js_1.bindManifestToIdentity; } });
|
|
32
|
+
Object.defineProperty(exports, "verifyManifestIdentity", { enumerable: true, get: function () { return manifest_binding_js_1.verifyManifestIdentity; } });
|
|
33
|
+
var guards_js_1 = require("./guards.js");
|
|
34
|
+
Object.defineProperty(exports, "isTotemIdentityDocument", { enumerable: true, get: function () { return guards_js_1.isTotemIdentityDocument; } });
|
|
35
|
+
Object.defineProperty(exports, "isIdentityClaim", { enumerable: true, get: function () { return guards_js_1.isIdentityClaim; } });
|
|
36
|
+
Object.defineProperty(exports, "isSignedIdentityClaim", { enumerable: true, get: function () { return guards_js_1.isSignedIdentityClaim; } });
|
|
37
|
+
Object.defineProperty(exports, "isRotationClaim", { enumerable: true, get: function () { return guards_js_1.isRotationClaim; } });
|
|
38
|
+
Object.defineProperty(exports, "isRevocationClaim", { enumerable: true, get: function () { return guards_js_1.isRevocationClaim; } });
|
package/dist/manifest-binding.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Manifest identity binding.
|
|
3
4
|
*
|
|
@@ -21,13 +22,16 @@
|
|
|
21
22
|
* 4. Address membership check
|
|
22
23
|
* 5. Identity status check (revoked = invalid)
|
|
23
24
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.verifyManifestIdentity = verifyManifestIdentity;
|
|
27
|
+
exports.bindManifestToIdentity = bindManifestToIdentity;
|
|
28
|
+
const manifest_1 = require("@totemsdk/manifest");
|
|
29
|
+
const resolver_js_1 = require("./resolver.js");
|
|
30
|
+
async function verifyManifestIdentity(signedManifest, identityGraph, options) {
|
|
27
31
|
const proofVerifiers = options?.proofVerifiers ?? {};
|
|
28
|
-
const manifestId = computeManifestId(signedManifest.manifest);
|
|
32
|
+
const manifestId = (0, manifest_1.computeManifestId)(signedManifest.manifest);
|
|
29
33
|
// Step 1: verify manifest signature first — fail fast on invalid manifest
|
|
30
|
-
const manifestVerifyResult = verifyManifest(signedManifest);
|
|
34
|
+
const manifestVerifyResult = (0, manifest_1.verifyManifest)(signedManifest);
|
|
31
35
|
if (!manifestVerifyResult.valid) {
|
|
32
36
|
return {
|
|
33
37
|
valid: false,
|
|
@@ -39,7 +43,7 @@ export async function verifyManifestIdentity(signedManifest, identityGraph, opti
|
|
|
39
43
|
};
|
|
40
44
|
}
|
|
41
45
|
// Step 2: resolve identity graph (includes claim signature verification)
|
|
42
|
-
const resolution = resolveIdentityGraph(identityGraph);
|
|
46
|
+
const resolution = (0, resolver_js_1.resolveIdentityGraph)(identityGraph);
|
|
43
47
|
if (!resolution.resolved) {
|
|
44
48
|
return {
|
|
45
49
|
valid: false,
|
|
@@ -125,6 +129,6 @@ export async function verifyManifestIdentity(signedManifest, identityGraph, opti
|
|
|
125
129
|
resolvedStatus: resolved.status,
|
|
126
130
|
};
|
|
127
131
|
}
|
|
128
|
-
|
|
132
|
+
async function bindManifestToIdentity(signedManifest, identityGraph, options) {
|
|
129
133
|
return verifyManifestIdentity(signedManifest, identityGraph, options);
|
|
130
134
|
}
|
package/dist/resolver.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Local-only identity graph resolver.
|
|
3
4
|
*
|
|
@@ -17,13 +18,15 @@
|
|
|
17
18
|
* 3. An address holding an active + signature-verified delegates_to claim from the subject
|
|
18
19
|
* Claims from unauthorized issuers are silently dropped.
|
|
19
20
|
*/
|
|
20
|
-
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.resolveIdentityGraph = resolveIdentityGraph;
|
|
23
|
+
const verify_js_1 = require("./verify.js");
|
|
21
24
|
function isExpired(claim) {
|
|
22
25
|
if (claim.expiresAt === undefined)
|
|
23
26
|
return false;
|
|
24
27
|
return Date.now() > claim.expiresAt;
|
|
25
28
|
}
|
|
26
|
-
|
|
29
|
+
function resolveIdentityGraph(graph) {
|
|
27
30
|
const { document, claims } = graph;
|
|
28
31
|
const { rootAddress, controllerAddress } = document;
|
|
29
32
|
// Step 1: signature-verify all claims upfront and collect the valid ones.
|
|
@@ -34,7 +37,7 @@ export function resolveIdentityGraph(graph) {
|
|
|
34
37
|
// This prevents issuer-field spoofing: an attacker cannot set claim.issuer to a privileged
|
|
35
38
|
// address (root, controller, delegate) if they signed with their own key.
|
|
36
39
|
const verifiedClaims = claims.filter((sc) => {
|
|
37
|
-
const result = verifyIdentityClaim(sc);
|
|
40
|
+
const result = (0, verify_js_1.verifyIdentityClaim)(sc);
|
|
38
41
|
return result.valid && result.signerAddress === sc.claim.issuer;
|
|
39
42
|
});
|
|
40
43
|
// Step 2: collect delegation claims issued by root or controller only (first-level authority).
|
package/dist/revocation.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Identity revocation — produces a RevocationClaim.
|
|
3
4
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.revokeIdentity = revokeIdentity;
|
|
7
|
+
const claims_js_1 = require("./claims.js");
|
|
8
|
+
function revokeIdentity(opts) {
|
|
6
9
|
const { issuer, subject, reason } = opts;
|
|
7
10
|
const payload = {};
|
|
8
11
|
if (reason !== undefined)
|
|
9
12
|
payload.reason = reason;
|
|
10
|
-
return createIdentityClaim({
|
|
13
|
+
return (0, claims_js_1.createIdentityClaim)({
|
|
11
14
|
type: 'revokes',
|
|
12
15
|
issuer,
|
|
13
16
|
subject,
|
package/dist/rotation.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* Identity rotation — produces a RotationClaim.
|
|
3
4
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.rotateIdentity = rotateIdentity;
|
|
7
|
+
const claims_js_1 = require("./claims.js");
|
|
8
|
+
function rotateIdentity(opts) {
|
|
6
9
|
const { issuer, subject, newAddress } = opts;
|
|
7
|
-
return createIdentityClaim({
|
|
10
|
+
return (0, claims_js_1.createIdentityClaim)({
|
|
8
11
|
type: 'rotates_to',
|
|
9
12
|
issuer,
|
|
10
13
|
subject,
|