cojson 0.20.7 → 0.20.9
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +26 -0
- package/dist/SyncStateManager.d.ts.map +1 -1
- package/dist/SyncStateManager.js +0 -2
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/base64url.d.ts +15 -0
- package/dist/base64url.d.ts.map +1 -1
- package/dist/base64url.js +101 -5
- package/dist/base64url.js.map +1 -1
- package/dist/base64url.test.js +76 -1
- package/dist/base64url.test.js.map +1 -1
- package/dist/coValue.d.ts +2 -1
- package/dist/coValue.d.ts.map +1 -1
- package/dist/coValue.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +9 -11
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +92 -65
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts +38 -7
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +226 -30
- package/dist/coValueCore/verifiedState.js.map +1 -1
- package/dist/coValues/binaryCoStream.d.ts +63 -0
- package/dist/coValues/binaryCoStream.d.ts.map +1 -0
- package/dist/coValues/binaryCoStream.js +125 -0
- package/dist/coValues/binaryCoStream.js.map +1 -0
- package/dist/coValues/coList.d.ts +3 -1
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +15 -6
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +1 -1
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js +2 -2
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.d.ts +0 -38
- package/dist/coValues/coStream.d.ts.map +1 -1
- package/dist/coValues/coStream.js +0 -86
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/coValues/group.d.ts +44 -6
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +198 -17
- package/dist/coValues/group.js.map +1 -1
- package/dist/coreToCoValue.d.ts +2 -1
- package/dist/coreToCoValue.d.ts.map +1 -1
- package/dist/coreToCoValue.js +2 -1
- package/dist/coreToCoValue.js.map +1 -1
- package/dist/crypto/NapiCrypto.d.ts +18 -24
- package/dist/crypto/NapiCrypto.d.ts.map +1 -1
- package/dist/crypto/NapiCrypto.js +98 -60
- package/dist/crypto/NapiCrypto.js.map +1 -1
- package/dist/crypto/RNCrypto.d.ts +16 -3
- package/dist/crypto/RNCrypto.d.ts.map +1 -1
- package/dist/crypto/RNCrypto.js +117 -54
- package/dist/crypto/RNCrypto.js.map +1 -1
- package/dist/crypto/WasmCrypto.d.ts +18 -24
- package/dist/crypto/WasmCrypto.d.ts.map +1 -1
- package/dist/crypto/WasmCrypto.js +100 -61
- package/dist/crypto/WasmCrypto.js.map +1 -1
- package/dist/crypto/crypto.d.ts +55 -19
- package/dist/crypto/crypto.d.ts.map +1 -1
- package/dist/crypto/crypto.js +14 -3
- package/dist/crypto/crypto.js.map +1 -1
- package/dist/exports.d.ts +7 -3
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +4 -2
- package/dist/exports.js.map +1 -1
- package/dist/localNode.d.ts +3 -1
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +10 -3
- package/dist/localNode.js.map +1 -1
- package/dist/media.d.ts +1 -1
- package/dist/media.d.ts.map +1 -1
- package/dist/permissions.d.ts +2 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +19 -3
- package/dist/permissions.js.map +1 -1
- package/dist/storage/sqliteAsync/client.d.ts +24 -12
- package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
- package/dist/storage/sqliteAsync/client.js +70 -58
- package/dist/storage/sqliteAsync/client.js.map +1 -1
- package/dist/storage/sqliteAsync/types.d.ts +1 -1
- package/dist/storage/sqliteAsync/types.d.ts.map +1 -1
- package/dist/storage/types.d.ts +1 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +7 -1
- package/dist/sync.js.map +1 -1
- package/dist/tests/CojsonMessageChannel.test.js +2 -2
- package/dist/tests/SQLiteClientAsync.test.d.ts +2 -0
- package/dist/tests/SQLiteClientAsync.test.d.ts.map +1 -0
- package/dist/tests/SQLiteClientAsync.test.js +64 -0
- package/dist/tests/SQLiteClientAsync.test.js.map +1 -0
- package/dist/tests/StorageApiAsync.test.js +2 -8
- package/dist/tests/StorageApiAsync.test.js.map +1 -1
- package/dist/tests/SyncStateManager.test.js +2 -2
- package/dist/tests/WasmCrypto.test.js +1 -15
- package/dist/tests/WasmCrypto.test.js.map +1 -1
- package/dist/tests/coList.test.js +24 -5
- package/dist/tests/coList.test.js.map +1 -1
- package/dist/tests/coStream.test.js +4 -3
- package/dist/tests/coStream.test.js.map +1 -1
- package/dist/tests/coValueCore.initTransaction.test.d.ts +2 -0
- package/dist/tests/coValueCore.initTransaction.test.d.ts.map +1 -0
- package/dist/tests/coValueCore.initTransaction.test.js +438 -0
- package/dist/tests/coValueCore.initTransaction.test.js.map +1 -0
- package/dist/tests/coValueCore.test.js +11 -19
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/crypto.test.js +83 -0
- package/dist/tests/crypto.test.js.map +1 -1
- package/dist/tests/deleteCoValue.test.js +5 -5
- package/dist/tests/deleteCoValue.test.js.map +1 -1
- package/dist/tests/group.inheritance.test.js +11 -0
- package/dist/tests/group.inheritance.test.js.map +1 -1
- package/dist/tests/group.test.js +24 -1
- package/dist/tests/group.test.js.map +1 -1
- package/dist/tests/groupSealer.test.d.ts +2 -0
- package/dist/tests/groupSealer.test.d.ts.map +1 -0
- package/dist/tests/groupSealer.test.js +913 -0
- package/dist/tests/groupSealer.test.js.map +1 -0
- package/dist/tests/setup.js +5 -0
- package/dist/tests/setup.js.map +1 -1
- package/dist/tests/sync.auth.test.js +10 -10
- package/dist/tests/sync.concurrentLoad.test.js +12 -12
- package/dist/tests/sync.deleted.test.js +8 -8
- package/dist/tests/sync.garbageCollection.test.js +10 -10
- package/dist/tests/sync.invite.test.js +12 -12
- package/dist/tests/sync.known.test.js +2 -2
- package/dist/tests/sync.load.test.js +107 -107
- package/dist/tests/sync.mesh.test.js +164 -46
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.multipleServers.test.js +43 -43
- package/dist/tests/sync.peerReconciliation.test.js +29 -29
- package/dist/tests/sync.sharding.test.js +3 -3
- package/dist/tests/sync.storage.test.js +104 -104
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.storageAsync.test.js +56 -56
- package/dist/tests/sync.upload.test.js +22 -22
- package/dist/tests/testStorage.d.ts +2 -0
- package/dist/tests/testStorage.d.ts.map +1 -1
- package/dist/tests/testStorage.js +30 -6
- package/dist/tests/testStorage.js.map +1 -1
- package/dist/typeUtils/isCoValue.js +1 -1
- package/dist/typeUtils/isCoValue.js.map +1 -1
- package/package.json +4 -4
- package/src/SyncStateManager.ts +0 -2
- package/src/base64url.test.ts +89 -1
- package/src/base64url.ts +134 -6
- package/src/coValue.ts +2 -1
- package/src/coValueCore/coValueCore.ts +126 -84
- package/src/coValueCore/verifiedState.ts +335 -53
- package/src/coValues/binaryCoStream.ts +217 -0
- package/src/coValues/coList.ts +21 -8
- package/src/coValues/coMap.ts +3 -0
- package/src/coValues/coStream.ts +0 -170
- package/src/coValues/group.ts +270 -21
- package/src/coreToCoValue.ts +2 -1
- package/src/crypto/NapiCrypto.ts +198 -95
- package/src/crypto/RNCrypto.ts +229 -102
- package/src/crypto/WasmCrypto.ts +201 -95
- package/src/crypto/crypto.ts +118 -45
- package/src/exports.ts +11 -5
- package/src/localNode.ts +17 -1
- package/src/media.ts +1 -1
- package/src/permissions.ts +30 -7
- package/src/storage/sqliteAsync/client.ts +136 -115
- package/src/storage/sqliteAsync/types.ts +3 -1
- package/src/storage/types.ts +4 -0
- package/src/sync.ts +10 -1
- package/src/tests/CojsonMessageChannel.test.ts +2 -2
- package/src/tests/SQLiteClientAsync.test.ts +75 -0
- package/src/tests/StorageApiAsync.test.ts +4 -9
- package/src/tests/SyncStateManager.test.ts +2 -2
- package/src/tests/WasmCrypto.test.ts +1 -25
- package/src/tests/coList.test.ts +39 -5
- package/src/tests/coStream.test.ts +4 -5
- package/src/tests/coValueCore.initTransaction.test.ts +836 -0
- package/src/tests/coValueCore.test.ts +11 -22
- package/src/tests/crypto.test.ts +107 -0
- package/src/tests/deleteCoValue.test.ts +5 -5
- package/src/tests/group.inheritance.test.ts +16 -0
- package/src/tests/group.test.ts +29 -1
- package/src/tests/groupSealer.test.ts +1473 -0
- package/src/tests/setup.ts +6 -0
- package/src/tests/sync.auth.test.ts +10 -10
- package/src/tests/sync.concurrentLoad.test.ts +12 -12
- package/src/tests/sync.deleted.test.ts +8 -8
- package/src/tests/sync.garbageCollection.test.ts +10 -10
- package/src/tests/sync.invite.test.ts +12 -12
- package/src/tests/sync.known.test.ts +2 -2
- package/src/tests/sync.load.test.ts +107 -107
- package/src/tests/sync.mesh.test.ts +189 -46
- package/src/tests/sync.multipleServers.test.ts +43 -43
- package/src/tests/sync.peerReconciliation.test.ts +29 -29
- package/src/tests/sync.sharding.test.ts +3 -3
- package/src/tests/sync.storage.test.ts +104 -104
- package/src/tests/sync.storageAsync.test.ts +56 -56
- package/src/tests/sync.upload.test.ts +22 -22
- package/src/tests/testStorage.ts +39 -9
- package/src/typeUtils/isCoValue.ts +1 -1
- package/dist/coValueCore/SessionMap.d.ts +0 -55
- package/dist/coValueCore/SessionMap.d.ts.map +0 -1
- package/dist/coValueCore/SessionMap.js +0 -206
- package/dist/coValueCore/SessionMap.js.map +0 -1
- package/dist/tests/coreWasm.test.d.ts +0 -2
- package/dist/tests/coreWasm.test.d.ts.map +0 -1
- package/dist/tests/coreWasm.test.js +0 -203
- package/dist/tests/coreWasm.test.js.map +0 -1
- package/src/coValueCore/SessionMap.ts +0 -394
- package/src/tests/coreWasm.test.ts +0 -452
package/src/crypto/WasmCrypto.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
SessionMap as WasmSessionMap,
|
|
3
3
|
initialize,
|
|
4
4
|
initializeSync,
|
|
5
5
|
Blake3Hasher,
|
|
@@ -12,35 +12,35 @@ import {
|
|
|
12
12
|
newEd25519SigningKey,
|
|
13
13
|
newX25519PrivateKey,
|
|
14
14
|
seal,
|
|
15
|
+
sealForGroup,
|
|
16
|
+
shortHash,
|
|
15
17
|
sign,
|
|
16
18
|
unseal,
|
|
19
|
+
unsealForGroup,
|
|
17
20
|
verify,
|
|
18
21
|
} from "cojson-core-wasm";
|
|
19
22
|
import { base64URLtoBytes, bytesToBase64url } from "../base64url.js";
|
|
20
|
-
import { RawCoID,
|
|
23
|
+
import { RawCoID, TransactionID } from "../ids.js";
|
|
24
|
+
import { Transaction } from "../coValueCore/verifiedState.js";
|
|
21
25
|
import { Stringified, stableStringify } from "../jsonStringify.js";
|
|
22
|
-
import {
|
|
26
|
+
import { JsonValue } from "../jsonValue.js";
|
|
23
27
|
import { logger } from "../logger.js";
|
|
24
28
|
import {
|
|
25
29
|
CryptoProvider,
|
|
26
30
|
Encrypted,
|
|
27
|
-
KeyID,
|
|
28
31
|
KeySecret,
|
|
29
32
|
Sealed,
|
|
33
|
+
SealedForGroup,
|
|
30
34
|
SealerID,
|
|
31
35
|
SealerSecret,
|
|
36
|
+
SessionMapImpl,
|
|
37
|
+
ShortHash,
|
|
32
38
|
Signature,
|
|
33
39
|
SignerID,
|
|
34
40
|
SignerSecret,
|
|
35
41
|
textDecoder,
|
|
36
42
|
textEncoder,
|
|
37
43
|
} from "./crypto.js";
|
|
38
|
-
import { ControlledAccountOrAgent } from "../coValues/account.js";
|
|
39
|
-
import {
|
|
40
|
-
PrivateTransaction,
|
|
41
|
-
Transaction,
|
|
42
|
-
TrustingTransaction,
|
|
43
|
-
} from "../coValueCore/verifiedState.js";
|
|
44
44
|
import { isCloudflare, isEvalAllowed } from "../platformUtils.js";
|
|
45
45
|
|
|
46
46
|
type Blake3State = Blake3Hasher;
|
|
@@ -117,6 +117,10 @@ export class WasmCrypto extends CryptoProvider<Blake3State> {
|
|
|
117
117
|
return blake3HashOnceWithContext(data, context);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
shortHash(value: JsonValue): ShortHash {
|
|
121
|
+
return shortHash(stableStringify(value)) as ShortHash;
|
|
122
|
+
}
|
|
123
|
+
|
|
120
124
|
newEd25519SigningKey(): Uint8Array {
|
|
121
125
|
return newEd25519SigningKey();
|
|
122
126
|
}
|
|
@@ -221,116 +225,218 @@ export class WasmCrypto extends CryptoProvider<Blake3State> {
|
|
|
221
225
|
}
|
|
222
226
|
}
|
|
223
227
|
|
|
224
|
-
|
|
225
|
-
|
|
228
|
+
sealForGroup<T extends JsonValue>({
|
|
229
|
+
message,
|
|
230
|
+
to,
|
|
231
|
+
nOnceMaterial,
|
|
232
|
+
}: {
|
|
233
|
+
message: T;
|
|
234
|
+
to: SealerID;
|
|
235
|
+
nOnceMaterial: { in: RawCoID; tx: TransactionID };
|
|
236
|
+
}): SealedForGroup<T> {
|
|
237
|
+
return `sealedForGroup_U${bytesToBase64url(
|
|
238
|
+
sealForGroup(
|
|
239
|
+
textEncoder.encode(stableStringify(message)),
|
|
240
|
+
to,
|
|
241
|
+
textEncoder.encode(stableStringify(nOnceMaterial)),
|
|
242
|
+
),
|
|
243
|
+
)}` as SealedForGroup<T>;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
unsealForGroup<T extends JsonValue>(
|
|
247
|
+
sealed: SealedForGroup<T>,
|
|
248
|
+
groupSealerSecret: SealerSecret,
|
|
249
|
+
nOnceMaterial: { in: RawCoID; tx: TransactionID },
|
|
250
|
+
): T | undefined {
|
|
251
|
+
try {
|
|
252
|
+
const plaintext = textDecoder.decode(
|
|
253
|
+
unsealForGroup(
|
|
254
|
+
base64URLtoBytes(sealed.substring("sealedForGroup_U".length)),
|
|
255
|
+
groupSealerSecret,
|
|
256
|
+
textEncoder.encode(stableStringify(nOnceMaterial)),
|
|
257
|
+
),
|
|
258
|
+
);
|
|
259
|
+
return JSON.parse(plaintext) as T;
|
|
260
|
+
} catch (e) {
|
|
261
|
+
logger.error("Failed to decrypt/parse sealed for group message", {
|
|
262
|
+
err: e,
|
|
263
|
+
});
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
createSessionMap(
|
|
269
|
+
coID: RawCoID,
|
|
270
|
+
headerJson: string,
|
|
271
|
+
maxTxSize?: number,
|
|
272
|
+
skipVerify?: boolean,
|
|
273
|
+
): SessionMapImpl {
|
|
274
|
+
return new SessionMapAdapter(
|
|
275
|
+
new WasmSessionMap(coID, headerJson, maxTxSize, skipVerify),
|
|
276
|
+
);
|
|
226
277
|
}
|
|
227
278
|
}
|
|
228
279
|
|
|
229
|
-
|
|
230
|
-
|
|
280
|
+
/**
|
|
281
|
+
* Adapter wrapping WasmSessionMap to implement SessionMapImpl interface
|
|
282
|
+
*/
|
|
283
|
+
class SessionMapAdapter implements SessionMapImpl {
|
|
284
|
+
constructor(private readonly sessionMap: WasmSessionMap) {}
|
|
285
|
+
|
|
286
|
+
// === Header ===
|
|
287
|
+
getHeader(): string {
|
|
288
|
+
return this.sessionMap.getHeader();
|
|
289
|
+
}
|
|
231
290
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
291
|
+
// === Transaction Operations ===
|
|
292
|
+
addTransactions(
|
|
293
|
+
sessionId: string,
|
|
294
|
+
signerId: string | undefined,
|
|
295
|
+
transactionsJson: string,
|
|
296
|
+
signature: string,
|
|
235
297
|
skipVerify: boolean,
|
|
236
298
|
): void {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
tx.meta,
|
|
245
|
-
);
|
|
246
|
-
} else {
|
|
247
|
-
this.sessionLog.addExistingTrustingTransaction(
|
|
248
|
-
tx.changes,
|
|
249
|
-
tx.madeAt,
|
|
250
|
-
tx.meta,
|
|
251
|
-
);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
this.sessionLog.commitTransactions(newSignature, skipVerify);
|
|
299
|
+
this.sessionMap.addTransactions(
|
|
300
|
+
sessionId,
|
|
301
|
+
signerId,
|
|
302
|
+
transactionsJson,
|
|
303
|
+
signature,
|
|
304
|
+
skipVerify,
|
|
305
|
+
);
|
|
255
306
|
}
|
|
256
307
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
308
|
+
makeNewPrivateTransaction(
|
|
309
|
+
sessionId: string,
|
|
310
|
+
signerSecret: string,
|
|
311
|
+
changesJson: string,
|
|
312
|
+
keyId: string,
|
|
313
|
+
keySecret: string,
|
|
314
|
+
metaJson: string | undefined,
|
|
262
315
|
madeAt: number,
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
316
|
+
): string {
|
|
317
|
+
return this.sessionMap.makeNewPrivateTransaction(
|
|
318
|
+
sessionId,
|
|
319
|
+
signerSecret,
|
|
320
|
+
changesJson,
|
|
321
|
+
keyId,
|
|
269
322
|
keySecret,
|
|
270
|
-
|
|
323
|
+
metaJson,
|
|
271
324
|
madeAt,
|
|
272
|
-
// We can avoid stableStringify because it will be encrypted.
|
|
273
|
-
meta ? JSON.stringify(meta) : undefined,
|
|
274
325
|
);
|
|
275
|
-
const parsedOutput = JSON.parse(output);
|
|
276
|
-
const transaction: PrivateTransaction = {
|
|
277
|
-
privacy: "private",
|
|
278
|
-
madeAt,
|
|
279
|
-
encryptedChanges: parsedOutput.encrypted_changes,
|
|
280
|
-
keyUsed: keyID,
|
|
281
|
-
meta: parsedOutput.meta,
|
|
282
|
-
};
|
|
283
|
-
return { signature: parsedOutput.signature, transaction };
|
|
284
326
|
}
|
|
285
327
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
328
|
+
makeNewTrustingTransaction(
|
|
329
|
+
sessionId: string,
|
|
330
|
+
signerSecret: string,
|
|
331
|
+
changesJson: string,
|
|
332
|
+
metaJson: string | undefined,
|
|
289
333
|
madeAt: number,
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
const output = this.sessionLog.addNewTrustingTransaction(
|
|
297
|
-
stringifiedChanges,
|
|
298
|
-
signerAgent.currentSignerSecret(),
|
|
334
|
+
): string {
|
|
335
|
+
return this.sessionMap.makeNewTrustingTransaction(
|
|
336
|
+
sessionId,
|
|
337
|
+
signerSecret,
|
|
338
|
+
changesJson,
|
|
339
|
+
metaJson,
|
|
299
340
|
madeAt,
|
|
300
|
-
stringifiedMeta,
|
|
301
341
|
);
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// === Session Queries ===
|
|
345
|
+
getSessionIds(): string[] {
|
|
346
|
+
return this.sessionMap.getSessionIds();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
getTransactionCount(sessionId: string): number {
|
|
350
|
+
return this.sessionMap.getTransactionCount(sessionId);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
getTransaction(sessionId: string, txIndex: number): Transaction | undefined {
|
|
354
|
+
const result = this.sessionMap.getTransaction(sessionId, txIndex);
|
|
355
|
+
if (!result) return undefined;
|
|
356
|
+
return JSON.parse(result) as Transaction;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
getSessionTransactions(
|
|
360
|
+
sessionId: string,
|
|
361
|
+
fromIndex: number,
|
|
362
|
+
): Transaction[] | undefined {
|
|
363
|
+
const result = this.sessionMap.getSessionTransactions(sessionId, fromIndex);
|
|
364
|
+
if (!result) return undefined;
|
|
365
|
+
return result.map((tx) => JSON.parse(tx) as Transaction);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
getLastSignature(sessionId: string): string | undefined {
|
|
369
|
+
return this.sessionMap.getLastSignature(sessionId) ?? undefined;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
getSignatureAfter(sessionId: string, txIndex: number): string | undefined {
|
|
373
|
+
return this.sessionMap.getSignatureAfter(sessionId, txIndex) ?? undefined;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
getLastSignatureCheckpoint(sessionId: string): number | undefined {
|
|
377
|
+
return this.sessionMap.getLastSignatureCheckpoint(sessionId) ?? undefined;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// === Known State ===
|
|
381
|
+
getKnownState(): {
|
|
382
|
+
id: string;
|
|
383
|
+
header: boolean;
|
|
384
|
+
sessions: Record<string, number>;
|
|
385
|
+
} {
|
|
386
|
+
// WASM returns a native JS object via serde_wasm_bindgen
|
|
387
|
+
return this.sessionMap.getKnownState() as {
|
|
388
|
+
id: string;
|
|
389
|
+
header: boolean;
|
|
390
|
+
sessions: Record<string, number>;
|
|
307
391
|
};
|
|
308
|
-
return { signature: output as Signature, transaction };
|
|
309
392
|
}
|
|
310
393
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
394
|
+
getKnownStateWithStreaming():
|
|
395
|
+
| { id: string; header: boolean; sessions: Record<string, number> }
|
|
396
|
+
| undefined {
|
|
397
|
+
// WASM returns a native JS object via serde_wasm_bindgen, or undefined
|
|
398
|
+
const result = this.sessionMap.getKnownStateWithStreaming();
|
|
399
|
+
if (!result || result === undefined) return undefined;
|
|
400
|
+
return result as {
|
|
401
|
+
id: string;
|
|
402
|
+
header: boolean;
|
|
403
|
+
sessions: Record<string, number>;
|
|
404
|
+
};
|
|
320
405
|
}
|
|
321
406
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
407
|
+
setStreamingKnownState(streamingJson: string): void {
|
|
408
|
+
this.sessionMap.setStreamingKnownState(streamingJson);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// === Deletion ===
|
|
412
|
+
markAsDeleted(): void {
|
|
413
|
+
this.sessionMap.markAsDeleted();
|
|
327
414
|
}
|
|
328
415
|
|
|
329
|
-
|
|
330
|
-
this.
|
|
416
|
+
isDeleted(): boolean {
|
|
417
|
+
return this.sessionMap.isDeleted();
|
|
331
418
|
}
|
|
332
419
|
|
|
333
|
-
|
|
334
|
-
|
|
420
|
+
// === Decryption ===
|
|
421
|
+
decryptTransaction(
|
|
422
|
+
sessionId: string,
|
|
423
|
+
txIndex: number,
|
|
424
|
+
keySecret: string,
|
|
425
|
+
): string | undefined {
|
|
426
|
+
return (
|
|
427
|
+
this.sessionMap.decryptTransaction(sessionId, txIndex, keySecret) ??
|
|
428
|
+
undefined
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
decryptTransactionMeta(
|
|
433
|
+
sessionId: string,
|
|
434
|
+
txIndex: number,
|
|
435
|
+
keySecret: string,
|
|
436
|
+
): string | undefined {
|
|
437
|
+
return (
|
|
438
|
+
this.sessionMap.decryptTransactionMeta(sessionId, txIndex, keySecret) ??
|
|
439
|
+
undefined
|
|
440
|
+
);
|
|
335
441
|
}
|
|
336
442
|
}
|
package/src/crypto/crypto.ts
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
import { base58 } from "@scure/base";
|
|
2
|
-
import {
|
|
2
|
+
import { RawAccountID } from "../coValues/account.js";
|
|
3
3
|
import {
|
|
4
4
|
AgentID,
|
|
5
5
|
RawCoID,
|
|
6
6
|
TransactionID,
|
|
7
|
-
SessionID,
|
|
8
7
|
ActiveSessionID,
|
|
9
8
|
DeleteSessionID,
|
|
10
9
|
} from "../ids.js";
|
|
11
10
|
import { Stringified, parseJSON, stableStringify } from "../jsonStringify.js";
|
|
12
|
-
import {
|
|
11
|
+
import { JsonValue } from "../jsonValue.js";
|
|
13
12
|
import { logger } from "../logger.js";
|
|
14
|
-
import {
|
|
15
|
-
PrivateTransaction,
|
|
16
|
-
Transaction,
|
|
17
|
-
TrustingTransaction,
|
|
18
|
-
} from "../coValueCore/verifiedState.js";
|
|
13
|
+
import { Transaction } from "../coValueCore/verifiedState.js";
|
|
19
14
|
|
|
20
15
|
function randomBytes(bytesLength = 32): Uint8Array {
|
|
21
16
|
return crypto.getRandomValues(new Uint8Array(bytesLength));
|
|
@@ -28,6 +23,8 @@ export type Signature = `signature_z${string}`;
|
|
|
28
23
|
export type SealerSecret = `sealerSecret_z${string}`;
|
|
29
24
|
export type SealerID = `sealer_z${string}`;
|
|
30
25
|
export type Sealed<T> = `sealed_U${string}` & { __type: T };
|
|
26
|
+
// Anonymous box - encrypted to a group's sealer without sender authentication
|
|
27
|
+
export type SealedForGroup<T> = `sealedForGroup_U${string}` & { __type: T };
|
|
31
28
|
|
|
32
29
|
export type AgentSecret = `${SealerSecret}/${SignerSecret}`;
|
|
33
30
|
|
|
@@ -111,14 +108,7 @@ export abstract class CryptoProvider<Blake3State = any> {
|
|
|
111
108
|
)}`;
|
|
112
109
|
}
|
|
113
110
|
|
|
114
|
-
shortHash(value: JsonValue): ShortHash
|
|
115
|
-
return `shortHash_z${base58.encode(
|
|
116
|
-
this.blake3HashOnce(textEncoder.encode(stableStringify(value))).slice(
|
|
117
|
-
0,
|
|
118
|
-
shortHashLength,
|
|
119
|
-
),
|
|
120
|
-
)}`;
|
|
121
|
-
}
|
|
111
|
+
abstract shortHash(value: JsonValue): ShortHash;
|
|
122
112
|
|
|
123
113
|
abstract encrypt<T extends JsonValue, N extends JsonValue>(
|
|
124
114
|
value: T,
|
|
@@ -217,6 +207,45 @@ export abstract class CryptoProvider<Blake3State = any> {
|
|
|
217
207
|
nOnceMaterial: { in: RawCoID; tx: TransactionID },
|
|
218
208
|
): T | undefined;
|
|
219
209
|
|
|
210
|
+
// Derive group sealer deterministically from read key
|
|
211
|
+
// This ensures concurrent migrations by different admins produce the same result
|
|
212
|
+
groupSealerFromReadKey(readKeySecret: KeySecret): {
|
|
213
|
+
publicKey: SealerID;
|
|
214
|
+
secret: SealerSecret;
|
|
215
|
+
} {
|
|
216
|
+
const sealerBytes = this.blake3HashOnceWithContext(
|
|
217
|
+
textEncoder.encode(readKeySecret),
|
|
218
|
+
{ context: textEncoder.encode("groupSealer") },
|
|
219
|
+
);
|
|
220
|
+
// Blake3 output must be exactly 32 bytes to match X25519 secret key length
|
|
221
|
+
if (sealerBytes.length !== 32) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`Blake3 output must be 32 bytes for X25519 key, got ${sealerBytes.length}`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
const secret: SealerSecret = `sealerSecret_z${base58.encode(sealerBytes)}`;
|
|
227
|
+
return {
|
|
228
|
+
secret,
|
|
229
|
+
publicKey: this.getSealerID(secret),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Anonymous box - encrypt data to a group's sealer without sender authentication
|
|
234
|
+
// Uses ephemeral key pair, embeds ephemeral public key in output
|
|
235
|
+
abstract sealForGroup<T extends JsonValue>(args: {
|
|
236
|
+
message: T;
|
|
237
|
+
to: SealerID;
|
|
238
|
+
nOnceMaterial: { in: RawCoID; tx: TransactionID };
|
|
239
|
+
}): SealedForGroup<T>;
|
|
240
|
+
|
|
241
|
+
// Decrypt data sealed to a group
|
|
242
|
+
// Extracts ephemeral public key from sealed data, derives shared secret
|
|
243
|
+
abstract unsealForGroup<T extends JsonValue>(
|
|
244
|
+
sealed: SealedForGroup<T>,
|
|
245
|
+
groupSealerSecret: SealerSecret,
|
|
246
|
+
nOnceMaterial: { in: RawCoID; tx: TransactionID },
|
|
247
|
+
): T | undefined;
|
|
248
|
+
|
|
220
249
|
uniquenessForHeader(): `z${string}` {
|
|
221
250
|
return `z${base58.encode(this.randomBytes(12))}`;
|
|
222
251
|
}
|
|
@@ -262,11 +291,12 @@ export abstract class CryptoProvider<Blake3State = any> {
|
|
|
262
291
|
return `${accountID}_session_d${randomPart}$`;
|
|
263
292
|
}
|
|
264
293
|
|
|
265
|
-
abstract
|
|
294
|
+
abstract createSessionMap(
|
|
266
295
|
coID: RawCoID,
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
296
|
+
headerJson: string,
|
|
297
|
+
maxTxSize?: number,
|
|
298
|
+
skipVerify?: boolean,
|
|
299
|
+
): SessionMapImpl;
|
|
270
300
|
}
|
|
271
301
|
|
|
272
302
|
export type Hash = `hash_z${string}`;
|
|
@@ -283,34 +313,77 @@ export type KeyID = `key_z${string}`;
|
|
|
283
313
|
|
|
284
314
|
export const secretSeedLength = 32;
|
|
285
315
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
316
|
+
/**
|
|
317
|
+
* SessionMapImpl - Native implementation of SessionMap
|
|
318
|
+
* One instance per CoValue, owns all session data including header
|
|
319
|
+
*/
|
|
320
|
+
export interface SessionMapImpl {
|
|
321
|
+
// === Header ===
|
|
322
|
+
getHeader(): string;
|
|
323
|
+
|
|
324
|
+
// === Transaction Operations ===
|
|
325
|
+
addTransactions(
|
|
326
|
+
sessionId: string,
|
|
327
|
+
signerId: string | undefined,
|
|
328
|
+
transactionsJson: string,
|
|
329
|
+
signature: string,
|
|
291
330
|
skipVerify: boolean,
|
|
292
331
|
): void;
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
332
|
+
|
|
333
|
+
makeNewPrivateTransaction(
|
|
334
|
+
sessionId: string,
|
|
335
|
+
signerSecret: string,
|
|
336
|
+
changesJson: string,
|
|
337
|
+
keyId: string,
|
|
338
|
+
keySecret: string,
|
|
339
|
+
metaJson: string | undefined,
|
|
298
340
|
madeAt: number,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
341
|
+
): string; // Returns JSON: { signature, transaction }
|
|
342
|
+
|
|
343
|
+
makeNewTrustingTransaction(
|
|
344
|
+
sessionId: string,
|
|
345
|
+
signerSecret: string,
|
|
346
|
+
changesJson: string,
|
|
347
|
+
metaJson: string | undefined,
|
|
304
348
|
madeAt: number,
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
):
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
349
|
+
): string; // Returns JSON: { signature, transaction }
|
|
350
|
+
|
|
351
|
+
// === Session Queries ===
|
|
352
|
+
getSessionIds(): string[];
|
|
353
|
+
getTransactionCount(sessionId: string): number; // -1 if not found
|
|
354
|
+
getTransaction(sessionId: string, txIndex: number): Transaction | undefined;
|
|
355
|
+
getSessionTransactions(
|
|
356
|
+
sessionId: string,
|
|
357
|
+
fromIndex: number,
|
|
358
|
+
): Transaction[] | undefined;
|
|
359
|
+
getLastSignature(sessionId: string): string | undefined;
|
|
360
|
+
getSignatureAfter(sessionId: string, txIndex: number): string | undefined;
|
|
361
|
+
getLastSignatureCheckpoint(sessionId: string): number | undefined; // -1 if no checkpoints, undefined if session not found
|
|
362
|
+
|
|
363
|
+
// === Known State ===
|
|
364
|
+
getKnownState(): {
|
|
365
|
+
id: string;
|
|
366
|
+
header: boolean;
|
|
367
|
+
sessions: Record<string, number>;
|
|
368
|
+
};
|
|
369
|
+
getKnownStateWithStreaming():
|
|
370
|
+
| { id: string; header: boolean; sessions: Record<string, number> }
|
|
371
|
+
| undefined;
|
|
372
|
+
setStreamingKnownState(streamingJson: string): void;
|
|
373
|
+
|
|
374
|
+
// === Deletion ===
|
|
375
|
+
markAsDeleted(): void;
|
|
376
|
+
isDeleted(): boolean;
|
|
377
|
+
|
|
378
|
+
// === Decryption ===
|
|
379
|
+
decryptTransaction(
|
|
380
|
+
sessionId: string,
|
|
381
|
+
txIndex: number,
|
|
382
|
+
keySecret: string,
|
|
383
|
+
): string | undefined;
|
|
384
|
+
decryptTransactionMeta(
|
|
385
|
+
sessionId: string,
|
|
386
|
+
txIndex: number,
|
|
387
|
+
keySecret: string,
|
|
315
388
|
): string | undefined;
|
|
316
389
|
}
|
package/src/exports.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
base64URLtoBytes,
|
|
3
|
+
bytesToBase64url,
|
|
4
|
+
bytesToBase64,
|
|
5
|
+
} from "./base64url.js";
|
|
2
6
|
import { type RawCoValue } from "./coValue.js";
|
|
3
7
|
import {
|
|
4
8
|
CoValueCore,
|
|
@@ -23,10 +27,9 @@ import { RawCoPlainText, stringifyOpID } from "./coValues/coPlainText.js";
|
|
|
23
27
|
import {
|
|
24
28
|
BinaryStreamItem,
|
|
25
29
|
BinaryStreamStart,
|
|
26
|
-
CoStreamItem,
|
|
27
30
|
RawBinaryCoStream,
|
|
28
|
-
|
|
29
|
-
} from "./coValues/coStream.js";
|
|
31
|
+
} from "./coValues/binaryCoStream.js";
|
|
32
|
+
import { CoStreamItem, RawCoStream } from "./coValues/coStream.js";
|
|
30
33
|
import { EVERYONE, RawGroup } from "./coValues/group.js";
|
|
31
34
|
import type { Everyone } from "./coValues/group.js";
|
|
32
35
|
import {
|
|
@@ -58,7 +61,7 @@ import type {
|
|
|
58
61
|
import type {
|
|
59
62
|
BinaryCoStreamMeta,
|
|
60
63
|
BinaryStreamInfo,
|
|
61
|
-
} from "./coValues/
|
|
64
|
+
} from "./coValues/binaryCoStream.js";
|
|
62
65
|
import type { InviteSecret } from "./coValues/group.js";
|
|
63
66
|
import { AgentSecret, textDecoder, textEncoder } from "./crypto/crypto.js";
|
|
64
67
|
import type { AgentID, RawCoID, SessionID } from "./ids.js";
|
|
@@ -112,6 +115,7 @@ export const cojsonInternals = {
|
|
|
112
115
|
expectGroup,
|
|
113
116
|
base64URLtoBytes,
|
|
114
117
|
bytesToBase64url,
|
|
118
|
+
bytesToBase64,
|
|
115
119
|
parseJSON,
|
|
116
120
|
stableStringify,
|
|
117
121
|
getDependedOnCoValues,
|
|
@@ -229,6 +233,8 @@ export namespace CojsonInternalTypes {
|
|
|
229
233
|
export type RawCoID = import("./ids.js").RawCoID;
|
|
230
234
|
export type ProfileShape = import("./coValues/account.js").ProfileShape;
|
|
231
235
|
export type SealerSecret = import("./crypto/crypto.js").SealerSecret;
|
|
236
|
+
export type SealerID = import("./crypto/crypto.js").SealerID;
|
|
237
|
+
export type SealedForGroup<T> = import("./crypto/crypto.js").SealedForGroup<T>;
|
|
232
238
|
export type SignerID = import("./crypto/crypto.js").SignerID;
|
|
233
239
|
export type SignerSecret = import("./crypto/crypto.js").SignerSecret;
|
|
234
240
|
export type JsonObject = import("./jsonValue.js").JsonObject;
|
package/src/localNode.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
import {
|
|
28
28
|
type InviteSecret,
|
|
29
29
|
type RawGroup,
|
|
30
|
+
formatGroupSealerValue,
|
|
30
31
|
secretSeedFromInviteSecret,
|
|
31
32
|
} from "./coValues/group.js";
|
|
32
33
|
import { CO_VALUE_LOADING_CONFIG } from "./config.js";
|
|
@@ -824,13 +825,19 @@ export class LocalNode {
|
|
|
824
825
|
|
|
825
826
|
createGroup(
|
|
826
827
|
uniqueness: CoValueUniqueness = this.crypto.createdNowUnique(),
|
|
828
|
+
options?: { name?: string },
|
|
827
829
|
): RawGroup {
|
|
828
830
|
const account = this.getCurrentAgent();
|
|
829
831
|
|
|
832
|
+
const meta =
|
|
833
|
+
options?.name != null && options.name !== ""
|
|
834
|
+
? { name: options.name }
|
|
835
|
+
: null;
|
|
836
|
+
|
|
830
837
|
const groupCoValue = this.createCoValue({
|
|
831
838
|
type: "comap",
|
|
832
839
|
ruleset: { type: "group", initialAdmin: account.id },
|
|
833
|
-
meta
|
|
840
|
+
meta,
|
|
834
841
|
...(uniqueness.createdAt !== undefined
|
|
835
842
|
? { createdAt: uniqueness.createdAt }
|
|
836
843
|
: {}),
|
|
@@ -859,6 +866,15 @@ export class LocalNode {
|
|
|
859
866
|
|
|
860
867
|
group.set("readKey", readKey.id, "trusting");
|
|
861
868
|
|
|
869
|
+
// Initialize the group sealer (derived deterministically from the read key)
|
|
870
|
+
// Store composite value with readKeyID for deterministic readKey association
|
|
871
|
+
const groupSealer = this.crypto.groupSealerFromReadKey(readKey.secret);
|
|
872
|
+
group.set(
|
|
873
|
+
"groupSealer",
|
|
874
|
+
formatGroupSealerValue(readKey.id, groupSealer.publicKey),
|
|
875
|
+
"trusting",
|
|
876
|
+
);
|
|
877
|
+
|
|
862
878
|
return group;
|
|
863
879
|
}
|
|
864
880
|
|
package/src/media.ts
CHANGED