1

Would something like this be a reasonable way to implement the double ratchet with libsodium? The sodium port that I'm using is quite limited and does not offer a kdf.

Here is a link to the libsodium documentation page regarding the scalar multiplication.

What crypto_scalarmult_base / crypto_scalarmult does is: "compute a shared secret given a user's secret key and another user's public key".

// random key shared between parties during the initial handshake
const preKey = "yez6GrvuVl3zihPwHf9wiXUv93CSkXHBQhPs6e0YLxg="

// compute the first chain key based on the shared pre key const chain1 = sodium.crypto_scalarmult_base(preKey)

// compute the actual message key, since chain has just started, // use the pre key as the "public key" and chain1 key as // the "secret key" for the multiplication const key1 = sodium.crypto_scalarmult(chain1, preKey)

const cipher1 = encrypt_message(key1)

// compute the next chain2 key based on the previous chain1 key const chain2 = sodium.crypto_scalarmult_base(chain1)

// compute the next message key based on the chain2 key, // used as the "secret key" and the chain1 key used as // the "public key" for the multiplication const key2 = sodium.crypto_scalarmult(chain2, chain1)

const cipher2 = encrypt_message(key2)

const chain3 = sodium.crypto_scalarmult_base(chain2) const key3 = sodium.crypto_scalarmult(chain3, chain2)

const cipher3 = encrypt_message(key3)

// and so on ....

From what I understand, scalarmult is not backtrackable and breaking key2 would not allow you to calculate key1 nor key3.

So my question is - is an implementation like this reasonable or a total disaster?

M K
  • 113
  • 4

1 Answers1

1

I would strongly encourage you to carefully read the entire Point*scalar multiplication page and Signal's Double Ratchet documentation. You can also read about the Double Ratchet and Signal protocol in the book Real-World Cryptography.

Make sure you understand what the libsodium functions do, how the Double Ratchet algorithm works, and why the algorithm was designed that way.

So my question is - is an implementation like this reasonable or a total disaster?

It's unclear what the preKey is, but a key exchange involves exchanging two public keys, not a single key. If you exchange a single key, there's no need to use asymmetric cryptography like this at all. The tricky part is keeping that single key secure, which is why public keys (non-secrets) are normally exchanged.

crypto_scalarmult_base() computes a public key from the private key, not a shared secret.

crypto_scalarmult() computes a shared secret for a private key and someone else's public key. It looks like it's being used to compute a shared secret from what you believe to be another shared secret; I'm not entirely sure. There should be more than one key exchange going on.

You shouldn't use shared secrets directly because they're not uniformly random. They're meant to be passed through a KDF.

Also, for a given public key with X25519, there are several equivalent public keys that can be computed. This can lead to vulnerabilities, so it's recommended to hash/KDF the shared secret concatenated with both public keys. That's what the newer key exchange API does, but I'm guessing you don't have access to that.

The sodium port that I'm using is quite limited and does not offer a kdf.

This is a problem. If it supports keyed hashing, you can construct a KDF, but it sounds like you should just switch to a better libsodium binding if possible.

samuel-lucas6
  • 2,211
  • 9
  • 20