Skip to content

Add crypto_stream_xchacha20 to ext/sodium #6868

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions ext/sodium/libsodium.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ static zend_class_entry *sodium_exception_ce;
# define HAVE_AESGCM 1
#endif

static zend_always_inline zend_string *zend_string_checked_alloc(size_t len, int persistent)
{
zend_string *zs;

if (ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)) < len) {
zend_error_noreturn(E_ERROR, "Memory allocation too large (%zu bytes)", len);
}
zs = zend_string_alloc(len, persistent);
ZSTR_VAL(zs)[len] = 0;

return zs;
}

#include "libsodium_arginfo.h"

#ifndef crypto_aead_chacha20poly1305_IETF_KEYBYTES
Expand Down Expand Up @@ -335,6 +348,13 @@ PHP_MINIT_FUNCTION(sodium)
crypto_stream_NONCEBYTES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_STREAM_KEYBYTES",
crypto_stream_KEYBYTES, CONST_CS | CONST_PERSISTENT);

#ifdef crypto_stream_xchacha20_KEYBYTES
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES",
crypto_stream_xchacha20_NONCEBYTES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES",
crypto_stream_xchacha20_KEYBYTES, CONST_CS | CONST_PERSISTENT);
#endif
#ifdef sodium_base64_VARIANT_ORIGINAL
REGISTER_LONG_CONSTANT("SODIUM_BASE64_VARIANT_ORIGINAL",
sodium_base64_VARIANT_ORIGINAL, CONST_CS | CONST_PERSISTENT);
Expand Down Expand Up @@ -1465,6 +1485,87 @@ PHP_FUNCTION(sodium_crypto_stream_xor)
RETURN_NEW_STR(ciphertext);
}

#ifdef crypto_stream_xchacha20_KEYBYTES
PHP_FUNCTION(sodium_crypto_stream_xchacha20)
{
zend_string *ciphertext;
unsigned char *key;
unsigned char *nonce;
zend_long ciphertext_len;
size_t key_len;
size_t nonce_len;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "lss",
&ciphertext_len,
&nonce, &nonce_len,
&key, &key_len) == FAILURE) {
sodium_remove_param_values_from_backtrace(EG(exception));
RETURN_THROWS();
}
if (ciphertext_len <= 0 || ciphertext_len >= SIZE_MAX) {
zend_argument_error(sodium_exception_ce, 1, "length must be greater than 0");
RETURN_THROWS();
}
if (nonce_len != crypto_stream_xchacha20_NONCEBYTES) {
zend_argument_error(sodium_exception_ce, 2, "nonce must be SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES bytes long");
RETURN_THROWS();
}
if (key_len != crypto_stream_xchacha20_KEYBYTES) {
zend_argument_error(sodium_exception_ce, 3, "key must be SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES bytes long");
RETURN_THROWS();
}
ciphertext = zend_string_checked_alloc((size_t) ciphertext_len, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, it looks like this function was added in the PECL extension in jedisct1/libsodium-php@75701f2, but is missing here...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

./configure doesn't work locally, otherwise we would have caught these without having to depend on CI.

if (crypto_stream_xchacha20((unsigned char *) ZSTR_VAL(ciphertext),
(unsigned long long) ciphertext_len, nonce, key) != 0) {
zend_string_free(ciphertext);
zend_throw_exception(sodium_exception_ce, "internal error", 0);
RETURN_THROWS();
}
ZSTR_VAL(ciphertext)[ciphertext_len] = 0;

RETURN_NEW_STR(ciphertext);
}

PHP_FUNCTION(sodium_crypto_stream_xchacha20_xor)
{
zend_string *ciphertext;
unsigned char *key;
unsigned char *msg;
unsigned char *nonce;
size_t ciphertext_len;
size_t key_len;
size_t msg_len;
size_t nonce_len;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "sss",
&msg, &msg_len,
&nonce, &nonce_len,
&key, &key_len) == FAILURE) {
sodium_remove_param_values_from_backtrace(EG(exception));
RETURN_THROWS();
}
if (nonce_len != crypto_stream_xchacha20_NONCEBYTES) {
zend_argument_error(sodium_exception_ce, 2, "nonce must be SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES bytes long");
RETURN_THROWS();
}
if (key_len != crypto_stream_xchacha20_KEYBYTES) {
zend_argument_error(sodium_exception_ce, 3, "key must be SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES bytes long");
RETURN_THROWS();
}
ciphertext_len = msg_len;
ciphertext = zend_string_checked_alloc((size_t) ciphertext_len, 0);
if (crypto_stream_xchacha20_xor((unsigned char *) ZSTR_VAL(ciphertext), msg,
(unsigned long long) msg_len, nonce, key) != 0) {
zend_string_free(ciphertext);
zend_throw_exception(sodium_exception_ce, "internal error", 0);
RETURN_THROWS();
}
ZSTR_VAL(ciphertext)[ciphertext_len] = 0;

RETURN_NEW_STR(ciphertext);
}
#endif

#ifdef crypto_pwhash_SALTBYTES
PHP_FUNCTION(sodium_crypto_pwhash)
{
Expand Down Expand Up @@ -2894,6 +2995,18 @@ PHP_FUNCTION(sodium_crypto_stream_keygen)
randombytes_buf(key, sizeof key);
RETURN_STRINGL((const char *) key, sizeof key);
}
#ifdef crypto_stream_xchacha20_KEYBYTES
PHP_FUNCTION(sodium_crypto_stream_xchacha20_keygen)
{
unsigned char key[crypto_stream_xchacha20_KEYBYTES];

if (zend_parse_parameters_none() == FAILURE) {
return;
}
randombytes_buf(key, sizeof key);
RETURN_STRINGL((const char *) key, sizeof key);
}
#endif

PHP_FUNCTION(sodium_crypto_kdf_derive_from_key)
{
Expand Down
8 changes: 8 additions & 0 deletions ext/sodium/libsodium.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ function sodium_crypto_stream_keygen(): string {}

function sodium_crypto_stream_xor(string $message, string $nonce, string $key): string {}

#if defined(crypto_stream_xchacha20_KEYBYTES)
function sodium_crypto_stream_xchacha20(int $length, string $nonce, string $key) : string {}

function sodium_crypto_stream_xchacha20_keygen() : string {}

function sodium_crypto_stream_xchacha20_xor(string $message, string $nonce, string $key) : string {}
#endif

function sodium_add(string &$string1, string $string2): void {}

function sodium_compare(string $string1, string $string2): int {}
Expand Down
41 changes: 40 additions & 1 deletion ext/sodium/libsodium_arginfo.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 457c4c5a0243f815d859bdc9728709b4a8dc84d7 */
* Stub hash: 55ce0e93db5fac4311ba90693668a92001167573 */

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_aead_aes256gcm_is_available, 0, 0, _IS_BOOL, 0)
ZEND_END_ARG_INFO()
Expand Down Expand Up @@ -348,6 +348,27 @@ ZEND_END_ARG_INFO()

#define arginfo_sodium_crypto_stream_xor arginfo_sodium_crypto_secretbox

#if defined(crypto_stream_xchacha20_KEYBYTES)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_stream_xchacha20, 0, 3, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, nonce, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_END_ARG_INFO()
#endif

#if defined(crypto_stream_xchacha20_KEYBYTES)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_stream_xchacha20_keygen, 0, 0, IS_STRING, 0)
ZEND_END_ARG_INFO()
#endif

#if defined(crypto_stream_xchacha20_KEYBYTES)
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_stream_xchacha20_xor, 0, 3, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, nonce, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
ZEND_END_ARG_INFO()
#endif
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a generated file, run build/gen_stub.php ext/sodium to regenerate.

Copy link
Contributor Author

@paragonie-security paragonie-security Apr 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function isn't guaranteed to exist on older versions (1.0.11 and earlier) of libsodium. Does build/gen_stub.php need to take this into consideration?

3v4l.org in particular seems to use an ancient version of the library (possibly 1.0.8).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you need to add the corresponding #ifdef in the stub file as well. They will be transferred to the generated output.


ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_add, 0, 2, IS_VOID, 0)
ZEND_ARG_TYPE_INFO(1, string1, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, string2, IS_STRING, 0)
Expand Down Expand Up @@ -514,6 +535,15 @@ ZEND_FUNCTION(sodium_crypto_sign_verify_detached);
ZEND_FUNCTION(sodium_crypto_stream);
ZEND_FUNCTION(sodium_crypto_stream_keygen);
ZEND_FUNCTION(sodium_crypto_stream_xor);
#if defined(crypto_stream_xchacha20_KEYBYTES)
ZEND_FUNCTION(sodium_crypto_stream_xchacha20);
#endif
#if defined(crypto_stream_xchacha20_KEYBYTES)
ZEND_FUNCTION(sodium_crypto_stream_xchacha20_keygen);
#endif
#if defined(crypto_stream_xchacha20_KEYBYTES)
ZEND_FUNCTION(sodium_crypto_stream_xchacha20_xor);
#endif
ZEND_FUNCTION(sodium_add);
ZEND_FUNCTION(sodium_compare);
ZEND_FUNCTION(sodium_increment);
Expand Down Expand Up @@ -643,6 +673,15 @@ static const zend_function_entry ext_functions[] = {
ZEND_FE(sodium_crypto_stream, arginfo_sodium_crypto_stream)
ZEND_FE(sodium_crypto_stream_keygen, arginfo_sodium_crypto_stream_keygen)
ZEND_FE(sodium_crypto_stream_xor, arginfo_sodium_crypto_stream_xor)
#if defined(crypto_stream_xchacha20_KEYBYTES)
ZEND_FE(sodium_crypto_stream_xchacha20, arginfo_sodium_crypto_stream_xchacha20)
#endif
#if defined(crypto_stream_xchacha20_KEYBYTES)
ZEND_FE(sodium_crypto_stream_xchacha20_keygen, arginfo_sodium_crypto_stream_xchacha20_keygen)
#endif
#if defined(crypto_stream_xchacha20_KEYBYTES)
ZEND_FE(sodium_crypto_stream_xchacha20_xor, arginfo_sodium_crypto_stream_xchacha20_xor)
#endif
ZEND_FE(sodium_add, arginfo_sodium_add)
ZEND_FE(sodium_compare, arginfo_sodium_compare)
ZEND_FE(sodium_increment, arginfo_sodium_increment)
Expand Down
3 changes: 3 additions & 0 deletions ext/sodium/php_libsodium.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ PHP_FUNCTION(sodium_crypto_sign_verify_detached);
PHP_FUNCTION(sodium_crypto_stream);
PHP_FUNCTION(sodium_crypto_stream_keygen);
PHP_FUNCTION(sodium_crypto_stream_xor);
PHP_FUNCTION(sodium_crypto_stream_xchacha20);
PHP_FUNCTION(sodium_crypto_stream_xchacha20_keygen);
PHP_FUNCTION(sodium_crypto_stream_xchacha20_xor);
PHP_FUNCTION(sodium_hex2bin);
PHP_FUNCTION(sodium_increment);
PHP_FUNCTION(sodium_memcmp);
Expand Down
52 changes: 52 additions & 0 deletions ext/sodium/tests/crypto_stream_xchacha20.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
--TEST--
Check for libsodium stream
--SKIPIF--
<?php if (!extension_loaded("sodium") || !defined('CRYPTO_STREAM_XCHACHA20_KEYBYTES')) print "skip"; ?>
--FILE--
<?php
$nonce = random_bytes(SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES);
$key = sodium_crypto_stream_xchacha20_keygen();

$len = 100;
$stream = sodium_crypto_stream_xchacha20($len, $nonce, $key);
var_dump(strlen($stream));

$stream2 = sodium_crypto_stream_xchacha20($len, $nonce, $key);

$nonce = random_bytes(SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES);
$stream3 = sodium_crypto_stream_xchacha20($len, $nonce, $key);

$key = sodium_crypto_stream_keygen();
$stream4 = sodium_crypto_stream_xchacha20($len, $nonce, $key);

var_dump($stream === $stream2);
var_dump($stream !== $stream3);
var_dump($stream !== $stream4);
var_dump($stream2 !== $stream3);
var_dump($stream2 !== $stream4);
var_dump($stream3 !== $stream4);

$stream5 = sodium_crypto_stream_xchacha20_xor($stream, $nonce, $key);
var_dump($stream5 !== $stream);
$stream6 = sodium_crypto_stream_xchacha20_xor($stream5, $nonce, $key);

var_dump($stream6 === $stream);

try {
sodium_crypto_stream_xchacha20($len, substr($nonce, 1), $key);
} catch (SodiumException $ex) {
var_dump(true);
}

?>
--EXPECT--
int(100)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)