Open Mikescops opened 6 months ago
Yes, that should totally be doable. The state is just the address of a 52 byte array, that can be safely moved to different hosts.
@jedisct1 thanks, where that change should happen? in the parent libsodium library?
No, just in the JavaScript code. I think it can be done even without changing libsodium-wrappers
.
First, I simply printed the the state_in and and state_out value. They seems to be numbers, which is not exactly what the @types/libsodium-wrappers is expecting.
What's weird too is that even if I rebuild the project and run multiple times i always get the same numbers.
state_in = 102240
state_out = 102144
So I tried to read through the wrapper code to understand what's going on.
I took a look at where the crypto_secretstream_xchacha20poly1305_init_pull
is used:
var h = new u(52).address;
if (0 == (0 | a._crypto_secretstream_xchacha20poly1305_init_pull(h, n, c))) {
var p = h;
return g(_),
p
}
b(_, "invalid usage")
In this code:
and then where crypto_secretstream_xchacha20poly1305_init_push
is used:
var s = new u(52).address,
c = new u(0 | a._crypto_secretstream_xchacha20poly1305_headerbytes()),
o = c.address;
if (t.push(o), 0 == (0 | a._crypto_secretstream_xchacha20poly1305_init_push(s, o, _))) {
var h = {
state: s,
header: y(c, r)
};
return g(t),
h
}
b(t, "invalid usage")
In this code:
Now taking a look at the function u()
that seems to allocate some memory.
function u(e) {
this.length = e,
this.address = v(e)
}
function d(e) {
var r = v(e.length);
return a.HEAPU8.set(e, r),
r
}
function v(e) {
var r = a._malloc(e);
if (0 === r) throw {
message: "_malloc() failed",
length: e
};
return r
}
Sounds like it's a binding to a malloc (probably to the C code?).
What I get from this point is that the state and the header are constructed the same way with the u()
function, and from the output of the function we get the address in a Uint8Array
which is easily convertible to any other format.
The call to y(c, r)
seems to do the trick, so let's look at the code:
function y(e, r) {
var a = r || t;
if (!i(a)) throw new Error(a + " output format is not available");
if (e instanceof u) {
if ("uint8array" === a) return e.to_Uint8Array();
if ("text" === a) return s(e.to_Uint8Array());
if ("hex" === a) return c(e.to_Uint8Array());
if ("base64" === a) return p(e.to_Uint8Array(), o.URLSAFE_NO_PADDING);
throw new Error('What is output format "' + a + '"?')
}
if ("object" == typeof e) {
for (var _ = Object.keys(e), n = {},
h = 0; h < _.length; h++) n[_[h]] = y(e[_[h]], a);
return n
}
if ("string" == typeof e) return e;
throw new TypeError("Cannot format output")
}
So the .to_Uint8Array()
method from the prototype is converting this address to the needed Uint8Array, which look like this in the code:
return u.prototype.to_Uint8Array = function() {
var e = new Uint8Array(this.length);
return e.set(a.HEAPU8.subarray(this.address, this.address + this.length)),
e
},
In the end the issue relies on the fact that for the state we return only the address var s = new u(52).address
while for the header we convert the entire buffer to a to_Uint8Array().
Two options:
a.HEAPU8.subarray(this.address, this.address + this.length)
What do you think?
Hello,
I'm wondering if there is a way to serialize the
state_in
and thestate_out
fromcrypto_secretstream_xchacha20poly1305_init_push
andcrypto_secretstream_xchacha20poly1305_init_pull
in order to store them in a persistent memory.My first use case is a client / server exchanges where the server is stateless and cannot keep in RAM the states.
My second use case is a client / server exchanges where the server is a set of multiple machines behind a load balancer where the client has no guarantee to hit the same machine in a row.
Thanks!