Using the Sodium Lib in PHP functions

831 Views Asked by At

I'm going to try posting on this site again. My previous questions have been ignored or criticized because I guess people didn't think I had put much work into trying to figure out a problem Vs just looking for some easy code to copy!

My goal is to write PHP functions that can be called to encrypt and decrypt data to be stored on a server with the encryption key being stored on a USB dongle. I have been playing around with Sodium but there doesn't seem to be a lot of information on the web about it. I'm also not a highly educated programmer since I have taught myself everything I know.

Here is the sample test code:

<?php

//set variables
$key="";
$passedkey="";
$name="";
$nonce="";
$ciphertext="";
$encoded="";
$encodeddata="";
$datatoencode="";
$decodeddata="";
$decodedkey="";

function encodedata($passedkey, $datatoencode){
    $decodedkey = base64_decode($passedkey);    
    $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
    $ciphertext = sodium_crypto_secretbox($datatoencode, $nonce, $decodedkey);
    $encodeddata = base64_encode($nonce . $ciphertext);
}

function decodedata($passedkey, $datatodecode, $decodeddata){
    $decodedkey = base64_decode($passedkey);
    $nonce = mb_substr($datatodecode, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
    $ciphertext = mb_substr($datatodecode, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
    $decodeddata = sodium_crypto_secretbox_open($ciphertext, $nonce, $decodedkey);
}

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // collect value of input field
    $encodedata = $_POST['encodedata'];
    $passedkey = base64_decode($_POST['skey']);
    echo $encodeddata . "<br>";

    //call function to encode the data
    encodedata($passedkey, $datatoencode);

    // now decode the data
    decodedata($passedkey, $encodeddata, $decodeddata);
    echo $decodeddata;
}

?>




<html>
<body>

<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
  Data to encode: <input type="text" name="encodedata">
  Key: <input type="text" name="skey">
  <input type="submit">
</form>


</body>
</html>

I get this error message in the server error log:

[06-Mar-2020 16:16:11 UTC] PHP Fatal error:  Uncaught SodiumException: key size should be SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes in /home/.../test/keytest.php:17
Stack trace:
#0 /home/.../test/keytest.php(17): sodium_crypto_secretbox()
#1 /home/.../test/keytest.php(35): encodedata()
#2 {main}
  thrown in /home/.../test/keytest.php on line 17
[06-Mar-2020 16:16:24 UTC] PHP Fatal error:  Uncaught SodiumException: key size should be SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes in /home/.../test/keytest.php:17
Stack trace:
#0 /home/.../test/keytest.php(17): sodium_crypto_secretbox()
#1 /home/.../test/keytest.php(35): encodedata()
#2 {main}
  thrown in /home/.../test/keytest.php on line 17
[06-Mar-2020 16:30:38 UTC] PHP Fatal error:  Uncaught SodiumException: key size should be SODIUM_CRYPTO_SECRETBOX_KEYBYTES bytes in /home/.../test/keytest.php:18
Stack trace:
#0 /home/.../test/keytest.php(18): sodium_crypto_secretbox()
#1 /home/.../test/keytest.php(36): encodedata()
#2 {main}
  thrown in /home/.../test/keytest.php on line 18

Any tips to help debug the code would be appreciated!

1

There are 1 best solutions below

1
Tesmurd101 On

Your variables are way off. There are a few things that are off in this code, and quite honestly, it took me a few minutes to notice that your $encodedata was not set to function encodedata(). Anyhow, it was my bad that I trusted that your variables are already set. Please bear with me because this is quite long of an explanation.

The first thing I noticed is that you decoded $passedkey twice (or thrice, if both functions counted). When you call the function/s, in if($_SERVER("REQUEST_METHOD") == "POST"), $passedkey was already decoded via base64_decode and is placed in a variable $passedkey. In both functions, encodedata and decodedata, you decoded it again, hence an error when you try to call the functions. Since the parameter $passedkey's value is the already decoded value before the function has been reached, then there is not a need to decode it again. It's redundant like me. Therefore, $decodedkey = base64_decode($passedkey); is no longer needed and $decodedkey variables should be changed to $passedkey on both function.

Second is that your $encodedata is not called. In if($_SERVER("REQUEST_METHOD") == "POST"), $encodedata's value is $_POST['encodedata'], hence the value of input Data to Encode. Yet, when you call the function, encodedata, $encodedata is nowhere to be found.

Third problem is that your function is not "returning" anything. You can't expect echo $encodeddata; or echo $decodeddata; to return a value from the "inside" of a function. Well, I don't know if you are using a platform that can, but there you go.

Basically, the problem with the example code is wrong placement of variables and not paying attention to the values and parameters. Anyhow, my advice is that the saved skey in a USB Dongle should be in base64_encode() and is the proper sodium key value, otherwise, this won't work.

Here is an edited code, I don't know if this will work in your end, but it does in mine.

<?php

//set variables
$key="";
$passedkey="";
$name="";
$nonce="";
$ciphertext="";
$encoded="";
$encodeddata="";
$datatoencode="";
$decodeddata="";
$decodedkey="";

function encodedata($passedkey, $datatoencode){
    //no need to decode $passedkey since it is already decoded before calling the function
        //you can still retain $decodedkey = base64_decode($passedkey) but should REPLACE $passedkey = base64_decode($_POST['skey']); WITH $passedkey = $_POST['skey']; below
    $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
    //change $decodedkey to $passedkey
    $ciphertext = sodium_crypto_secretbox($datatoencode, $nonce, $passedkey);
    $encodeddata = base64_encode($nonce . $ciphertext);
    //return the encrypted data when the function is called
    return $encodeddata;
}

function decodedata($passedkey, $datatodecode){
    //decode the encrypted data since it is stored as base64_encode()
        //you can still retain $decodedkey = base64_decode($passedkey) but should REPLACE $passedkey = base64_decode($_POST['skey']); WITH $passedkey = $_POST['skey']; below
    $datatodecode = base64_decode($datatodecode);
    $nonce = mb_substr($datatodecode, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
    $ciphertext = mb_substr($datatodecode, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
    //change $decodedkey to $passedkey
    $decodeddata = sodium_crypto_secretbox_open($ciphertext, $nonce, $passedkey);
    //return the plaintext when the function is called
    return $decodeddata;
}

if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // collect value of input field
    $encodedata = $_POST['encodedata'];
    $passedkey = base64_decode($_POST['skey']);

    //call function to encode the data
        //you can't fetch the value of a variable from outside of the function hence we will make the function itself a variable.
        //the returned data from the function is now the value of the variabl
    $encrypted = encodedata($passedkey, $encodedata);
    echo $encrypted.'<br />';
    

    // now decode the data
    $decypted = decodedata($passedkey, $encrypted);
    echo $decypted.'<br />';
}

?>




<html>
<body>

<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
  Data to encode: <input type="text" name="encodedata">
  Key: <input type="text" name="skey">
  <input type="submit">
</form>


</body>
</html>

I don't know what you mean or what exactly you want to do, but that's the way to make your given code work.