cojson 0.19.21 → 0.20.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +67 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.d.ts +42 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.js +261 -0
- package/dist/CojsonMessageChannel/CojsonMessageChannel.js.map +1 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.d.ts +18 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.js +37 -0
- package/dist/CojsonMessageChannel/MessagePortOutgoingChannel.js.map +1 -0
- package/dist/CojsonMessageChannel/index.d.ts +3 -0
- package/dist/CojsonMessageChannel/index.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/index.js +2 -0
- package/dist/CojsonMessageChannel/index.js.map +1 -0
- package/dist/CojsonMessageChannel/types.d.ts +149 -0
- package/dist/CojsonMessageChannel/types.d.ts.map +1 -0
- package/dist/CojsonMessageChannel/types.js +36 -0
- package/dist/CojsonMessageChannel/types.js.map +1 -0
- package/dist/GarbageCollector.d.ts +4 -2
- package/dist/GarbageCollector.d.ts.map +1 -1
- package/dist/GarbageCollector.js +5 -3
- package/dist/GarbageCollector.js.map +1 -1
- package/dist/SyncStateManager.d.ts +3 -3
- package/dist/SyncStateManager.d.ts.map +1 -1
- package/dist/SyncStateManager.js +4 -4
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/coValueContentMessage.d.ts +0 -2
- package/dist/coValueContentMessage.d.ts.map +1 -1
- package/dist/coValueContentMessage.js +0 -8
- package/dist/coValueContentMessage.js.map +1 -1
- package/dist/coValueCore/SessionMap.d.ts +4 -2
- package/dist/coValueCore/SessionMap.d.ts.map +1 -1
- package/dist/coValueCore/SessionMap.js +30 -0
- package/dist/coValueCore/SessionMap.js.map +1 -1
- package/dist/coValueCore/coValueCore.d.ts +86 -4
- package/dist/coValueCore/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore/coValueCore.js +318 -17
- package/dist/coValueCore/coValueCore.js.map +1 -1
- package/dist/coValueCore/verifiedState.d.ts +6 -1
- package/dist/coValueCore/verifiedState.d.ts.map +1 -1
- package/dist/coValueCore/verifiedState.js +9 -0
- package/dist/coValueCore/verifiedState.js.map +1 -1
- package/dist/coValues/coList.d.ts +3 -2
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/group.d.ts.map +1 -1
- package/dist/coValues/group.js +3 -6
- package/dist/coValues/group.js.map +1 -1
- package/dist/config.d.ts +0 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +0 -8
- package/dist/config.js.map +1 -1
- package/dist/crypto/NapiCrypto.d.ts +1 -2
- package/dist/crypto/NapiCrypto.d.ts.map +1 -1
- package/dist/crypto/NapiCrypto.js +19 -4
- package/dist/crypto/NapiCrypto.js.map +1 -1
- package/dist/crypto/RNCrypto.d.ts.map +1 -1
- package/dist/crypto/RNCrypto.js +19 -4
- package/dist/crypto/RNCrypto.js.map +1 -1
- package/dist/crypto/WasmCrypto.d.ts +11 -4
- package/dist/crypto/WasmCrypto.d.ts.map +1 -1
- package/dist/crypto/WasmCrypto.js +52 -10
- package/dist/crypto/WasmCrypto.js.map +1 -1
- package/dist/crypto/WasmCryptoEdge.d.ts +1 -0
- package/dist/crypto/WasmCryptoEdge.d.ts.map +1 -1
- package/dist/crypto/WasmCryptoEdge.js +4 -1
- package/dist/crypto/WasmCryptoEdge.js.map +1 -1
- package/dist/crypto/crypto.d.ts +3 -3
- package/dist/crypto/crypto.d.ts.map +1 -1
- package/dist/crypto/crypto.js +6 -1
- package/dist/crypto/crypto.js.map +1 -1
- package/dist/exports.d.ts +3 -2
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +3 -1
- package/dist/exports.js.map +1 -1
- package/dist/ids.d.ts +4 -1
- package/dist/ids.d.ts.map +1 -1
- package/dist/ids.js +4 -0
- package/dist/ids.js.map +1 -1
- package/dist/knownState.d.ts +2 -0
- package/dist/knownState.d.ts.map +1 -1
- package/dist/localNode.d.ts +13 -3
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +17 -2
- package/dist/localNode.js.map +1 -1
- package/dist/platformUtils.d.ts +3 -0
- package/dist/platformUtils.d.ts.map +1 -0
- package/dist/platformUtils.js +24 -0
- package/dist/platformUtils.js.map +1 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.d.ts +30 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.d.ts.map +1 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.js +84 -0
- package/dist/storage/DeletedCoValuesEraserScheduler.js.map +1 -0
- package/dist/storage/sqlite/client.d.ts +3 -0
- package/dist/storage/sqlite/client.d.ts.map +1 -1
- package/dist/storage/sqlite/client.js +44 -0
- package/dist/storage/sqlite/client.js.map +1 -1
- package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -1
- package/dist/storage/sqlite/sqliteMigrations.js +7 -0
- package/dist/storage/sqlite/sqliteMigrations.js.map +1 -1
- package/dist/storage/sqliteAsync/client.d.ts +3 -0
- package/dist/storage/sqliteAsync/client.d.ts.map +1 -1
- package/dist/storage/sqliteAsync/client.js +42 -0
- package/dist/storage/sqliteAsync/client.js.map +1 -1
- package/dist/storage/storageAsync.d.ts +15 -3
- package/dist/storage/storageAsync.d.ts.map +1 -1
- package/dist/storage/storageAsync.js +60 -3
- package/dist/storage/storageAsync.js.map +1 -1
- package/dist/storage/storageSync.d.ts +14 -3
- package/dist/storage/storageSync.d.ts.map +1 -1
- package/dist/storage/storageSync.js +54 -3
- package/dist/storage/storageSync.js.map +1 -1
- package/dist/storage/types.d.ts +64 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +12 -1
- package/dist/storage/types.js.map +1 -1
- package/dist/sync.d.ts +6 -0
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +69 -15
- package/dist/sync.js.map +1 -1
- package/dist/tests/CojsonMessageChannel.test.d.ts +2 -0
- package/dist/tests/CojsonMessageChannel.test.d.ts.map +1 -0
- package/dist/tests/CojsonMessageChannel.test.js +236 -0
- package/dist/tests/CojsonMessageChannel.test.js.map +1 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts +2 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.d.ts.map +1 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.js +149 -0
- package/dist/tests/DeletedCoValuesEraserScheduler.test.js.map +1 -0
- package/dist/tests/GarbageCollector.test.js +91 -18
- package/dist/tests/GarbageCollector.test.js.map +1 -1
- package/dist/tests/StorageApiAsync.test.js +510 -146
- package/dist/tests/StorageApiAsync.test.js.map +1 -1
- package/dist/tests/StorageApiSync.test.js +531 -130
- package/dist/tests/StorageApiSync.test.js.map +1 -1
- package/dist/tests/SyncManager.processQueues.test.js +1 -1
- package/dist/tests/SyncManager.processQueues.test.js.map +1 -1
- package/dist/tests/SyncStateManager.test.js +1 -1
- package/dist/tests/SyncStateManager.test.js.map +1 -1
- package/dist/tests/WasmCrypto.test.js +6 -3
- package/dist/tests/WasmCrypto.test.js.map +1 -1
- package/dist/tests/coPlainText.test.js +1 -1
- package/dist/tests/coPlainText.test.js.map +1 -1
- package/dist/tests/coValueCore.loadFromStorage.test.js +4 -0
- package/dist/tests/coValueCore.loadFromStorage.test.js.map +1 -1
- package/dist/tests/coValueCore.test.js +34 -13
- package/dist/tests/coValueCore.test.js.map +1 -1
- package/dist/tests/coreWasm.test.js +127 -4
- package/dist/tests/coreWasm.test.js.map +1 -1
- package/dist/tests/crypto.test.js +89 -93
- package/dist/tests/crypto.test.js.map +1 -1
- package/dist/tests/deleteCoValue.test.d.ts +2 -0
- package/dist/tests/deleteCoValue.test.d.ts.map +1 -0
- package/dist/tests/deleteCoValue.test.js +313 -0
- package/dist/tests/deleteCoValue.test.js.map +1 -0
- package/dist/tests/group.removeMember.test.js +18 -30
- package/dist/tests/group.removeMember.test.js.map +1 -1
- package/dist/tests/knownState.lazyLoading.test.js +4 -0
- package/dist/tests/knownState.lazyLoading.test.js.map +1 -1
- package/dist/tests/sync.deleted.test.d.ts +2 -0
- package/dist/tests/sync.deleted.test.d.ts.map +1 -0
- package/dist/tests/sync.deleted.test.js +214 -0
- package/dist/tests/sync.deleted.test.js.map +1 -0
- package/dist/tests/sync.garbageCollection.test.js +56 -32
- package/dist/tests/sync.garbageCollection.test.js.map +1 -1
- package/dist/tests/sync.load.test.js +3 -5
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.mesh.test.js +4 -3
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.peerReconciliation.test.js +3 -3
- package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +12 -11
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.storageAsync.test.js +7 -7
- package/dist/tests/sync.storageAsync.test.js.map +1 -1
- package/dist/tests/sync.test.js +3 -2
- package/dist/tests/sync.test.js.map +1 -1
- package/dist/tests/sync.tracking.test.js +35 -4
- package/dist/tests/sync.tracking.test.js.map +1 -1
- package/dist/tests/testStorage.d.ts +3 -0
- package/dist/tests/testStorage.d.ts.map +1 -1
- package/dist/tests/testStorage.js +16 -2
- package/dist/tests/testStorage.js.map +1 -1
- package/dist/tests/testUtils.d.ts +29 -4
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +84 -9
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +6 -16
- package/src/CojsonMessageChannel/CojsonMessageChannel.ts +332 -0
- package/src/CojsonMessageChannel/MessagePortOutgoingChannel.ts +52 -0
- package/src/CojsonMessageChannel/index.ts +9 -0
- package/src/CojsonMessageChannel/types.ts +200 -0
- package/src/GarbageCollector.ts +5 -5
- package/src/SyncStateManager.ts +6 -6
- package/src/coValueContentMessage.ts +0 -14
- package/src/coValueCore/SessionMap.ts +43 -1
- package/src/coValueCore/coValueCore.ts +430 -15
- package/src/coValueCore/verifiedState.ts +26 -3
- package/src/coValues/coList.ts +5 -3
- package/src/coValues/group.ts +5 -6
- package/src/config.ts +0 -9
- package/src/crypto/NapiCrypto.ts +29 -13
- package/src/crypto/RNCrypto.ts +29 -11
- package/src/crypto/WasmCrypto.ts +67 -20
- package/src/crypto/WasmCryptoEdge.ts +5 -1
- package/src/crypto/crypto.ts +16 -4
- package/src/exports.ts +3 -0
- package/src/ids.ts +11 -1
- package/src/localNode.ts +18 -5
- package/src/platformUtils.ts +26 -0
- package/src/storage/DeletedCoValuesEraserScheduler.ts +124 -0
- package/src/storage/sqlite/client.ts +77 -0
- package/src/storage/sqlite/sqliteMigrations.ts +7 -0
- package/src/storage/sqliteAsync/client.ts +75 -0
- package/src/storage/storageAsync.ts +77 -4
- package/src/storage/storageSync.ts +73 -4
- package/src/storage/types.ts +75 -0
- package/src/sync.ts +84 -15
- package/src/tests/CojsonMessageChannel.test.ts +306 -0
- package/src/tests/DeletedCoValuesEraserScheduler.test.ts +185 -0
- package/src/tests/GarbageCollector.test.ts +119 -22
- package/src/tests/StorageApiAsync.test.ts +615 -156
- package/src/tests/StorageApiSync.test.ts +623 -137
- package/src/tests/SyncManager.processQueues.test.ts +1 -1
- package/src/tests/SyncStateManager.test.ts +1 -1
- package/src/tests/WasmCrypto.test.ts +8 -3
- package/src/tests/coPlainText.test.ts +1 -1
- package/src/tests/coValueCore.loadFromStorage.test.ts +8 -0
- package/src/tests/coValueCore.test.ts +49 -14
- package/src/tests/coreWasm.test.ts +319 -10
- package/src/tests/crypto.test.ts +141 -150
- package/src/tests/deleteCoValue.test.ts +528 -0
- package/src/tests/group.removeMember.test.ts +35 -35
- package/src/tests/knownState.lazyLoading.test.ts +8 -0
- package/src/tests/sync.deleted.test.ts +294 -0
- package/src/tests/sync.garbageCollection.test.ts +69 -36
- package/src/tests/sync.load.test.ts +3 -5
- package/src/tests/sync.mesh.test.ts +6 -3
- package/src/tests/sync.peerReconciliation.test.ts +3 -3
- package/src/tests/sync.storage.test.ts +14 -11
- package/src/tests/sync.storageAsync.test.ts +7 -7
- package/src/tests/sync.test.ts +5 -2
- package/src/tests/sync.tracking.test.ts +54 -4
- package/src/tests/testStorage.ts +30 -3
- package/src/tests/testUtils.ts +113 -15
- package/dist/crypto/PureJSCrypto.d.ts +0 -77
- package/dist/crypto/PureJSCrypto.d.ts.map +0 -1
- package/dist/crypto/PureJSCrypto.js +0 -236
- package/dist/crypto/PureJSCrypto.js.map +0 -1
- package/dist/tests/PureJSCrypto.test.d.ts +0 -2
- package/dist/tests/PureJSCrypto.test.d.ts.map +0 -1
- package/dist/tests/PureJSCrypto.test.js +0 -145
- package/dist/tests/PureJSCrypto.test.js.map +0 -1
- package/src/crypto/PureJSCrypto.ts +0 -429
- package/src/tests/PureJSCrypto.test.ts +0 -217
|
@@ -1,429 +0,0 @@
|
|
|
1
|
-
import { xsalsa20, xsalsa20poly1305 } from "@noble/ciphers/salsa";
|
|
2
|
-
import { ed25519, x25519 } from "@noble/curves/ed25519";
|
|
3
|
-
import { blake3 } from "@noble/hashes/blake3";
|
|
4
|
-
import { base58 } from "@scure/base";
|
|
5
|
-
import { base64URLtoBytes, bytesToBase64url } from "../base64url.js";
|
|
6
|
-
import {
|
|
7
|
-
PrivateTransaction,
|
|
8
|
-
Transaction,
|
|
9
|
-
TrustingTransaction,
|
|
10
|
-
} from "../coValueCore/verifiedState.js";
|
|
11
|
-
import { RawCoID, SessionID, TransactionID } from "../ids.js";
|
|
12
|
-
import { Stringified, stableStringify } from "../jsonStringify.js";
|
|
13
|
-
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
14
|
-
import { logger } from "../logger.js";
|
|
15
|
-
import {
|
|
16
|
-
CryptoProvider,
|
|
17
|
-
Encrypted,
|
|
18
|
-
KeyID,
|
|
19
|
-
KeySecret,
|
|
20
|
-
Sealed,
|
|
21
|
-
SealerID,
|
|
22
|
-
SealerSecret,
|
|
23
|
-
SessionLogImpl,
|
|
24
|
-
Signature,
|
|
25
|
-
SignerID,
|
|
26
|
-
SignerSecret,
|
|
27
|
-
textDecoder,
|
|
28
|
-
textEncoder,
|
|
29
|
-
} from "./crypto.js";
|
|
30
|
-
import { ControlledAccountOrAgent } from "../coValues/account.js";
|
|
31
|
-
|
|
32
|
-
export type Blake3State = {
|
|
33
|
-
update: (buf: Uint8Array) => Blake3State;
|
|
34
|
-
digest: () => Uint8Array;
|
|
35
|
-
clone: () => Blake3State;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const x25519SharedSecretCache = new Map<string, Uint8Array>();
|
|
39
|
-
|
|
40
|
-
function getx25519SharedSecret(
|
|
41
|
-
privateKeyA: SealerSecret,
|
|
42
|
-
publicKeyB: SealerID,
|
|
43
|
-
): Uint8Array {
|
|
44
|
-
const cacheKey = `${privateKeyA}-${publicKeyB}`;
|
|
45
|
-
let sharedSecret = x25519SharedSecretCache.get(cacheKey);
|
|
46
|
-
|
|
47
|
-
if (!sharedSecret) {
|
|
48
|
-
const privateKeyABytes = base58.decode(
|
|
49
|
-
privateKeyA.substring("sealerSecret_z".length),
|
|
50
|
-
);
|
|
51
|
-
const publicKeyBBytes = base58.decode(
|
|
52
|
-
publicKeyB.substring("sealer_z".length),
|
|
53
|
-
);
|
|
54
|
-
sharedSecret = x25519.getSharedSecret(privateKeyABytes, publicKeyBBytes);
|
|
55
|
-
x25519SharedSecretCache.set(cacheKey, sharedSecret);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return sharedSecret;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Pure JavaScript implementation of the CryptoProvider interface using noble-curves and noble-ciphers libraries.
|
|
63
|
-
* This provides a fallback implementation that doesn't require WebAssembly, offering:
|
|
64
|
-
* - Signing/verifying (Ed25519)
|
|
65
|
-
* - Encryption/decryption (XSalsa20)
|
|
66
|
-
* - Sealing/unsealing (X25519 + XSalsa20-Poly1305)
|
|
67
|
-
* - Hashing (BLAKE3)
|
|
68
|
-
*/
|
|
69
|
-
export class PureJSCrypto extends CryptoProvider<Blake3State> {
|
|
70
|
-
static async create(): Promise<PureJSCrypto> {
|
|
71
|
-
return new PureJSCrypto();
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
createStreamingHash(): Blake3State {
|
|
75
|
-
return blake3.create({});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
blake3HashOnce(data: Uint8Array) {
|
|
79
|
-
return blake3(data);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
blake3HashOnceWithContext(
|
|
83
|
-
data: Uint8Array,
|
|
84
|
-
{ context }: { context: Uint8Array },
|
|
85
|
-
) {
|
|
86
|
-
return this.createStreamingHash().update(context).update(data).digest();
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
generateNonce(input: Uint8Array): Uint8Array {
|
|
90
|
-
return this.blake3HashOnce(input).slice(0, 24);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
generateJsonNonce(material: JsonValue): Uint8Array {
|
|
94
|
-
return this.generateNonce(textEncoder.encode(stableStringify(material)));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
newEd25519SigningKey(): Uint8Array {
|
|
98
|
-
return ed25519.utils.randomPrivateKey();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
getSignerID(secret: SignerSecret): SignerID {
|
|
102
|
-
return `signer_z${base58.encode(
|
|
103
|
-
ed25519.getPublicKey(
|
|
104
|
-
base58.decode(secret.substring("signerSecret_z".length)),
|
|
105
|
-
),
|
|
106
|
-
)}`;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
sign(secret: SignerSecret, message: JsonValue): Signature {
|
|
110
|
-
const signature = ed25519.sign(
|
|
111
|
-
textEncoder.encode(stableStringify(message)),
|
|
112
|
-
base58.decode(secret.substring("signerSecret_z".length)),
|
|
113
|
-
);
|
|
114
|
-
return `signature_z${base58.encode(signature)}`;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
verify(signature: Signature, message: JsonValue, id: SignerID): boolean {
|
|
118
|
-
return ed25519.verify(
|
|
119
|
-
base58.decode(signature.substring("signature_z".length)),
|
|
120
|
-
textEncoder.encode(stableStringify(message)),
|
|
121
|
-
base58.decode(id.substring("signer_z".length)),
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
newX25519StaticSecret(): Uint8Array {
|
|
126
|
-
return x25519.utils.randomPrivateKey();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
getSealerID(secret: SealerSecret): SealerID {
|
|
130
|
-
return `sealer_z${base58.encode(
|
|
131
|
-
x25519.getPublicKey(
|
|
132
|
-
base58.decode(secret.substring("sealerSecret_z".length)),
|
|
133
|
-
),
|
|
134
|
-
)}`;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
encrypt<T extends JsonValue, N extends JsonValue>(
|
|
138
|
-
value: T,
|
|
139
|
-
keySecret: KeySecret,
|
|
140
|
-
nOnceMaterial: N,
|
|
141
|
-
): Encrypted<T, N> {
|
|
142
|
-
const keySecretBytes = base58.decode(
|
|
143
|
-
keySecret.substring("keySecret_z".length),
|
|
144
|
-
);
|
|
145
|
-
const nOnce = this.generateJsonNonce(nOnceMaterial);
|
|
146
|
-
|
|
147
|
-
const plaintext = textEncoder.encode(stableStringify(value));
|
|
148
|
-
const ciphertext = xsalsa20(keySecretBytes, nOnce, plaintext);
|
|
149
|
-
return `encrypted_U${bytesToBase64url(ciphertext)}` as Encrypted<T, N>;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
decryptRaw<T extends JsonValue, N extends JsonValue>(
|
|
153
|
-
encrypted: Encrypted<T, N>,
|
|
154
|
-
keySecret: KeySecret,
|
|
155
|
-
nOnceMaterial: N,
|
|
156
|
-
): Stringified<T> {
|
|
157
|
-
const keySecretBytes = base58.decode(
|
|
158
|
-
keySecret.substring("keySecret_z".length),
|
|
159
|
-
);
|
|
160
|
-
const nOnce = this.generateJsonNonce(nOnceMaterial);
|
|
161
|
-
|
|
162
|
-
const ciphertext = base64URLtoBytes(
|
|
163
|
-
encrypted.substring("encrypted_U".length),
|
|
164
|
-
);
|
|
165
|
-
const plaintext = xsalsa20(keySecretBytes, nOnce, ciphertext);
|
|
166
|
-
|
|
167
|
-
return textDecoder.decode(plaintext) as Stringified<T>;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
seal<T extends JsonValue>({
|
|
171
|
-
message,
|
|
172
|
-
from,
|
|
173
|
-
to,
|
|
174
|
-
nOnceMaterial,
|
|
175
|
-
}: {
|
|
176
|
-
message: T;
|
|
177
|
-
from: SealerSecret;
|
|
178
|
-
to: SealerID;
|
|
179
|
-
nOnceMaterial: { in: RawCoID; tx: TransactionID };
|
|
180
|
-
}): Sealed<T> {
|
|
181
|
-
const sharedSecret = getx25519SharedSecret(from, to);
|
|
182
|
-
const nOnce = this.generateJsonNonce(nOnceMaterial);
|
|
183
|
-
const plaintext = textEncoder.encode(stableStringify(message));
|
|
184
|
-
|
|
185
|
-
const sealedBytes = xsalsa20poly1305(sharedSecret, nOnce).encrypt(
|
|
186
|
-
plaintext,
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
return `sealed_U${bytesToBase64url(sealedBytes)}` as Sealed<T>;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
unseal<T extends JsonValue>(
|
|
193
|
-
sealed: Sealed<T>,
|
|
194
|
-
sealer: SealerSecret,
|
|
195
|
-
from: SealerID,
|
|
196
|
-
nOnceMaterial: { in: RawCoID; tx: TransactionID },
|
|
197
|
-
): T | undefined {
|
|
198
|
-
const nOnce = this.generateJsonNonce(nOnceMaterial);
|
|
199
|
-
|
|
200
|
-
const sharedSecret = getx25519SharedSecret(sealer, from);
|
|
201
|
-
const sealedBytes = base64URLtoBytes(sealed.substring("sealed_U".length));
|
|
202
|
-
|
|
203
|
-
const plaintext = xsalsa20poly1305(sharedSecret, nOnce).decrypt(
|
|
204
|
-
sealedBytes,
|
|
205
|
-
);
|
|
206
|
-
|
|
207
|
-
try {
|
|
208
|
-
return JSON.parse(textDecoder.decode(plaintext));
|
|
209
|
-
} catch (e) {
|
|
210
|
-
logger.error("Failed to decrypt/parse sealed message", { err: e });
|
|
211
|
-
return undefined;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
createSessionLog(
|
|
216
|
-
coID: RawCoID,
|
|
217
|
-
sessionID: SessionID,
|
|
218
|
-
signerID?: SignerID,
|
|
219
|
-
): SessionLogImpl {
|
|
220
|
-
return new PureJSSessionLog(coID, sessionID, signerID, this);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export class PureJSSessionLog implements SessionLogImpl {
|
|
225
|
-
transactions: string[] = [];
|
|
226
|
-
lastSignature: Signature | undefined;
|
|
227
|
-
streamingHash: Blake3State;
|
|
228
|
-
|
|
229
|
-
constructor(
|
|
230
|
-
private readonly coID: RawCoID,
|
|
231
|
-
private readonly sessionID: SessionID,
|
|
232
|
-
private readonly signerID: SignerID | undefined,
|
|
233
|
-
private readonly crypto: PureJSCrypto,
|
|
234
|
-
) {
|
|
235
|
-
this.streamingHash = crypto.createStreamingHash();
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
clone(): SessionLogImpl {
|
|
239
|
-
const newLog = new PureJSSessionLog(
|
|
240
|
-
this.coID,
|
|
241
|
-
this.sessionID,
|
|
242
|
-
this.signerID,
|
|
243
|
-
this.crypto,
|
|
244
|
-
);
|
|
245
|
-
newLog.transactions = this.transactions.slice();
|
|
246
|
-
newLog.lastSignature = this.lastSignature;
|
|
247
|
-
newLog.streamingHash = this.streamingHash.clone();
|
|
248
|
-
return newLog;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
tryAdd(
|
|
252
|
-
transactions: Transaction[],
|
|
253
|
-
newSignature: Signature,
|
|
254
|
-
skipVerify: boolean,
|
|
255
|
-
): void {
|
|
256
|
-
this.internalTryAdd(
|
|
257
|
-
transactions.map((tx) => stableStringify(tx)),
|
|
258
|
-
newSignature,
|
|
259
|
-
skipVerify,
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
internalTryAdd(
|
|
264
|
-
transactions: string[],
|
|
265
|
-
newSignature: Signature,
|
|
266
|
-
skipVerify: boolean,
|
|
267
|
-
) {
|
|
268
|
-
for (const tx of transactions) {
|
|
269
|
-
this.streamingHash.update(textEncoder.encode(tx));
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
if (!skipVerify) {
|
|
273
|
-
if (!this.signerID) {
|
|
274
|
-
throw new Error("Tried to add transactions without signer ID");
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
const newHash = this.streamingHash.clone().digest();
|
|
278
|
-
const newHashEncoded = `hash_z${base58.encode(newHash)}`;
|
|
279
|
-
|
|
280
|
-
if (!this.crypto.verify(newSignature, newHashEncoded, this.signerID)) {
|
|
281
|
-
// Rebuild the streaming hash to the original state
|
|
282
|
-
this.streamingHash = this.crypto.createStreamingHash();
|
|
283
|
-
for (const tx of this.transactions) {
|
|
284
|
-
this.streamingHash.update(textEncoder.encode(tx));
|
|
285
|
-
}
|
|
286
|
-
throw new Error("Signature verification failed");
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
for (const tx of transactions) {
|
|
291
|
-
this.transactions.push(tx);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
this.lastSignature = newSignature;
|
|
295
|
-
|
|
296
|
-
return newSignature;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
internalAddNewTransaction(
|
|
300
|
-
transaction: string,
|
|
301
|
-
signerAgent: ControlledAccountOrAgent,
|
|
302
|
-
) {
|
|
303
|
-
this.streamingHash.update(textEncoder.encode(transaction));
|
|
304
|
-
const newHash = this.streamingHash.clone().digest();
|
|
305
|
-
const newHashEncoded = `hash_z${base58.encode(newHash)}`;
|
|
306
|
-
const signature = this.crypto.sign(
|
|
307
|
-
signerAgent.currentSignerSecret(),
|
|
308
|
-
newHashEncoded,
|
|
309
|
-
);
|
|
310
|
-
this.transactions.push(transaction);
|
|
311
|
-
this.lastSignature = signature;
|
|
312
|
-
|
|
313
|
-
return signature;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
addNewPrivateTransaction(
|
|
317
|
-
signerAgent: ControlledAccountOrAgent,
|
|
318
|
-
changes: JsonValue[],
|
|
319
|
-
keyID: KeyID,
|
|
320
|
-
keySecret: KeySecret,
|
|
321
|
-
madeAt: number,
|
|
322
|
-
meta: JsonObject | undefined,
|
|
323
|
-
): { signature: Signature; transaction: PrivateTransaction } {
|
|
324
|
-
const encryptedChanges = this.crypto.encrypt(changes, keySecret, {
|
|
325
|
-
in: this.coID,
|
|
326
|
-
tx: { sessionID: this.sessionID, txIndex: this.transactions.length },
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
const encryptedMeta = meta
|
|
330
|
-
? this.crypto.encrypt(meta, keySecret, {
|
|
331
|
-
in: this.coID,
|
|
332
|
-
tx: { sessionID: this.sessionID, txIndex: this.transactions.length },
|
|
333
|
-
})
|
|
334
|
-
: undefined;
|
|
335
|
-
|
|
336
|
-
const tx = {
|
|
337
|
-
encryptedChanges: encryptedChanges,
|
|
338
|
-
madeAt: madeAt,
|
|
339
|
-
privacy: "private",
|
|
340
|
-
keyUsed: keyID,
|
|
341
|
-
meta: encryptedMeta,
|
|
342
|
-
} satisfies Transaction;
|
|
343
|
-
const signature = this.internalAddNewTransaction(
|
|
344
|
-
stableStringify(tx),
|
|
345
|
-
signerAgent,
|
|
346
|
-
);
|
|
347
|
-
return {
|
|
348
|
-
signature: signature as Signature,
|
|
349
|
-
transaction: tx,
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
addNewTrustingTransaction(
|
|
354
|
-
signerAgent: ControlledAccountOrAgent,
|
|
355
|
-
changes: JsonValue[],
|
|
356
|
-
madeAt: number,
|
|
357
|
-
meta: JsonObject | undefined,
|
|
358
|
-
): { signature: Signature; transaction: TrustingTransaction } {
|
|
359
|
-
const tx = {
|
|
360
|
-
changes: stableStringify(changes),
|
|
361
|
-
madeAt: madeAt,
|
|
362
|
-
privacy: "trusting",
|
|
363
|
-
meta: meta ? stableStringify(meta) : undefined,
|
|
364
|
-
} satisfies Transaction;
|
|
365
|
-
const signature = this.internalAddNewTransaction(
|
|
366
|
-
stableStringify(tx),
|
|
367
|
-
signerAgent,
|
|
368
|
-
);
|
|
369
|
-
return {
|
|
370
|
-
signature: signature as Signature,
|
|
371
|
-
transaction: tx,
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
decryptNextTransactionChangesJson(
|
|
376
|
-
txIndex: number,
|
|
377
|
-
keySecret: KeySecret,
|
|
378
|
-
): string {
|
|
379
|
-
const txJson = this.transactions[txIndex];
|
|
380
|
-
if (!txJson) {
|
|
381
|
-
throw new Error("Transaction not found");
|
|
382
|
-
}
|
|
383
|
-
const tx = JSON.parse(txJson) as Transaction;
|
|
384
|
-
if (tx.privacy === "private") {
|
|
385
|
-
const nOnceMaterial = {
|
|
386
|
-
in: this.coID,
|
|
387
|
-
tx: { sessionID: this.sessionID, txIndex: txIndex },
|
|
388
|
-
};
|
|
389
|
-
|
|
390
|
-
return this.crypto.decryptRaw(
|
|
391
|
-
tx.encryptedChanges,
|
|
392
|
-
keySecret,
|
|
393
|
-
nOnceMaterial,
|
|
394
|
-
);
|
|
395
|
-
} else {
|
|
396
|
-
return tx.changes;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
decryptNextTransactionMetaJson(
|
|
401
|
-
txIndex: number,
|
|
402
|
-
keySecret: KeySecret,
|
|
403
|
-
): string | undefined {
|
|
404
|
-
const txJson = this.transactions[txIndex];
|
|
405
|
-
if (!txJson) {
|
|
406
|
-
throw new Error("Transaction not found");
|
|
407
|
-
}
|
|
408
|
-
const tx = JSON.parse(txJson) as Transaction;
|
|
409
|
-
|
|
410
|
-
if (!tx.meta) {
|
|
411
|
-
return undefined;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
if (tx.privacy === "private") {
|
|
415
|
-
const nOnceMaterial = {
|
|
416
|
-
in: this.coID,
|
|
417
|
-
tx: { sessionID: this.sessionID, txIndex: txIndex },
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
return this.crypto.decryptRaw(tx.meta, keySecret, nOnceMaterial);
|
|
421
|
-
} else {
|
|
422
|
-
return tx.meta;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
free(): void {
|
|
427
|
-
// no-op
|
|
428
|
-
}
|
|
429
|
-
}
|
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { assert, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
loadCoValueOrFail,
|
|
4
|
-
setCurrentTestCryptoProvider,
|
|
5
|
-
setupTestNode,
|
|
6
|
-
setupTestAccount,
|
|
7
|
-
} from "./testUtils";
|
|
8
|
-
import { PureJSCrypto } from "../crypto/PureJSCrypto";
|
|
9
|
-
import { stableStringify } from "../jsonStringify";
|
|
10
|
-
|
|
11
|
-
const jsCrypto = await PureJSCrypto.create();
|
|
12
|
-
setCurrentTestCryptoProvider(jsCrypto);
|
|
13
|
-
|
|
14
|
-
let syncServer: ReturnType<typeof setupTestNode>;
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
syncServer = setupTestNode({ isSyncServer: true });
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
// A suite of tests focused on high-level tests that verify:
|
|
21
|
-
// - Keys creation and unsealing
|
|
22
|
-
// - Signature creation and verification
|
|
23
|
-
// - Encryption and decryption of values
|
|
24
|
-
describe("PureJSCrypto", () => {
|
|
25
|
-
it("successfully creates a private CoValue and reads it in another session", async () => {
|
|
26
|
-
const client = setupTestNode({
|
|
27
|
-
connected: true,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const group = client.node.createGroup();
|
|
31
|
-
const map = group.createMap();
|
|
32
|
-
map.set("count", 0, "private");
|
|
33
|
-
map.set("count", 1, "private");
|
|
34
|
-
map.set("count", 2, "private");
|
|
35
|
-
|
|
36
|
-
const client2 = client.spawnNewSession();
|
|
37
|
-
|
|
38
|
-
const mapInTheOtherSession = await loadCoValueOrFail(client2.node, map.id);
|
|
39
|
-
|
|
40
|
-
expect(mapInTheOtherSession.get("count")).toEqual(2);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("successfully updates a private CoValue and reads it in another session", async () => {
|
|
44
|
-
const client = setupTestNode({
|
|
45
|
-
connected: true,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const group = client.node.createGroup();
|
|
49
|
-
const map = group.createMap();
|
|
50
|
-
map.set("count", 0, "private");
|
|
51
|
-
map.set("count", 1, "private");
|
|
52
|
-
map.set("count", 2, "private");
|
|
53
|
-
|
|
54
|
-
const client2 = client.spawnNewSession();
|
|
55
|
-
|
|
56
|
-
const mapInTheOtherSession = await loadCoValueOrFail(client2.node, map.id);
|
|
57
|
-
mapInTheOtherSession.set("count", 3, "private");
|
|
58
|
-
|
|
59
|
-
await mapInTheOtherSession.core.waitForSync();
|
|
60
|
-
|
|
61
|
-
expect(mapInTheOtherSession.get("count")).toEqual(3);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("can invite another account to a group and share a private CoValue", async () => {
|
|
65
|
-
const client = setupTestNode({
|
|
66
|
-
connected: true,
|
|
67
|
-
});
|
|
68
|
-
const account = await setupTestAccount({
|
|
69
|
-
connected: true,
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const group = client.node.createGroup();
|
|
73
|
-
const invite = group.createInvite("admin");
|
|
74
|
-
|
|
75
|
-
await account.node.acceptInvite(group.id, invite);
|
|
76
|
-
|
|
77
|
-
const map = group.createMap();
|
|
78
|
-
map.set("secret", "private-data", "private");
|
|
79
|
-
|
|
80
|
-
// The other account should be able to read the private value
|
|
81
|
-
const mapInOtherSession = await loadCoValueOrFail(account.node, map.id);
|
|
82
|
-
expect(mapInOtherSession.get("secret")).toEqual("private-data");
|
|
83
|
-
|
|
84
|
-
mapInOtherSession.set("secret", "modified", "private");
|
|
85
|
-
|
|
86
|
-
await mapInOtherSession.core.waitForSync();
|
|
87
|
-
|
|
88
|
-
expect(map.get("secret")).toEqual("modified");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("rejects sessions with invalid signatures", async () => {
|
|
92
|
-
const client = setupTestNode({
|
|
93
|
-
connected: true,
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const group = client.node.createGroup();
|
|
97
|
-
const map = group.createMap();
|
|
98
|
-
map.set("count", 0, "trusting");
|
|
99
|
-
|
|
100
|
-
// Create a new session with the same agent
|
|
101
|
-
const client2 = client.spawnNewSession();
|
|
102
|
-
|
|
103
|
-
// This should work normally
|
|
104
|
-
const mapInOtherSession = await loadCoValueOrFail(client2.node, map.id);
|
|
105
|
-
expect(mapInOtherSession.get("count")).toEqual(0);
|
|
106
|
-
|
|
107
|
-
mapInOtherSession.core.tryAddTransactions(
|
|
108
|
-
client2.node.currentSessionID,
|
|
109
|
-
[
|
|
110
|
-
{
|
|
111
|
-
privacy: "trusting",
|
|
112
|
-
changes: stableStringify([{ op: "set", key: "count", value: 1 }]),
|
|
113
|
-
madeAt: Date.now(),
|
|
114
|
-
},
|
|
115
|
-
],
|
|
116
|
-
"signature_z12345678",
|
|
117
|
-
true,
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
const content = mapInOtherSession.core.newContentSince(undefined)?.[0];
|
|
121
|
-
assert(content);
|
|
122
|
-
|
|
123
|
-
client.node.syncManager.handleNewContent(content, "storage");
|
|
124
|
-
|
|
125
|
-
expect(map.get("count")).toEqual(0);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("can add a meta to a private transaction", async () => {
|
|
129
|
-
const client = setupTestNode({
|
|
130
|
-
connected: true,
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
const group = client.node.createGroup();
|
|
134
|
-
const map = group.createMap();
|
|
135
|
-
|
|
136
|
-
map.core.makeTransaction([], "private", {
|
|
137
|
-
meta: {
|
|
138
|
-
count: 1,
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
await map.core.waitForSync();
|
|
143
|
-
|
|
144
|
-
const session2 = client.spawnNewSession();
|
|
145
|
-
|
|
146
|
-
const mapInOtherSession = await loadCoValueOrFail(session2.node, map.id);
|
|
147
|
-
|
|
148
|
-
const decryptedMeta =
|
|
149
|
-
mapInOtherSession.core.verified.decryptTransactionMeta(
|
|
150
|
-
client.node.currentSessionID,
|
|
151
|
-
0,
|
|
152
|
-
map.core.getCurrentReadKey().secret!,
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
expect(decryptedMeta).toEqual({
|
|
156
|
-
meta: {
|
|
157
|
-
count: 1,
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it("can add a meta to a trusting transaction", async () => {
|
|
163
|
-
const client = setupTestNode({
|
|
164
|
-
connected: true,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
const group = client.node.createGroup();
|
|
168
|
-
const map = group.createMap();
|
|
169
|
-
|
|
170
|
-
map.core.makeTransaction([], "trusting", {
|
|
171
|
-
meta: {
|
|
172
|
-
count: 1,
|
|
173
|
-
},
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
await map.core.waitForSync();
|
|
177
|
-
|
|
178
|
-
const session2 = client.spawnNewSession();
|
|
179
|
-
|
|
180
|
-
const mapInOtherSession = await loadCoValueOrFail(session2.node, map.id);
|
|
181
|
-
|
|
182
|
-
const transferredMeta = JSON.parse(
|
|
183
|
-
mapInOtherSession.core.verified.sessions.get(client.node.currentSessionID)
|
|
184
|
-
?.transactions[0]?.meta!,
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
expect(transferredMeta).toEqual({
|
|
188
|
-
meta: {
|
|
189
|
-
count: 1,
|
|
190
|
-
},
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
describe("PureJSSessionLog", () => {
|
|
196
|
-
it("fails to verify signatures without a signer ID", async () => {
|
|
197
|
-
const agentSecret = jsCrypto.newRandomAgentSecret();
|
|
198
|
-
const sessionID = jsCrypto.newRandomSessionID(
|
|
199
|
-
jsCrypto.getAgentID(agentSecret),
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
const sessionLog = jsCrypto.createSessionLog("co_z12345678", sessionID);
|
|
203
|
-
expect(() =>
|
|
204
|
-
sessionLog.tryAdd(
|
|
205
|
-
[
|
|
206
|
-
{
|
|
207
|
-
privacy: "trusting",
|
|
208
|
-
changes: stableStringify([{ op: "set", key: "count", value: 1 }]),
|
|
209
|
-
madeAt: Date.now(),
|
|
210
|
-
},
|
|
211
|
-
],
|
|
212
|
-
"signature_z12345678",
|
|
213
|
-
false,
|
|
214
|
-
),
|
|
215
|
-
).toThrow("Tried to add transactions without signer ID");
|
|
216
|
-
});
|
|
217
|
-
});
|