I followed the description here:

Public-key authenticated encryption: crypto_box

and created an example that encrypts a message with Bob's public key and signs it with Alice's secret key :

cipher = crypto_box(secret_string, nonce, bob_pk, alice_sk);

The cipher text can be decrypted and verified with Bob's secret key and Alice's public key:

message = crypto_box_open(cipher, nonce, alice_pk, bob_sk);  

However, the message can also be decrypted with Bob' public key and Alice's secret key:

message = crypto_box_open(cipher, nonce, bob_pk, alice_sk); 

This is somehow unexpected. I couldn't find much documentation for the crypto box. What is the math behind it?

My full code to reproduce:

#include <crypto_box.h>
#include <string>
#include <iostream>
#include <fstream>
#include <randombytes.h>
#include <string>
#include <iostream>

int main() {

    std::string alice_pk;
    std::string alice_sk;
    std::string bob_pk;
    std::string bob_sk;
    std::string nonce;
    std::string cipher;
    std::string message;

    std::string secret_string = "my secret string";

    unsigned char buffer[crypto_box_NONCEBYTES];

    randombytes((unsigned char*)buffer, crypto_box_NONCEBYTES);
    nonce.assign((const char*)buffer, (size_t)crypto_box_NONCEBYTES);

    alice_pk = crypto_box_keypair(&alice_sk);
    bob_pk   = crypto_box_keypair(&bob_sk);

    cipher = crypto_box(secret_string, nonce, bob_pk, alice_sk);

    message = crypto_box_open(cipher, nonce, bob_pk, alice_sk);  

    std::cout << message << std::endl;

    message = crypto_box_open(cipher, nonce, alice_pk, bob_sk);  

    std::cout << message << std::endl;

  • $\begingroup$ > Selected primitive > crypto_box is curve25519xsalsa20poly1305, a particular combination of Curve25519, Salsa20, and Poly1305 specified in "Cryptography in NaCl". This function is conjectured to meet the standard notions of privacy and third-party unforgeability. $\endgroup$ Aug 17, 2019 at 14:50
  • $\begingroup$ That is where in the linked page it has a description of the crypto used. Mr. Denis' explanation is correct, since Curve25519 is an ECDH primitive in this context. $\endgroup$ Aug 17, 2019 at 14:53

1 Answer 1


Alice and Bob's keys are used to compute a shared key.

Both parties compute the same key, which is used both encryption and decryption.

This is a traditional, hybrid encryption system.

Using only asymmetric cryptography for encryption would be painfully slow.

As a mitigation, the sender can create an ephemeral key pair, deleted right after the message has been encrypted. See ECIES or libsodium's crypto_box_seal() construction.

  • $\begingroup$ So why exactly can I decrypt the message with the same public key used for encryption? $\endgroup$
    – Andy
    Aug 15, 2019 at 13:57
  • 1
    $\begingroup$ The same key can be computed either from (Alice PK, Bob SK) or (Bob PK, Alice SK). This is the key actually used for encryption, and decryption. $\endgroup$ Aug 15, 2019 at 14:02
  • $\begingroup$ So this is ECDH with some symmetric cipher then? On the website, they mention not a single bit about DH. $\endgroup$
    – Andy
    Aug 15, 2019 at 14:18
  • 3
    $\begingroup$ Yes, crypto_box() is ECDH using X25519, and encryption using salsa20poly1305. $\endgroup$ Aug 15, 2019 at 16:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.