NaCl allows decryption with the same public key as for encryption 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 &lt;crypto_box.h&gt;
#include &lt;string&gt;
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;randombytes.h&gt;
#include &lt;string&gt;
#include &lt;iostream&gt;

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(&amp;alice_sk);
    bob_pk   = crypto_box_keypair(&amp;bob_sk);

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

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

    std::cout &lt;&lt; message &lt;&lt; std::endl;

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

    std::cout &lt;&lt; message &lt;&lt; std::endl;


}
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.