1

I am doing some experimentation with the new library "Libsodium". Based on the https://www.zimuel.it/slides/zendcon2018/sodium#/21 slide. In this slide there is an example show about encryption and decryption with sodium.

 $msg = 'This is a super secret message!';

// Generating an encryption key and a nonce
$key   = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); // 256 bit
$nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24 bytes

// Encrypt
$ciphertext = sodium_crypto_secretbox($msg, $nonce, $key);
// Decrypt
$plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);

echo $plaintext === $msg ? 'Success' : 'Error';

I used this in a PHP class method like this:

public function sodium_encrypt($p_sPlaintext)            
{
    try{
        if(!empty($p_sPlaintext) && is_string($p_sPlaintext)){
            // Generating an encryption key and a nonce
            $key   = random_bytes(SODIUM_CRYPTO_SECRETBOX_KEYBYTES); // 256 bit                        
            $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24 bytes                

            // Encrypt Note: the encryption is always authenticated, you need to store also nonce + ciphertext
            $eCiphertext = sodium_crypto_secretbox($p_sPlaintext, $nonce, $key);
            $eCryptotext = $nonce.$eCiphertext;
            if(!empty($eCiphertext)){
                return($eCiphertext);
            } else{
                throw new Exception('clse004');                             // Error trigger
            }
        } else{
            throw new Exception('clse005');                                 // Error trigger
        }
    } catch (Exception $e){
        echo("<br>Errormessage, code: ".$e->getMessage());
    }
}

and it works like it should be, no problems there. BUT..... :-) there is always an 'but'.

If I use this method to encrypt, for example, an e-mail address and store this in the database as the users login credentials, then they are an unique encryption of his credentials. The next time the user enters his credentials and I encrypt these, then I wound find him in the DB becaurse of the random generating of the $key and the $nonce.

If I generate my own, not random, key: (https://www.allkeysgenerator.com/Random/Security-Encryption-Key-Generator.aspx)

 $key   = "4D6251655468576D597133743677397A24432646294A404E635266556A586E32"; // 256 bit hex

Then I get the message: "key size should be SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes".

With the key:

 $key   = "E)H@McQfTjWnZr4u7w!z%C*F-JaNdRgU"; // 256 bit non hex

is that problem solved too.

Then the $ciphertext is still random becaurse of the random nonce. Replacing the randomizer with a non random nonce like: (https://www.random.org/bytes/)

$nonce = "d80ac8385bb52cef7920ded5bda4de50697efc95ea53d81b" ; // 24 bytes hex

this message is shown: "nonce size should be SODIUM_CRYPTO_SECRETBOX_NONCEBYTES bytes".

The same with:

$nonce = "249206220193104991548714284109130186236311295118249161302345616 " ; // 24 bytes decimal
$nonce = "056073032127157034374115050245203151150054323272014260311360377272100266" ; // 24 bytes octal

For the time being my current methode looks like:

public function sodium_encrypt($p_sPlaintext)            
{
    try{
        if(!empty($p_sPlaintext) && is_string($p_sPlaintext)){
            // Generating an encryption key and a nonce
            $key   = "E)H@McQfTjWnZr4u7w!z%C*F-JaNdRgU"; // 256 bit                                        
            $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); // 24 bytes
            // Encrypt Note: the encryption is always authenticated, you need to store also nonce + ciphertext
            $eCiphertext = sodium_crypto_secretbox($p_sPlaintext, $nonce, $key);
            //$eCryptoText = $nonce.$eCiphertext;                
            echo $eCiphertext;
            if(!empty($eCiphertext)){
                return($eCiphertext);
            } else{
                throw new Exception('clse004');                             // Error trigger
            }
            exit();
        } else{
            throw new Exception('clse005');                                 // Error trigger
        }
    } catch (Exception $e){
        echo("<br>Errormessage, code: ".$e->getMessage());
    }
}

I looked at https://download.libsodium.org/doc/secret-key_cryptography but cant find a solution to create a non random (secret)nonce to use in this instance of encryption. Does anyone have ideas? Are more people working with Sodium in PHP? Any input is welcome.

  • In general (and not only for Sodium), for algorithms which generate different ciphertexts for the same plaintext (probabilistic), it is not possible to deduce from the inequality of two ciphertexts the inequality of the plaintexts. This would only be possible by using the same nonce (assuming the same key, of course), but exactly this reduces the security or even destroys it completely, i.e. it is crucial to use a key/nonce pair only once. Technically, of course, you can use any nonce by converting the hex-string with `hex2bin` to the corresponding byte-array. – Topaco Nov 11 '19 at 08:08

2 Answers2

2

I know this message is old and probably solved, but I guess this might help.

NONCE is 24 characters long. -> this is pretty much the answer. However, if you try to make nonce not random, including the key, then the output will no longer a series of endless random characters for a single data. It will be easily said as one is to one. Anyhow, KEY is 32 characters. Let's try this example.

<?php
class justEncrypt {
     private $key = 'vDIa5JdknBqfrKOu8d7UpddnBMCH1vza'; //32 characters
     private $nonce = 'Ra5LeH7ntW2rvkz3dmqI5Stx'; //24 characters

     public function sodiumStaticEncrypt(string $data) {
           $encrypted = base64_encode(
                 sodium_crypto_secretbox(
                       $data,
                       $this->nonce,
                       $this->key
                 )
           );
           return $encrypted;
    }
}

Testing the script:

<?php
$site = new justEncrypt; // I feel like I should not have done this
    echo $site->sodiumStaticEncrypt("It's better late than never!");

will have an output of 5Was2l9V0RgsgEm5csEbXnCaIxLkWwWiOxnHBgrej9+Ipyqn4ehn8VImFa8= and that's it because you don't want it to become random, right?

Retrieving the data is relatively easy. Just add this code:

     public function sodiumStaticDecrypt(string $data) {
           // decode the base64 on $data first
           $decrypt = base64_decode($data);
           $decrypted = sodium_crypto_secretbox_open($data,$this->nonce,$this->key);
           return $decrypted;
     }

Therefore:

<?php

echo $site->sodiumStaticDecrypt('5Was2l9V0RgsgEm5csEbXnCaIxLkWwWiOxnHBgrej9+Ipyqn4ehn8VImFa8=');

and it will return to the plain text: It's better late than never!.

No matter what you do, as long you don't change the key and nonce, the encryption will never change. It's like a one is to one encryption. Just make sure no one knows the key and nonce because everything will become useless. Don't forget NONCE should be 24 characters long, not more, not less; and KEY should be 32 characters long, not more, not less.

The regular sodium random encryption can also be used (in saving email, usernames, etc.) no matter the nonce and key are randomized every time. But this is not the question. Anyway, I recommend not to make it static because it is designed not be one.

Well, I hope you already solved the problem and I hope this helps somehow (even if I really do think, it does not).

Tesmurd101
  • 21
  • 2
0

Only one comment on @Tesmurd101. In the sodiumStaticDecrypt the following line

$decrypted = sodium_crypto_secretbox_open($data,$this->nonce,$this->key);

should be

$decrypted = sodium_crypto_secretbox_open($decrypt,$this->nonce,$this->key);
  • 1
    As the code line your'e quoting is in the public function sodiumStaticDecrypt(string $data) the $data variable refers to the $data-statement in the function header and it is correct. Of course it would be better readable if both variables were named $encrypt :-) – Michael Fehr Jul 01 '20 at 22:06