cojson 0.8.12 → 0.8.17

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.
Files changed (164) hide show
  1. package/CHANGELOG.md +95 -83
  2. package/dist/native/PeerKnownStates.js +6 -1
  3. package/dist/native/PeerKnownStates.js.map +1 -1
  4. package/dist/native/PeerState.js +4 -3
  5. package/dist/native/PeerState.js.map +1 -1
  6. package/dist/native/PriorityBasedMessageQueue.js +1 -10
  7. package/dist/native/PriorityBasedMessageQueue.js.map +1 -1
  8. package/dist/native/SyncStateSubscriptionManager.js +70 -0
  9. package/dist/native/SyncStateSubscriptionManager.js.map +1 -0
  10. package/dist/native/base64url.js.map +1 -1
  11. package/dist/native/base64url.test.js +1 -1
  12. package/dist/native/base64url.test.js.map +1 -1
  13. package/dist/native/coValue.js.map +1 -1
  14. package/dist/native/coValueCore.js +141 -149
  15. package/dist/native/coValueCore.js.map +1 -1
  16. package/dist/native/coValueState.js.map +1 -1
  17. package/dist/native/coValues/account.js +6 -6
  18. package/dist/native/coValues/account.js.map +1 -1
  19. package/dist/native/coValues/coList.js +2 -3
  20. package/dist/native/coValues/coList.js.map +1 -1
  21. package/dist/native/coValues/coMap.js +1 -1
  22. package/dist/native/coValues/coMap.js.map +1 -1
  23. package/dist/native/coValues/coStream.js +3 -5
  24. package/dist/native/coValues/coStream.js.map +1 -1
  25. package/dist/native/coValues/group.js +11 -11
  26. package/dist/native/coValues/group.js.map +1 -1
  27. package/dist/native/coreToCoValue.js +2 -2
  28. package/dist/native/coreToCoValue.js.map +1 -1
  29. package/dist/native/crypto/PureJSCrypto.js +4 -4
  30. package/dist/native/crypto/PureJSCrypto.js.map +1 -1
  31. package/dist/native/crypto/crypto.js.map +1 -1
  32. package/dist/native/exports.js +12 -12
  33. package/dist/native/exports.js.map +1 -1
  34. package/dist/native/ids.js.map +1 -1
  35. package/dist/native/jsonStringify.js.map +1 -1
  36. package/dist/native/localNode.js +5 -7
  37. package/dist/native/localNode.js.map +1 -1
  38. package/dist/native/permissions.js +4 -7
  39. package/dist/native/permissions.js.map +1 -1
  40. package/dist/native/priority.js.map +1 -1
  41. package/dist/native/storage/FileSystem.js.map +1 -1
  42. package/dist/native/storage/chunksAndKnownStates.js +2 -4
  43. package/dist/native/storage/chunksAndKnownStates.js.map +1 -1
  44. package/dist/native/storage/index.js +6 -15
  45. package/dist/native/storage/index.js.map +1 -1
  46. package/dist/native/streamUtils.js.map +1 -1
  47. package/dist/native/sync.js +57 -7
  48. package/dist/native/sync.js.map +1 -1
  49. package/dist/native/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
  50. package/dist/native/typeUtils/expectGroup.js.map +1 -1
  51. package/dist/native/typeUtils/isAccountID.js.map +1 -1
  52. package/dist/native/typeUtils/isCoValue.js +1 -1
  53. package/dist/native/typeUtils/isCoValue.js.map +1 -1
  54. package/dist/web/PeerKnownStates.js +6 -1
  55. package/dist/web/PeerKnownStates.js.map +1 -1
  56. package/dist/web/PeerState.js +4 -3
  57. package/dist/web/PeerState.js.map +1 -1
  58. package/dist/web/PriorityBasedMessageQueue.js +1 -10
  59. package/dist/web/PriorityBasedMessageQueue.js.map +1 -1
  60. package/dist/web/SyncStateSubscriptionManager.js +70 -0
  61. package/dist/web/SyncStateSubscriptionManager.js.map +1 -0
  62. package/dist/web/base64url.js.map +1 -1
  63. package/dist/web/base64url.test.js +1 -1
  64. package/dist/web/base64url.test.js.map +1 -1
  65. package/dist/web/coValue.js.map +1 -1
  66. package/dist/web/coValueCore.js +141 -149
  67. package/dist/web/coValueCore.js.map +1 -1
  68. package/dist/web/coValueState.js.map +1 -1
  69. package/dist/web/coValues/account.js +6 -6
  70. package/dist/web/coValues/account.js.map +1 -1
  71. package/dist/web/coValues/coList.js +2 -3
  72. package/dist/web/coValues/coList.js.map +1 -1
  73. package/dist/web/coValues/coMap.js +1 -1
  74. package/dist/web/coValues/coMap.js.map +1 -1
  75. package/dist/web/coValues/coStream.js +3 -5
  76. package/dist/web/coValues/coStream.js.map +1 -1
  77. package/dist/web/coValues/group.js +11 -11
  78. package/dist/web/coValues/group.js.map +1 -1
  79. package/dist/web/coreToCoValue.js +2 -2
  80. package/dist/web/coreToCoValue.js.map +1 -1
  81. package/dist/web/crypto/PureJSCrypto.js +4 -4
  82. package/dist/web/crypto/PureJSCrypto.js.map +1 -1
  83. package/dist/web/crypto/WasmCrypto.js +5 -5
  84. package/dist/web/crypto/WasmCrypto.js.map +1 -1
  85. package/dist/web/crypto/crypto.js.map +1 -1
  86. package/dist/web/exports.js +12 -12
  87. package/dist/web/exports.js.map +1 -1
  88. package/dist/web/ids.js.map +1 -1
  89. package/dist/web/jsonStringify.js.map +1 -1
  90. package/dist/web/localNode.js +5 -7
  91. package/dist/web/localNode.js.map +1 -1
  92. package/dist/web/permissions.js +4 -7
  93. package/dist/web/permissions.js.map +1 -1
  94. package/dist/web/priority.js.map +1 -1
  95. package/dist/web/storage/FileSystem.js.map +1 -1
  96. package/dist/web/storage/chunksAndKnownStates.js +2 -4
  97. package/dist/web/storage/chunksAndKnownStates.js.map +1 -1
  98. package/dist/web/storage/index.js +6 -15
  99. package/dist/web/storage/index.js.map +1 -1
  100. package/dist/web/streamUtils.js.map +1 -1
  101. package/dist/web/sync.js +57 -7
  102. package/dist/web/sync.js.map +1 -1
  103. package/dist/web/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
  104. package/dist/web/typeUtils/expectGroup.js.map +1 -1
  105. package/dist/web/typeUtils/isAccountID.js.map +1 -1
  106. package/dist/web/typeUtils/isCoValue.js +1 -1
  107. package/dist/web/typeUtils/isCoValue.js.map +1 -1
  108. package/package.json +4 -14
  109. package/src/PeerKnownStates.ts +98 -90
  110. package/src/PeerState.ts +92 -73
  111. package/src/PriorityBasedMessageQueue.ts +42 -49
  112. package/src/SyncStateSubscriptionManager.ts +124 -0
  113. package/src/base64url.test.ts +24 -24
  114. package/src/base64url.ts +44 -45
  115. package/src/coValue.ts +45 -45
  116. package/src/coValueCore.ts +746 -785
  117. package/src/coValueState.ts +82 -72
  118. package/src/coValues/account.ts +143 -150
  119. package/src/coValues/coList.ts +520 -522
  120. package/src/coValues/coMap.ts +283 -285
  121. package/src/coValues/coStream.ts +320 -324
  122. package/src/coValues/group.ts +306 -305
  123. package/src/coreToCoValue.ts +28 -31
  124. package/src/crypto/PureJSCrypto.ts +188 -194
  125. package/src/crypto/WasmCrypto.ts +236 -254
  126. package/src/crypto/crypto.ts +302 -309
  127. package/src/exports.ts +116 -116
  128. package/src/ids.ts +9 -9
  129. package/src/jsonStringify.ts +46 -46
  130. package/src/jsonValue.ts +24 -10
  131. package/src/localNode.ts +635 -660
  132. package/src/media.ts +3 -3
  133. package/src/permissions.ts +272 -278
  134. package/src/priority.ts +21 -19
  135. package/src/storage/FileSystem.ts +91 -99
  136. package/src/storage/chunksAndKnownStates.ts +110 -115
  137. package/src/storage/index.ts +466 -497
  138. package/src/streamUtils.ts +60 -60
  139. package/src/sync.ts +656 -608
  140. package/src/tests/PeerKnownStates.test.ts +38 -34
  141. package/src/tests/PeerState.test.ts +101 -64
  142. package/src/tests/PriorityBasedMessageQueue.test.ts +91 -91
  143. package/src/tests/SyncStateSubscriptionManager.test.ts +232 -0
  144. package/src/tests/account.test.ts +59 -59
  145. package/src/tests/coList.test.ts +65 -65
  146. package/src/tests/coMap.test.ts +137 -137
  147. package/src/tests/coStream.test.ts +254 -257
  148. package/src/tests/coValueCore.test.ts +153 -156
  149. package/src/tests/crypto.test.ts +136 -144
  150. package/src/tests/cryptoImpl.test.ts +205 -197
  151. package/src/tests/group.test.ts +24 -24
  152. package/src/tests/permissions.test.ts +1306 -1371
  153. package/src/tests/priority.test.ts +65 -82
  154. package/src/tests/sync.test.ts +1573 -1263
  155. package/src/tests/testUtils.ts +85 -53
  156. package/src/typeUtils/accountOrAgentIDfromSessionID.ts +4 -4
  157. package/src/typeUtils/expectGroup.ts +9 -9
  158. package/src/typeUtils/isAccountID.ts +1 -1
  159. package/src/typeUtils/isCoValue.ts +9 -9
  160. package/tsconfig.json +4 -6
  161. package/tsconfig.native.json +9 -11
  162. package/tsconfig.web.json +4 -10
  163. package/.eslintrc.cjs +0 -25
  164. package/.prettierrc.js +0 -9
@@ -1,44 +1,41 @@
1
1
  import type { CoValueCore } from "./coValueCore.js";
2
2
  import { RawAccount, RawControlledAccount } from "./coValues/account.js";
3
- import { RawGroup } from "./coValues/group.js";
4
- import { RawCoMap } from "./coValues/coMap.js";
5
3
  import { RawCoList } from "./coValues/coList.js";
4
+ import { RawCoMap } from "./coValues/coMap.js";
6
5
  import { RawCoStream } from "./coValues/coStream.js";
7
6
  import { RawBinaryCoStream } from "./coValues/coStream.js";
7
+ import { RawGroup } from "./coValues/group.js";
8
8
 
9
9
  export function coreToCoValue(
10
- core: CoValueCore,
11
- options?: { ignorePrivateTransactions: true },
10
+ core: CoValueCore,
11
+ options?: { ignorePrivateTransactions: true },
12
12
  ) {
13
- if (core.header.type === "comap") {
14
- if (core.header.ruleset.type === "group") {
15
- if (
16
- core.header.meta?.type === "account" &&
17
- !options?.ignorePrivateTransactions
18
- ) {
19
- if (core.id === core.node.account.id) {
20
- return new RawControlledAccount(
21
- core,
22
- core.node.account.agentSecret,
23
- );
24
- } else {
25
- return new RawAccount(core);
26
- }
27
- } else {
28
- return new RawGroup(core, options);
29
- }
13
+ if (core.header.type === "comap") {
14
+ if (core.header.ruleset.type === "group") {
15
+ if (
16
+ core.header.meta?.type === "account" &&
17
+ !options?.ignorePrivateTransactions
18
+ ) {
19
+ if (core.id === core.node.account.id) {
20
+ return new RawControlledAccount(core, core.node.account.agentSecret);
30
21
  } else {
31
- return new RawCoMap(core);
32
- }
33
- } else if (core.header.type === "colist") {
34
- return new RawCoList(core);
35
- } else if (core.header.type === "costream") {
36
- if (core.header.meta && core.header.meta.type === "binary") {
37
- return new RawBinaryCoStream(core);
38
- } else {
39
- return new RawCoStream(core);
22
+ return new RawAccount(core);
40
23
  }
24
+ } else {
25
+ return new RawGroup(core, options);
26
+ }
27
+ } else {
28
+ return new RawCoMap(core);
29
+ }
30
+ } else if (core.header.type === "colist") {
31
+ return new RawCoList(core);
32
+ } else if (core.header.type === "costream") {
33
+ if (core.header.meta && core.header.meta.type === "binary") {
34
+ return new RawBinaryCoStream(core);
41
35
  } else {
42
- throw new Error(`Unknown coValue type ${core.header.type}`);
36
+ return new RawCoStream(core);
43
37
  }
38
+ } else {
39
+ throw new Error(`Unknown coValue type ${core.header.type}`);
40
+ }
44
41
  }
@@ -1,205 +1,199 @@
1
+ import { xsalsa20, xsalsa20_poly1305 } from "@noble/ciphers/salsa";
2
+ import { randomBytes } from "@noble/ciphers/webcrypto/utils";
1
3
  import { ed25519, x25519 } from "@noble/curves/ed25519";
2
- import { xsalsa20_poly1305, xsalsa20 } from "@noble/ciphers/salsa";
3
- import { JsonValue } from "../jsonValue.js";
4
+ import { blake3 } from "@noble/hashes/blake3";
4
5
  import { base58 } from "@scure/base";
5
- import { randomBytes } from "@noble/ciphers/webcrypto/utils";
6
- import { RawCoID, TransactionID } from "../ids.js";
7
6
  import { base64URLtoBytes, bytesToBase64url } from "../base64url.js";
7
+ import { RawCoID, TransactionID } from "../ids.js";
8
8
  import { Stringified, stableStringify } from "../jsonStringify.js";
9
- import { blake3 } from "@noble/hashes/blake3";
9
+ import { JsonValue } from "../jsonValue.js";
10
10
  import {
11
- CryptoProvider,
12
- SignerSecret,
13
- SignerID,
14
- Signature,
15
- textEncoder,
16
- SealerSecret,
17
- SealerID,
18
- KeySecret,
19
- Encrypted,
20
- textDecoder,
21
- Sealed,
11
+ CryptoProvider,
12
+ Encrypted,
13
+ KeySecret,
14
+ Sealed,
15
+ SealerID,
16
+ SealerSecret,
17
+ Signature,
18
+ SignerID,
19
+ SignerSecret,
20
+ textDecoder,
21
+ textEncoder,
22
22
  } from "./crypto.js";
23
23
 
24
24
  type Blake3State = ReturnType<typeof blake3.create>;
25
25
 
26
26
  export class PureJSCrypto extends CryptoProvider<Blake3State> {
27
- static async create(): Promise<PureJSCrypto> {
28
- return new PureJSCrypto();
29
- }
30
-
31
- randomBytes(length: number): Uint8Array {
32
- return randomBytes(length);
33
- }
34
-
35
- emptyBlake3State(): Blake3State {
36
- return blake3.create({});
37
- }
38
-
39
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
- cloneBlake3State(state: any): Blake3State {
41
- return state.clone();
42
- }
43
-
44
- blake3HashOnce(data: Uint8Array) {
45
- return blake3(data);
46
- }
47
-
48
- blake3HashOnceWithContext(
49
- data: Uint8Array,
50
- { context }: { context: Uint8Array },
51
- ) {
52
- return blake3.create({}).update(context).update(data).digest();
53
- }
54
-
55
- blake3IncrementalUpdate(state: Blake3State, data: Uint8Array) {
56
- return state.update(data);
57
- }
58
-
59
- blake3DigestForState(state: Blake3State): Uint8Array {
60
- return state.clone().digest();
61
- }
62
-
63
- newEd25519SigningKey(): Uint8Array {
64
- return ed25519.utils.randomPrivateKey();
65
- }
66
-
67
- getSignerID(secret: SignerSecret): SignerID {
68
- return `signer_z${base58.encode(
69
- ed25519.getPublicKey(
70
- base58.decode(secret.substring("signerSecret_z".length)),
71
- ),
72
- )}`;
73
- }
74
-
75
- sign(secret: SignerSecret, message: JsonValue): Signature {
76
- const signature = ed25519.sign(
77
- textEncoder.encode(stableStringify(message)),
78
- base58.decode(secret.substring("signerSecret_z".length)),
79
- );
80
- return `signature_z${base58.encode(signature)}`;
81
- }
82
-
83
- verify(signature: Signature, message: JsonValue, id: SignerID): boolean {
84
- return ed25519.verify(
85
- base58.decode(signature.substring("signature_z".length)),
86
- textEncoder.encode(stableStringify(message)),
87
- base58.decode(id.substring("signer_z".length)),
88
- );
89
- }
90
-
91
- newX25519StaticSecret(): Uint8Array {
92
- return x25519.utils.randomPrivateKey();
93
- }
94
-
95
- getSealerID(secret: SealerSecret): SealerID {
96
- return `sealer_z${base58.encode(
97
- x25519.getPublicKey(
98
- base58.decode(secret.substring("sealerSecret_z".length)),
99
- ),
100
- )}`;
101
- }
102
-
103
- encrypt<T extends JsonValue, N extends JsonValue>(
104
- value: T,
105
- keySecret: KeySecret,
106
- nOnceMaterial: N,
107
- ): Encrypted<T, N> {
108
- const keySecretBytes = base58.decode(
109
- keySecret.substring("keySecret_z".length),
110
- );
111
- const nOnce = this.blake3HashOnce(
112
- textEncoder.encode(stableStringify(nOnceMaterial)),
113
- ).slice(0, 24);
114
-
115
- const plaintext = textEncoder.encode(stableStringify(value));
116
- const ciphertext = xsalsa20(keySecretBytes, nOnce, plaintext);
117
- return `encrypted_U${bytesToBase64url(ciphertext)}` as Encrypted<T, N>;
118
- }
119
-
120
- decryptRaw<T extends JsonValue, N extends JsonValue>(
121
- encrypted: Encrypted<T, N>,
122
- keySecret: KeySecret,
123
- nOnceMaterial: N,
124
- ): Stringified<T> {
125
- const keySecretBytes = base58.decode(
126
- keySecret.substring("keySecret_z".length),
127
- );
128
- const nOnce = this.blake3HashOnce(
129
- textEncoder.encode(stableStringify(nOnceMaterial)),
130
- ).slice(0, 24);
131
-
132
- const ciphertext = base64URLtoBytes(
133
- encrypted.substring("encrypted_U".length),
134
- );
135
- const plaintext = xsalsa20(keySecretBytes, nOnce, ciphertext);
136
-
137
- return textDecoder.decode(plaintext) as Stringified<T>;
138
- }
139
-
140
- seal<T extends JsonValue>({
141
- message,
142
- from,
143
- to,
144
- nOnceMaterial,
145
- }: {
146
- message: T;
147
- from: SealerSecret;
148
- to: SealerID;
149
- nOnceMaterial: { in: RawCoID; tx: TransactionID };
150
- }): Sealed<T> {
151
- const nOnce = this.blake3HashOnce(
152
- textEncoder.encode(stableStringify(nOnceMaterial)),
153
- ).slice(0, 24);
154
-
155
- const sealerPub = base58.decode(to.substring("sealer_z".length));
156
-
157
- const senderPriv = base58.decode(
158
- from.substring("sealerSecret_z".length),
159
- );
160
-
161
- const plaintext = textEncoder.encode(stableStringify(message));
162
-
163
- const sharedSecret = x25519.getSharedSecret(senderPriv, sealerPub);
164
-
165
- const sealedBytes = xsalsa20_poly1305(sharedSecret, nOnce).encrypt(
166
- plaintext,
167
- );
168
-
169
- return `sealed_U${bytesToBase64url(sealedBytes)}` as Sealed<T>;
170
- }
171
-
172
- unseal<T extends JsonValue>(
173
- sealed: Sealed<T>,
174
- sealer: SealerSecret,
175
- from: SealerID,
176
- nOnceMaterial: { in: RawCoID; tx: TransactionID },
177
- ): T | undefined {
178
- const nOnce = this.blake3HashOnce(
179
- textEncoder.encode(stableStringify(nOnceMaterial)),
180
- ).slice(0, 24);
181
-
182
- const sealerPriv = base58.decode(
183
- sealer.substring("sealerSecret_z".length),
184
- );
185
-
186
- const senderPub = base58.decode(from.substring("sealer_z".length));
187
-
188
- const sealedBytes = base64URLtoBytes(
189
- sealed.substring("sealed_U".length),
190
- );
191
-
192
- const sharedSecret = x25519.getSharedSecret(sealerPriv, senderPub);
193
-
194
- const plaintext = xsalsa20_poly1305(sharedSecret, nOnce).decrypt(
195
- sealedBytes,
196
- );
197
-
198
- try {
199
- return JSON.parse(textDecoder.decode(plaintext));
200
- } catch (e) {
201
- console.error("Failed to decrypt/parse sealed message", e);
202
- return undefined;
203
- }
204
- }
27
+ static async create(): Promise<PureJSCrypto> {
28
+ return new PureJSCrypto();
29
+ }
30
+
31
+ randomBytes(length: number): Uint8Array {
32
+ return randomBytes(length);
33
+ }
34
+
35
+ emptyBlake3State(): Blake3State {
36
+ return blake3.create({});
37
+ }
38
+
39
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
+ cloneBlake3State(state: any): Blake3State {
41
+ return state.clone();
42
+ }
43
+
44
+ blake3HashOnce(data: Uint8Array) {
45
+ return blake3(data);
46
+ }
47
+
48
+ blake3HashOnceWithContext(
49
+ data: Uint8Array,
50
+ { context }: { context: Uint8Array },
51
+ ) {
52
+ return blake3.create({}).update(context).update(data).digest();
53
+ }
54
+
55
+ blake3IncrementalUpdate(state: Blake3State, data: Uint8Array) {
56
+ return state.update(data);
57
+ }
58
+
59
+ blake3DigestForState(state: Blake3State): Uint8Array {
60
+ return state.clone().digest();
61
+ }
62
+
63
+ newEd25519SigningKey(): Uint8Array {
64
+ return ed25519.utils.randomPrivateKey();
65
+ }
66
+
67
+ getSignerID(secret: SignerSecret): SignerID {
68
+ return `signer_z${base58.encode(
69
+ ed25519.getPublicKey(
70
+ base58.decode(secret.substring("signerSecret_z".length)),
71
+ ),
72
+ )}`;
73
+ }
74
+
75
+ sign(secret: SignerSecret, message: JsonValue): Signature {
76
+ const signature = ed25519.sign(
77
+ textEncoder.encode(stableStringify(message)),
78
+ base58.decode(secret.substring("signerSecret_z".length)),
79
+ );
80
+ return `signature_z${base58.encode(signature)}`;
81
+ }
82
+
83
+ verify(signature: Signature, message: JsonValue, id: SignerID): boolean {
84
+ return ed25519.verify(
85
+ base58.decode(signature.substring("signature_z".length)),
86
+ textEncoder.encode(stableStringify(message)),
87
+ base58.decode(id.substring("signer_z".length)),
88
+ );
89
+ }
90
+
91
+ newX25519StaticSecret(): Uint8Array {
92
+ return x25519.utils.randomPrivateKey();
93
+ }
94
+
95
+ getSealerID(secret: SealerSecret): SealerID {
96
+ return `sealer_z${base58.encode(
97
+ x25519.getPublicKey(
98
+ base58.decode(secret.substring("sealerSecret_z".length)),
99
+ ),
100
+ )}`;
101
+ }
102
+
103
+ encrypt<T extends JsonValue, N extends JsonValue>(
104
+ value: T,
105
+ keySecret: KeySecret,
106
+ nOnceMaterial: N,
107
+ ): Encrypted<T, N> {
108
+ const keySecretBytes = base58.decode(
109
+ keySecret.substring("keySecret_z".length),
110
+ );
111
+ const nOnce = this.blake3HashOnce(
112
+ textEncoder.encode(stableStringify(nOnceMaterial)),
113
+ ).slice(0, 24);
114
+
115
+ const plaintext = textEncoder.encode(stableStringify(value));
116
+ const ciphertext = xsalsa20(keySecretBytes, nOnce, plaintext);
117
+ return `encrypted_U${bytesToBase64url(ciphertext)}` as Encrypted<T, N>;
118
+ }
119
+
120
+ decryptRaw<T extends JsonValue, N extends JsonValue>(
121
+ encrypted: Encrypted<T, N>,
122
+ keySecret: KeySecret,
123
+ nOnceMaterial: N,
124
+ ): Stringified<T> {
125
+ const keySecretBytes = base58.decode(
126
+ keySecret.substring("keySecret_z".length),
127
+ );
128
+ const nOnce = this.blake3HashOnce(
129
+ textEncoder.encode(stableStringify(nOnceMaterial)),
130
+ ).slice(0, 24);
131
+
132
+ const ciphertext = base64URLtoBytes(
133
+ encrypted.substring("encrypted_U".length),
134
+ );
135
+ const plaintext = xsalsa20(keySecretBytes, nOnce, ciphertext);
136
+
137
+ return textDecoder.decode(plaintext) as Stringified<T>;
138
+ }
139
+
140
+ seal<T extends JsonValue>({
141
+ message,
142
+ from,
143
+ to,
144
+ nOnceMaterial,
145
+ }: {
146
+ message: T;
147
+ from: SealerSecret;
148
+ to: SealerID;
149
+ nOnceMaterial: { in: RawCoID; tx: TransactionID };
150
+ }): Sealed<T> {
151
+ const nOnce = this.blake3HashOnce(
152
+ textEncoder.encode(stableStringify(nOnceMaterial)),
153
+ ).slice(0, 24);
154
+
155
+ const sealerPub = base58.decode(to.substring("sealer_z".length));
156
+
157
+ const senderPriv = base58.decode(from.substring("sealerSecret_z".length));
158
+
159
+ const plaintext = textEncoder.encode(stableStringify(message));
160
+
161
+ const sharedSecret = x25519.getSharedSecret(senderPriv, sealerPub);
162
+
163
+ const sealedBytes = xsalsa20_poly1305(sharedSecret, nOnce).encrypt(
164
+ plaintext,
165
+ );
166
+
167
+ return `sealed_U${bytesToBase64url(sealedBytes)}` as Sealed<T>;
168
+ }
169
+
170
+ unseal<T extends JsonValue>(
171
+ sealed: Sealed<T>,
172
+ sealer: SealerSecret,
173
+ from: SealerID,
174
+ nOnceMaterial: { in: RawCoID; tx: TransactionID },
175
+ ): T | undefined {
176
+ const nOnce = this.blake3HashOnce(
177
+ textEncoder.encode(stableStringify(nOnceMaterial)),
178
+ ).slice(0, 24);
179
+
180
+ const sealerPriv = base58.decode(sealer.substring("sealerSecret_z".length));
181
+
182
+ const senderPub = base58.decode(from.substring("sealer_z".length));
183
+
184
+ const sealedBytes = base64URLtoBytes(sealed.substring("sealed_U".length));
185
+
186
+ const sharedSecret = x25519.getSharedSecret(sealerPriv, senderPub);
187
+
188
+ const plaintext = xsalsa20_poly1305(sharedSecret, nOnce).decrypt(
189
+ sealedBytes,
190
+ );
191
+
192
+ try {
193
+ return JSON.parse(textDecoder.decode(plaintext));
194
+ } catch (e) {
195
+ console.error("Failed to decrypt/parse sealed message", e);
196
+ return undefined;
197
+ }
198
+ }
205
199
  }