# 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 <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;

}

• > 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. – SAI Peregrinus Aug 17 '19 at 14:50
• 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. – SAI Peregrinus Aug 17 '19 at 14:53

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.

• So why exactly can I decrypt the message with the same public key used for encryption? – Andy Aug 15 '19 at 13:57
• 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. – Frank Denis Aug 15 '19 at 14:02
• So this is ECDH with some symmetric cipher then? On the website, they mention not a single bit about DH. – Andy Aug 15 '19 at 14:18
• Yes, crypto_box() is ECDH using X25519, and encryption using salsa20poly1305. – Frank Denis Aug 15 '19 at 16:25