@cello-protocol/crypto 0.0.58 → 0.0.60

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.
@@ -0,0 +1,129 @@
1
+ /**
2
+ * THE SESSION SALT — Decisions Carried #8, #9, #10 (Andre, 2026-08-23).
3
+ *
4
+ * ⚠️ AGREED, STORED, AND VERIFIABLE — BUT NO SENDER SALTS YET. Two revisions of this header have
5
+ * now been overtaken, so it states the boundary precisely rather than a slogan:
6
+ *
7
+ * `session-salt-agreement.ts` calls `deriveSessionSalt` and `saltFingerprint`; `content_salt` has
8
+ * a writer and a reader; and `wire-content-hash.ts` DOES import `saltedContentHash` — the RECEIVE
9
+ * path will verify a frame that names `hmac-sha256-salt-v1` (`DOD-M15-SEALWIRE-1` part B1).
10
+ *
11
+ * What no code in this build does is SEND one. Every send site still calls `wireContentHash`, so
12
+ * no message's hash depends on a salt yet, and part B2 is what flips that.
13
+ *
14
+ * The distinction matters to anyone auditing reachability: the salted path is live inbound and dead
15
+ * outbound. A reader who takes "nothing hashes with it" from an older revision of this header would
16
+ * wrongly conclude the salted branch cannot execute in production at all.
17
+ *
18
+ * ─── Why this is a separate file from the key agreement ────────────────────────────────────────
19
+ *
20
+ * It used to be a second output of `deriveSessionSecrets`, and that was the defect. The envelope key
21
+ * and the salt are unrelated goals that merely both need a shared secret:
22
+ *
23
+ * the **envelope key** stops the relay reading messages in flight, and MUST be destroyed at close;
24
+ * the **session salt** stops anyone holding stored hashes from confirming a guessed message, and
25
+ * MUST survive for the life of the session.
26
+ *
27
+ * **Deriving both from one secret tied "must be forgotten" to "must be kept forever."** Every
28
+ * consequence that flowed from it — salt epochs, per-leaf epoch attribution, lockstep switching —
29
+ * was a symptom of that coupling, not a real requirement. Keeping the MOMENT (one exchange) and
30
+ * dropping the DERIVATION removes all of them.
31
+ *
32
+ * ─── What it defends against, and it is live today ─────────────────────────────────────────────
33
+ *
34
+ * `wireContentHash` is `SHA-256(0x00 ‖ content)` with nothing session-specific in it. So the same
35
+ * message text is the same 32 bytes in **every conversation, between every pair of agents, forever**
36
+ * — a relay can correlate one message across sessions and agent pairs, and can build a table of
37
+ * common short messages once and read it everywhere (`DOD-M15-HASHCORRELATE-1`).
38
+ *
39
+ * Chaining would not fix it: the adversary holds the previous hash, so they would compute
40
+ * `hash(previous ‖ guess)`. Chaining hides repeats; it does not hide content. A secret per-session
41
+ * salt does.
42
+ *
43
+ * ─── BOTH SIDES CONTRIBUTE, and why that is a requirement rather than a nicety ─────────────────
44
+ *
45
+ * Not initiator-minted. The client is open source and an operator can modify their own build, so a
46
+ * single minter could unilaterally destroy the property **for both parties** — always send the same
47
+ * salt, or a low-entropy one — and every conversation that client has becomes guessable by any relay
48
+ * holding the hashes. The honest peer cannot detect it and never consented to it.
49
+ *
50
+ * Both-contribute means **one honest participant is enough**: if either side's contribution is
51
+ * unpredictable, the salt is. Each side can also verify its own contribution was actually used.
52
+ * Same principle as the sovereign-node rule — no single party can unilaterally break a guarantee.
53
+ *
54
+ * (The envelope key already has this property structurally: X25519 ephemeral-ephemeral combines both
55
+ * secrets, and `session-key-agreement.ts` refuses the small-order point that is the one way a peer
56
+ * could force a degenerate result. Same guarantee, different mechanism.)
57
+ *
58
+ * ─── 🚨 THE SECRECY HERE IS THE CHANNEL'S, NOT THE CONSTRUCTION'S ──────────────────────────────
59
+ *
60
+ * The single most important thing about this module, and it does NOT resemble the envelope key.
61
+ *
62
+ * The key is a DH secret: a passive relay holding both public keys cannot compute it, and
63
+ * `dod-m15-keyagree-1.test.ts` pins exactly that. **The salt is a function of two values that are
64
+ * SENT.** Anyone who can read both contributions derives it with the same public HKDF label. There
65
+ * is no "a third party cannot derive it" test in this module, and there cannot be one.
66
+ *
67
+ * So "one honest participant is enough" carries a precondition that must never be dropped:
68
+ * unpredictable **to anyone who cannot read the exchange**.
69
+ *
70
+ * ─── THEREFORE, a rule for `DOD-M15-SEALWIRE-1`, which owns the wire ──────────────────────────
71
+ *
72
+ * The contributions MUST be exchanged on the peer-to-peer `/cello/content/1.0.0` stream. It rides
73
+ * circuit-relay-v2 carrying its own Noise session, so a relay forwarding it sees ciphertext.
74
+ *
75
+ * They MUST NEVER appear in `session_offer` / `session_offer_accept`, or anything else a
76
+ * DIRECTORY brokers. **That is the trap:** today the ONLY round trip at session open runs on the
77
+ * directory's signaling stream (`session-ceremony.ts`), so it is the natural, obvious place to put
78
+ * a contribution — one round trip, at open, before any leaf — and it is precisely the channel
79
+ * Decision #8 forbids. Nothing about this function protects the salt if that rule is broken, and
80
+ * a session that shipped that way cannot be repaired afterwards: the relay already holds the salt
81
+ * and the hashes.
82
+ *
83
+ * Residual, and it now covers TWO values rather than one: the contributions are unauthenticated,
84
+ * exactly like the ephemerals, so an ACTIVE on-path attacker who substitutes both sides defeats the
85
+ * salt as well as the key. That is `DOD-M15-EPHEMERAL-AUTH-1`.
86
+ */
87
+ /** Contributions and the salt are all 32 bytes. */
88
+ export declare const SALT_CONTRIBUTION_BYTES = 32;
89
+ export declare const SESSION_SALT_BYTES = 32;
90
+ /** Enough to detect a disagreement; short enough to be obviously not the salt. */
91
+ export declare const SALT_FINGERPRINT_BYTES = 8;
92
+ /** This side's random contribution. Fresh per session; sent to the peer. */
93
+ export declare function generateSaltContribution(): Uint8Array;
94
+ /**
95
+ * Combine the two contributions into the session salt.
96
+ *
97
+ * CANONICAL ORDER (lexicographic), so both sides compute identical bytes without agreeing on who
98
+ * initiated. The two daemons reach this from different code paths; ordering by role would mean a
99
+ * disagreement about "who started it" produced two different salts — and a salt disagreement is the
100
+ * least debuggable failure in this system, because the send succeeds and the receiver discards.
101
+ */
102
+ export declare function deriveSessionSalt(ourContribution: Uint8Array, peerContribution: Uint8Array): Uint8Array;
103
+ /**
104
+ * A short, one-way fingerprint of the salt, for the agreement check at session open — Decision #10.
105
+ *
106
+ * **The salt itself is never compared on the wire.** A fingerprint is derived under its own HKDF
107
+ * label, so it cannot be worked back to the salt, and it is deliberately short so nobody mistakes it
108
+ * for key material.
109
+ *
110
+ * Why compare at all: a salt disagreement makes every message fail the receive-path authenticity
111
+ * check, and `wire-content-hash.ts`'s own header calls that the least debuggable shape there is —
112
+ * the send succeeds, `parked: false`, the sender's log says the frame left, and the receiver discards
113
+ * before anything is logged about it. It cost two real daemons to find once. Refusing the session at
114
+ * open, with a named reason, is the difference between a diagnosis and a week.
115
+ */
116
+ export declare function saltFingerprint(salt: Uint8Array): Uint8Array;
117
+ /**
118
+ * The salted content hash — Decision #9.
119
+ *
120
+ * **HMAC, not `SHA-256(salt ‖ content)`.** The naive concatenation is vulnerable to length
121
+ * extension: an attacker who holds `H(salt ‖ m)` and knows `|salt|` can compute `H(salt ‖ m ‖ pad ‖
122
+ * m')` without knowing the salt. HMAC is the standard construction for exactly this and costs
123
+ * nothing extra here.
124
+ *
125
+ * The `0x00` domain byte is retained inside the message so this stays domain-separated from any
126
+ * other HMAC over the same key, and so the unsalted and salted forms can never collide.
127
+ */
128
+ export declare function saltedContentHash(salt: Uint8Array, content: Uint8Array): Uint8Array;
129
+ //# sourceMappingURL=session-salt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-salt.d.ts","sourceRoot":"","sources":["../src/session-salt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AAOH,mDAAmD;AACnD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,kFAAkF;AAClF,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAMxC,4EAA4E;AAC5E,wBAAgB,wBAAwB,IAAI,UAAU,CAErD;AAQD;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,GAAG,UAAU,CAkEvG;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAE5D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,UAAU,CAKnF"}
@@ -0,0 +1,205 @@
1
+ /**
2
+ * THE SESSION SALT — Decisions Carried #8, #9, #10 (Andre, 2026-08-23).
3
+ *
4
+ * ⚠️ AGREED, STORED, AND VERIFIABLE — BUT NO SENDER SALTS YET. Two revisions of this header have
5
+ * now been overtaken, so it states the boundary precisely rather than a slogan:
6
+ *
7
+ * `session-salt-agreement.ts` calls `deriveSessionSalt` and `saltFingerprint`; `content_salt` has
8
+ * a writer and a reader; and `wire-content-hash.ts` DOES import `saltedContentHash` — the RECEIVE
9
+ * path will verify a frame that names `hmac-sha256-salt-v1` (`DOD-M15-SEALWIRE-1` part B1).
10
+ *
11
+ * What no code in this build does is SEND one. Every send site still calls `wireContentHash`, so
12
+ * no message's hash depends on a salt yet, and part B2 is what flips that.
13
+ *
14
+ * The distinction matters to anyone auditing reachability: the salted path is live inbound and dead
15
+ * outbound. A reader who takes "nothing hashes with it" from an older revision of this header would
16
+ * wrongly conclude the salted branch cannot execute in production at all.
17
+ *
18
+ * ─── Why this is a separate file from the key agreement ────────────────────────────────────────
19
+ *
20
+ * It used to be a second output of `deriveSessionSecrets`, and that was the defect. The envelope key
21
+ * and the salt are unrelated goals that merely both need a shared secret:
22
+ *
23
+ * the **envelope key** stops the relay reading messages in flight, and MUST be destroyed at close;
24
+ * the **session salt** stops anyone holding stored hashes from confirming a guessed message, and
25
+ * MUST survive for the life of the session.
26
+ *
27
+ * **Deriving both from one secret tied "must be forgotten" to "must be kept forever."** Every
28
+ * consequence that flowed from it — salt epochs, per-leaf epoch attribution, lockstep switching —
29
+ * was a symptom of that coupling, not a real requirement. Keeping the MOMENT (one exchange) and
30
+ * dropping the DERIVATION removes all of them.
31
+ *
32
+ * ─── What it defends against, and it is live today ─────────────────────────────────────────────
33
+ *
34
+ * `wireContentHash` is `SHA-256(0x00 ‖ content)` with nothing session-specific in it. So the same
35
+ * message text is the same 32 bytes in **every conversation, between every pair of agents, forever**
36
+ * — a relay can correlate one message across sessions and agent pairs, and can build a table of
37
+ * common short messages once and read it everywhere (`DOD-M15-HASHCORRELATE-1`).
38
+ *
39
+ * Chaining would not fix it: the adversary holds the previous hash, so they would compute
40
+ * `hash(previous ‖ guess)`. Chaining hides repeats; it does not hide content. A secret per-session
41
+ * salt does.
42
+ *
43
+ * ─── BOTH SIDES CONTRIBUTE, and why that is a requirement rather than a nicety ─────────────────
44
+ *
45
+ * Not initiator-minted. The client is open source and an operator can modify their own build, so a
46
+ * single minter could unilaterally destroy the property **for both parties** — always send the same
47
+ * salt, or a low-entropy one — and every conversation that client has becomes guessable by any relay
48
+ * holding the hashes. The honest peer cannot detect it and never consented to it.
49
+ *
50
+ * Both-contribute means **one honest participant is enough**: if either side's contribution is
51
+ * unpredictable, the salt is. Each side can also verify its own contribution was actually used.
52
+ * Same principle as the sovereign-node rule — no single party can unilaterally break a guarantee.
53
+ *
54
+ * (The envelope key already has this property structurally: X25519 ephemeral-ephemeral combines both
55
+ * secrets, and `session-key-agreement.ts` refuses the small-order point that is the one way a peer
56
+ * could force a degenerate result. Same guarantee, different mechanism.)
57
+ *
58
+ * ─── 🚨 THE SECRECY HERE IS THE CHANNEL'S, NOT THE CONSTRUCTION'S ──────────────────────────────
59
+ *
60
+ * The single most important thing about this module, and it does NOT resemble the envelope key.
61
+ *
62
+ * The key is a DH secret: a passive relay holding both public keys cannot compute it, and
63
+ * `dod-m15-keyagree-1.test.ts` pins exactly that. **The salt is a function of two values that are
64
+ * SENT.** Anyone who can read both contributions derives it with the same public HKDF label. There
65
+ * is no "a third party cannot derive it" test in this module, and there cannot be one.
66
+ *
67
+ * So "one honest participant is enough" carries a precondition that must never be dropped:
68
+ * unpredictable **to anyone who cannot read the exchange**.
69
+ *
70
+ * ─── THEREFORE, a rule for `DOD-M15-SEALWIRE-1`, which owns the wire ──────────────────────────
71
+ *
72
+ * The contributions MUST be exchanged on the peer-to-peer `/cello/content/1.0.0` stream. It rides
73
+ * circuit-relay-v2 carrying its own Noise session, so a relay forwarding it sees ciphertext.
74
+ *
75
+ * They MUST NEVER appear in `session_offer` / `session_offer_accept`, or anything else a
76
+ * DIRECTORY brokers. **That is the trap:** today the ONLY round trip at session open runs on the
77
+ * directory's signaling stream (`session-ceremony.ts`), so it is the natural, obvious place to put
78
+ * a contribution — one round trip, at open, before any leaf — and it is precisely the channel
79
+ * Decision #8 forbids. Nothing about this function protects the salt if that rule is broken, and
80
+ * a session that shipped that way cannot be repaired afterwards: the relay already holds the salt
81
+ * and the hashes.
82
+ *
83
+ * Residual, and it now covers TWO values rather than one: the contributions are unauthenticated,
84
+ * exactly like the ephemerals, so an ACTIVE on-path attacker who substitutes both sides defeats the
85
+ * salt as well as the key. That is `DOD-M15-EPHEMERAL-AUTH-1`.
86
+ */
87
+ import { hkdf } from "@noble/hashes/hkdf.js";
88
+ import { sha256 } from "@noble/hashes/sha2.js";
89
+ import { hmac } from "@noble/hashes/hmac.js";
90
+ import { randomBytes } from "node:crypto";
91
+ /** Contributions and the salt are all 32 bytes. */
92
+ export const SALT_CONTRIBUTION_BYTES = 32;
93
+ export const SESSION_SALT_BYTES = 32;
94
+ /** Enough to detect a disagreement; short enough to be obviously not the salt. */
95
+ export const SALT_FINGERPRINT_BYTES = 8;
96
+ const ENC = new TextEncoder();
97
+ const INFO_SESSION_SALT = ENC.encode("cello/session/v1/salt");
98
+ const INFO_SALT_FINGERPRINT = ENC.encode("cello/session/v1/salt-fingerprint");
99
+ /** This side's random contribution. Fresh per session; sent to the peer. */
100
+ export function generateSaltContribution() {
101
+ return new Uint8Array(randomBytes(SALT_CONTRIBUTION_BYTES));
102
+ }
103
+ function isAllZero(b) {
104
+ let acc = 0;
105
+ for (const x of b)
106
+ acc |= x;
107
+ return acc === 0;
108
+ }
109
+ /**
110
+ * Combine the two contributions into the session salt.
111
+ *
112
+ * CANONICAL ORDER (lexicographic), so both sides compute identical bytes without agreeing on who
113
+ * initiated. The two daemons reach this from different code paths; ordering by role would mean a
114
+ * disagreement about "who started it" produced two different salts — and a salt disagreement is the
115
+ * least debuggable failure in this system, because the send succeeds and the receiver discards.
116
+ */
117
+ export function deriveSessionSalt(ourContribution, peerContribution) {
118
+ /**
119
+ * REFUSE A DEGENERATE PEER CONTRIBUTION — the same posture the key agreement takes toward a
120
+ * small-order point, and for the same reason: a peer that contributes nothing has unilaterally
121
+ * decided the salt, which is exactly the property both-contribute exists to prevent.
122
+ */
123
+ if (peerContribution.length !== SALT_CONTRIBUTION_BYTES) {
124
+ throw new Error(`SESSION SALT: the peer's salt contribution must be ${SALT_CONTRIBUTION_BYTES} bytes, got ` +
125
+ `${peerContribution.length}. Refusing rather than padding — a short contribution silently ` +
126
+ "zero-extended is a salt this side did not really help choose.");
127
+ }
128
+ if (isAllZero(peerContribution)) {
129
+ throw new Error("SESSION SALT: the peer's salt contribution is all zeros, which means it contributed nothing " +
130
+ "and the salt would be decided by one side alone. That is the property both-contribute exists " +
131
+ "to prevent: a modified client could then make every one of its conversations guessable by any " +
132
+ "relay holding the hashes, without its peer being able to tell. Refusing.");
133
+ }
134
+ if (ourContribution.length !== SALT_CONTRIBUTION_BYTES) {
135
+ throw new Error(`SESSION SALT: our own salt contribution must be ${SALT_CONTRIBUTION_BYTES} bytes, got ` +
136
+ `${ourContribution.length}. This is a local defect, not something the peer did.`);
137
+ }
138
+ /**
139
+ * OUR OWN all-zero contribution is refused too — review F6, and the asymmetry mattered.
140
+ *
141
+ * Without this, a daemon with a broken or patched RNG derives happily while the PEER refuses with
142
+ * *"the peer's salt contribution is all zeros"* — so the operator whose machine is actually broken
143
+ * reads a failure that blames their counterparty. `session-key-agreement.ts` already established
144
+ * the symmetric pattern for lengths ("this is a local defect, not something the peer did"); this
145
+ * file had it for length and not for zero.
146
+ */
147
+ if (isAllZero(ourContribution)) {
148
+ throw new Error("SESSION SALT: our OWN salt contribution is all zeros, so this side contributed nothing. This " +
149
+ "is a LOCAL defect — a broken or patched random source — not something the peer did. Refusing " +
150
+ "rather than deriving a salt one side chose alone.");
151
+ }
152
+ /**
153
+ * A REFLECTED contribution — the peer echoing ours back — is refused, matching
154
+ * `session-key-agreement.ts`'s reflection check. Confidentiality survives (our half is random), so
155
+ * this is hygiene rather than a break; but the module claims each side can verify its contribution
156
+ * was used, and nothing was verifying the PEER contributed at all.
157
+ */
158
+ if (Buffer.compare(Buffer.from(ourContribution), Buffer.from(peerContribution)) === 0) {
159
+ throw new Error("SESSION SALT: the peer's salt contribution is identical to our own — the peer contributed " +
160
+ "nothing to the agreement. Refusing: this is a reflection, not an exchange.");
161
+ }
162
+ const [first, second] = Buffer.compare(Buffer.from(ourContribution), Buffer.from(peerContribution)) < 0
163
+ ? [ourContribution, peerContribution]
164
+ : [peerContribution, ourContribution];
165
+ const ikm = new Uint8Array(first.length + second.length);
166
+ ikm.set(first, 0);
167
+ ikm.set(second, first.length);
168
+ // No salt argument to HKDF here: the session id is not needed to separate sessions, because the
169
+ // contributions are fresh random per session and already do it.
170
+ return hkdf(sha256, ikm, new Uint8Array(0), INFO_SESSION_SALT, SESSION_SALT_BYTES);
171
+ }
172
+ /**
173
+ * A short, one-way fingerprint of the salt, for the agreement check at session open — Decision #10.
174
+ *
175
+ * **The salt itself is never compared on the wire.** A fingerprint is derived under its own HKDF
176
+ * label, so it cannot be worked back to the salt, and it is deliberately short so nobody mistakes it
177
+ * for key material.
178
+ *
179
+ * Why compare at all: a salt disagreement makes every message fail the receive-path authenticity
180
+ * check, and `wire-content-hash.ts`'s own header calls that the least debuggable shape there is —
181
+ * the send succeeds, `parked: false`, the sender's log says the frame left, and the receiver discards
182
+ * before anything is logged about it. It cost two real daemons to find once. Refusing the session at
183
+ * open, with a named reason, is the difference between a diagnosis and a week.
184
+ */
185
+ export function saltFingerprint(salt) {
186
+ return hkdf(sha256, salt, new Uint8Array(0), INFO_SALT_FINGERPRINT, SALT_FINGERPRINT_BYTES);
187
+ }
188
+ /**
189
+ * The salted content hash — Decision #9.
190
+ *
191
+ * **HMAC, not `SHA-256(salt ‖ content)`.** The naive concatenation is vulnerable to length
192
+ * extension: an attacker who holds `H(salt ‖ m)` and knows `|salt|` can compute `H(salt ‖ m ‖ pad ‖
193
+ * m')` without knowing the salt. HMAC is the standard construction for exactly this and costs
194
+ * nothing extra here.
195
+ *
196
+ * The `0x00` domain byte is retained inside the message so this stays domain-separated from any
197
+ * other HMAC over the same key, and so the unsalted and salted forms can never collide.
198
+ */
199
+ export function saltedContentHash(salt, content) {
200
+ const msg = new Uint8Array(1 + content.length);
201
+ msg[0] = 0x00;
202
+ msg.set(content, 1);
203
+ return hmac(sha256, salt, msg);
204
+ }
205
+ //# sourceMappingURL=session-salt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-salt.js","sourceRoot":"","sources":["../src/session-salt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqFG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,mDAAmD;AACnD,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAC1C,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AACrC,kFAAkF;AAClF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAExC,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC;AAC9B,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,CAAC,uBAAuB,CAAC,CAAC;AAC9D,MAAM,qBAAqB,GAAG,GAAG,CAAC,MAAM,CAAC,mCAAmC,CAAC,CAAC;AAE9E,4EAA4E;AAC5E,MAAM,UAAU,wBAAwB;IACtC,OAAO,IAAI,UAAU,CAAC,WAAW,CAAC,uBAAuB,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,SAAS,SAAS,CAAC,CAAa;IAC9B,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,CAAC,IAAI,CAAC;QAAE,GAAG,IAAI,CAAC,CAAC;IAC5B,OAAO,GAAG,KAAK,CAAC,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,eAA2B,EAAE,gBAA4B;IACzF;;;;OAIG;IACH,IAAI,gBAAgB,CAAC,MAAM,KAAK,uBAAuB,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CACb,sDAAsD,uBAAuB,cAAc;YAC3F,GAAG,gBAAgB,CAAC,MAAM,iEAAiE;YAC3F,+DAA+D,CAChE,CAAC;IACJ,CAAC;IACD,IAAI,SAAS,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,8FAA8F;YAC9F,+FAA+F;YAC/F,gGAAgG;YAChG,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IACD,IAAI,eAAe,CAAC,MAAM,KAAK,uBAAuB,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CACb,mDAAmD,uBAAuB,cAAc;YACxF,GAAG,eAAe,CAAC,MAAM,uDAAuD,CACjF,CAAC;IACJ,CAAC;IACD;;;;;;;;OAQG;IACH,IAAI,SAAS,CAAC,eAAe,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,+FAA+F;YAC/F,+FAA+F;YAC/F,mDAAmD,CACpD,CAAC;IACJ,CAAC;IACD;;;;;OAKG;IACH,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACtF,MAAM,IAAI,KAAK,CACb,4FAA4F;YAC5F,4EAA4E,CAC7E,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,GAAG,CAAC;QACrG,CAAC,CAAC,CAAC,eAAe,EAAE,gBAAgB,CAAC;QACrC,CAAC,CAAC,CAAC,gBAAgB,EAAE,eAAe,CAAC,CAAC;IAExC,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IACzD,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAClB,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC9B,gGAAgG;IAChG,gEAAgE;IAChE,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,kBAAkB,CAAC,CAAC;AACrF,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,IAAgB;IAC9C,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,qBAAqB,EAAE,sBAAsB,CAAC,CAAC;AAC9F,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAgB,EAAE,OAAmB;IACrE,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IACd,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACpB,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;AACjC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cello-protocol/crypto",
3
- "version": "0.0.58",
3
+ "version": "0.0.60",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {