0

I can do that : Encrypt on frontend (sodium-plus.js) with public-key from backend (PHP sodium)

But I want to do the contrary (encrypt with php, decrypt with javascript), and I have an issue.

I can get my private key from my html page (generated with php) as a hex string ( sodium_bin2hex(sodium_crypto_secretbox_keygen()) ), but I can't use it with sodium plus.

I know this code to get public key :

let key = X25519PublicKey.from('...', "hex");

but in my case that doesn't work and I have an error passing this variable in

await sodium.crypto_secretbox_open(text, nonce, key);

I've tried just with the hex string convert to bin ( await sodium.sodium_hex2bin(key) ) but it doesn't work too.

Here is my code :

define(function (require) {
    const { SodiumPlus } = require("sodium-plus");
});

let sodium;

(async function () {
    if (!sodium) sodium = await SodiumPlus.auto();
    let text = "...";//my text + nonce (at the end) in hex

    let nonce = text.substr(-48);
    text = text.substr(0, text.length - 48);
    let key = X25519PublicKey.from($("#key").text(), "hex");//get my private key in hex, on my html page

    text = await sodium.sodium_hex2bin(text);
    nonce = await sodium.sodium_hex2bin(nonce);

    let output = await sodium.crypto_secretbox_open(text, nonce, key);    

    console.log(output.toString());

})();

thank you

Topaco
  • 40,594
  • 4
  • 35
  • 62
DoMiSol
  • 9
  • 4
  • Just a "quick hot" - maybe you could use "X25519SecretKey" instead of "X25519PublicKey" to import the key ? – Michael Fehr Feb 14 '22 at 07:13
  • If you have imported the secret key (s. prev. comment), you can derive the public key with `crypto_box_publickey_from_secretkey()`. Also, *crypto_box* must be used instead of *crypto_secretbox*. – Topaco Feb 17 '22 at 07:49
  • Thank you Michael Fehr and @Topaco I saw you first com here, and I think I will use keypair (secret and public x 2). I didn't think about that the first time, and I think it's the best idea to enhance the security :D Thank you, I'm a beginner in crypto. As see here : https://github.com/paragonie/sodium-plus/blob/master/docs/SodiumPlus/authenticated-public-key-encryption.md – DoMiSol Feb 19 '22 at 01:53

1 Answers1

0

Here is my solution with crypto_box and secret and public twice :

define(function (require) {
    const { SodiumPlus } = require("sodium-plus");
});

let sodium;

(async function () {
    if (!sodium) sodium = await SodiumPlus.auto();
    let text = ""; //string in hex to be decrypted (see below for my php code server side)

    let nonce = text.substring(text.length -48); //nonce at the end of the  string
    nonce = await sodium.sodium_hex2bin(nonce); //nonce hex to bin

    text = text.substring(0, text.length - 48); //text without nonce
    text = await sodium.sodium_hex2bin(text); //text hex to bin

    let publicKey = $("#key").text(); //to get my key in hex from my html page (key = secret key + public key), see below my php code $decryption_key

    let secretKey = publicKey.substring(0, 64);//to get the public key in hex
    secretKey = X25519SecretKey.from(secretKey, "hex"); //public key hex to array bin

    publicKey = publicKey.substring(publicKey.length -64); //same for public key
    publicKey = X25519PublicKey.from(publicKey, "hex");

    let output = await sodium.crypto_box_open(text, nonce, secretKey, publicKey);
    output = output.toString(); // bin to string

    console.log(output);

})();

Here is my php code to encrypt, for example :

$text = '...'; //text to encrypt
$key = sodium_hex2bin( '...' ); //key in hex (got from an other code, see below $encryption_key)
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
$text_encrypt = sodium_crypto_box($text, $nonce, $key);
$text_encrypt = sodium_bin2hex($text_encrypt.$nonce);
return $text_encrypt;

My php code for encryption key in php, and decryption key on my html page (then got by javascript)

$keypair1 = sodium_crypto_box_keypair();
$keypair1_secret = sodium_crypto_box_secretkey($keypair1);
$keypair1_public = sodium_crypto_box_publickey($keypair1);
$keypair2 = sodium_crypto_box_keypair();
$keypair2_secret = sodium_crypto_box_secretkey($keypair2);
$keypair2_public = sodium_crypto_box_publickey($keypair2);

$encryption_key = sodium_crypto_box_keypair_from_secretkey_and_publickey($keypair1_secret, $keypair2_public);
$encryption_key = sodium_bin2hex( $encryption_key ); //for my php encrypt code

$decryption_key = sodium_crypto_box_keypair_from_secretkey_and_publickey($keypair2_secret, $keypair1_public);
$decryption_key = sodium_bin2hex( $decryption_key ); //for my html page, then got by javascript
DoMiSol
  • 9
  • 4
  • Unfortunately, encryption between the PHP and the JavaScript side seems to be _completely_ broken here! The problem is that you create the key pair for the JavaScript side `$keypair2` on the PHP side. `$decryption_key` is the concatenation of the secret key of the JavaScript side `$keypair2_secret` and the public key of the PHP side `$keypair1_public`. So if you pass `$decryption_key` to the JavaScript side, you _leak the secret key and encryption is broken_. For secure communication, the JavaScript side must generate its own key pair and pass the public key to the PHP side. – Topaco Feb 19 '22 at 08:03
  • I understand but it’s just for encrypt from php and decrypt in JS. So a communication from PHP to JS. I think there is no way to completely secure this communication because of JS. Right ? Moreover my page are refreshed sometimes. And I don’t want an ephemeral key. I have others verifications too. – DoMiSol Feb 19 '22 at 15:02
  • But I understand what you mean : pass JS public key to php, encrypt in php, return to JS encrypted text + php public key, decrypt with JS secret key, is it right ? – DoMiSol Feb 19 '22 at 15:09
  • As for the communication between PHP and JavaScript, I just wanted to point out that, as far as confidentiality is concerned, there is no difference between the posted solution and an _unencrypted_ communication, so encryption could be omitted altogether. Without keeping the secret key secret, public-key encryption is pointless. – Topaco Feb 19 '22 at 15:42
  • I understand, because my JS secrey key is exposed on html and pass from php ? I will edit my code, thank you – DoMiSol Feb 19 '22 at 15:58
  • @Topaco I don't know how I can use private and public key all over my javascript code with async to be able to use the code in my different async function with a return value or other. I can display it in console.log, otherwise I have a problem with "promise" to get the value. – DoMiSol Feb 19 '22 at 18:08