StableLib / stablelib

A stable library of useful TypeScript/JavaScript code
https://www.stablelib.com
Other
175 stars 36 forks source link

Migrating from libsodium: crypto_box_seal and crypto_box_seal_open equivalent #59

Closed AndreasGassmann closed 2 years ago

AndreasGassmann commented 2 years ago

After https://github.com/StableLib/stablelib/pull/58 had been merged, I continued with the migration of our library https://github.com/airgap-it/beacon-sdk/pull/387 to @stablelib. There is now only one thing missing that I cannot seem to get working:

In our code, we use the following 2 methods of libsodium:

const encryptedMessage = crypto_box_seal(payload, kxSelfPublicKey)

const decryptedMessage = crypto_box_seal_open(encryptedPayload, kxSelfPublicKey, kxSelfPrivateKey)

Are there equivalents for those methods in @stablelib? I'm tried to decrypt it with everything I could find, I tried with openSecretBox and openBox from @stablelib/nacl, but also with XChaCha20Poly1305 from @stablelib/xchacha20poly1306. I tried to reproduce the nonce generation from libsodium https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_box/crypto_box_seal.c#L10, but I just cannot get it to work, I always get "null" when trying to decrypt the payload.

Is this implemented and I'm simply not using the right method, or is that also functionality that is not available in @stablelib?

Thanks again for the help. Once I'm done with the migration, I will create a small document about how one can migrate from libsodium to stablelib. Because most of it is quite straight forward, if you know which method to use :).

dchest commented 2 years ago

It's basically a message encrypted with a freshly generated key pair and nonce which is generated as:

import { BLAKE2b } from "@stablelib/blake2b"

let h = new BLAKE2b(24)
h.update(publicKey1)
h.update(publicKey2)
let nonce = h.digest()

The public key is then concatenated with the result of encryption with nacl.box (you can use concat from @stablelib/bytes).

For decrypting, extract the public key from the sealed box, generate the nonce again and feed it to openBox, along with the part of the box minus the public key.

AndreasGassmann commented 2 years ago

I can't believe I was so close. I did everything right, but I didn't see that openBox actually requires the OTHER publicKey and the own privateKey, I passed in my pk and sk, just like you have to do in libsodium. Changing that fixed it.

The working code looks like this for anyone looking for it in the future:

// libsodium
// const decryptedMessage = crypto_box_seal_open(encryptedPayload, kxSelfPublicKey, kxSelfPrivateKey)

const epk = payload.slice(0, 32)
const cyphertext = payload.slice(32)

const state = new BLAKE2b(24)
const nonce = state.update(epk, 32).update(kxSelfPublicKey, 32).digest()

console.log(openBox(epk, kxSelfPrivateKey, nonce, ciphertext))

Thanks a lot for your help, keep up the good work :)