Skip to content

Commit afd8f84

Browse files
paragonie-securitycmb69
authored andcommitted
Add sodium_crypto_stream_xchacha20_xor_ic()
There are many use-cases where a PHP user is currently using sodium_compat's implementation of this low-level XChaCha20 API. For example, multi-part message processing (in low-memory settings) for a ciphertext that was encrypted with XChaCha20-Poly1305 (rather than the secretstream API). Adding this function to ext/sodium offers better performance and lowers users' memory usage with the polyfill, and ensures that users coming from other languages that provide libsodium bindings have a more consistent experience with our bindings. This is a win-win. This patch follows the libsodium precedent of adding functions instead of optional parameters to existing functions. The parameter order is also consistent with the C API. https://doc.libsodium.org/advanced/stream_ciphers/xchacha20#usage Closes GH-8276.
1 parent d260e06 commit afd8f84

File tree

6 files changed

+84
-0
lines changed

6 files changed

+84
-0
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ PHP NEWS
2626
. Fixed bug #80909 (crash with persistent connections in PDO_ODBC). (Calvin
2727
Buckley)
2828

29+
- Sodium:
30+
. Added sodium_crypto_stream_xchacha20_xor_ic(). (Scott)
31+
2932
- Standard:
3033
. net_get_interfaces() also reports wireless network interfaces on Windows.
3134
(Yurun)

UPGRADING

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ PHP 8.2 UPGRADE NOTES
114114
6. New Functions
115115
========================================
116116

117+
- Sodium:
118+
. sodium_crypto_stream_xchacha20_xor_ic()
119+
117120
- Standard:
118121
. The peak memory usage can now be reset to the current usage thanks to
119122
memory_reset_peak_usage().

ext/sodium/libsodium.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,6 +1578,49 @@ PHP_FUNCTION(sodium_crypto_stream_xchacha20_xor)
15781578

15791579
RETURN_NEW_STR(ciphertext);
15801580
}
1581+
1582+
PHP_FUNCTION(sodium_crypto_stream_xchacha20_xor_ic)
1583+
{
1584+
zend_string *ciphertext;
1585+
unsigned char *key;
1586+
unsigned char *msg;
1587+
unsigned char *nonce;
1588+
zend_long *ic;
1589+
1590+
size_t ciphertext_len;
1591+
size_t key_len;
1592+
size_t msg_len;
1593+
size_t nonce_len;
1594+
1595+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssls",
1596+
&msg, &msg_len,
1597+
&nonce, &nonce_len,
1598+
&ic,
1599+
&key, &key_len) == FAILURE) {
1600+
sodium_remove_param_values_from_backtrace(EG(exception));
1601+
RETURN_THROWS();
1602+
}
1603+
if (nonce_len != crypto_stream_xchacha20_NONCEBYTES) {
1604+
zend_argument_error(sodium_exception_ce, 2, "must be SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES bytes long");
1605+
RETURN_THROWS();
1606+
}
1607+
if (key_len != crypto_stream_xchacha20_KEYBYTES) {
1608+
zend_argument_error(sodium_exception_ce, 3, "must be SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES bytes long");
1609+
RETURN_THROWS();
1610+
}
1611+
ciphertext_len = msg_len;
1612+
ciphertext = zend_string_checked_alloc((size_t) ciphertext_len, 0);
1613+
if (crypto_stream_xchacha20_xor_ic((unsigned char *) ZSTR_VAL(ciphertext), msg,
1614+
(unsigned long long) msg_len, nonce,
1615+
(unsigned long long) ic, key) != 0) {
1616+
zend_string_free(ciphertext);
1617+
zend_throw_exception(sodium_exception_ce, "internal error", 0);
1618+
RETURN_THROWS();
1619+
}
1620+
ZSTR_VAL(ciphertext)[ciphertext_len] = 0;
1621+
1622+
RETURN_NEW_STR(ciphertext);
1623+
}
15811624
#endif
15821625

15831626
#ifdef crypto_pwhash_SALTBYTES

ext/sodium/libsodium.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,8 @@ function sodium_crypto_stream_xchacha20(int $length, string $nonce, string $key)
205205
function sodium_crypto_stream_xchacha20_keygen(): string {}
206206

207207
function sodium_crypto_stream_xchacha20_xor(string $message, string $nonce, string $key): string {}
208+
209+
function sodium_crypto_stream_xchacha20_xor_ic(string $message, string $nonce, int $counter, string $key): string {}
208210
#endif
209211

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

ext/sodium/libsodium_arginfo.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,15 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_stream_xchacha20_x
445445
ZEND_END_ARG_INFO()
446446
#endif
447447

448+
#if defined(crypto_stream_xchacha20_KEYBYTES)
449+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_crypto_stream_xchacha20_xor_ic, 0, 4, IS_STRING, 0)
450+
ZEND_ARG_TYPE_INFO(0, message, IS_STRING, 0)
451+
ZEND_ARG_TYPE_INFO(0, nonce, IS_STRING, 0)
452+
ZEND_ARG_TYPE_INFO(0, counter, IS_LONG, 0)
453+
ZEND_ARG_TYPE_INFO(0, key, IS_STRING, 0)
454+
ZEND_END_ARG_INFO()
455+
#endif
456+
448457
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sodium_add, 0, 2, IS_VOID, 0)
449458
ZEND_ARG_TYPE_INFO(1, string1, IS_STRING, 0)
450459
ZEND_ARG_TYPE_INFO(0, string2, IS_STRING, 0)
@@ -662,6 +671,9 @@ ZEND_FUNCTION(sodium_crypto_stream_xchacha20_keygen);
662671
#if defined(crypto_stream_xchacha20_KEYBYTES)
663672
ZEND_FUNCTION(sodium_crypto_stream_xchacha20_xor);
664673
#endif
674+
#if defined(crypto_stream_xchacha20_KEYBYTES)
675+
ZEND_FUNCTION(sodium_crypto_stream_xchacha20_xor_ic);
676+
#endif
665677
ZEND_FUNCTION(sodium_add);
666678
ZEND_FUNCTION(sodium_compare);
667679
ZEND_FUNCTION(sodium_increment);
@@ -844,6 +856,9 @@ static const zend_function_entry ext_functions[] = {
844856
#endif
845857
#if defined(crypto_stream_xchacha20_KEYBYTES)
846858
ZEND_FE(sodium_crypto_stream_xchacha20_xor, arginfo_sodium_crypto_stream_xchacha20_xor)
859+
#endif
860+
#if defined(crypto_stream_xchacha20_KEYBYTES)
861+
ZEND_FE(sodium_crypto_stream_xchacha20_xor_ic, arginfo_sodium_crypto_stream_xchacha20_xor_ic)
847862
#endif
848863
ZEND_FE(sodium_add, arginfo_sodium_add)
849864
ZEND_FE(sodium_compare, arginfo_sodium_compare)

ext/sodium/tests/crypto_stream_xchacha20.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,22 @@ $stream6 = sodium_crypto_stream_xchacha20_xor($stream5, $nonce, $key);
3434

3535
var_dump($stream6 === $stream);
3636

37+
// New test (with Initial Counter feature):
38+
$n2 = random_bytes(SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES);
39+
$left = str_repeat("\x01", 64);
40+
$right = str_repeat("\xfe", 64);
41+
42+
// All at once:
43+
$stream7_unified = sodium_crypto_stream_xchacha20_xor($left . $right, $n2, $key);
44+
45+
// Piecewise, with initial counter:
46+
$stream7_left = sodium_crypto_stream_xchacha20_xor_ic($left, $n2, 0, $key);
47+
$stream7_right = sodium_crypto_stream_xchacha20_xor_ic($right, $n2, 1, $key);
48+
$stream7_concat = $stream7_left . $stream7_right;
49+
50+
var_dump(strlen($stream7_concat));
51+
var_dump($stream7_unified === $stream7_concat);
52+
3753
try {
3854
sodium_crypto_stream_xchacha20(-1, $nonce, $key);
3955
} catch (SodiumException $ex) {
@@ -71,6 +87,8 @@ bool(true)
7187
bool(true)
7288
bool(true)
7389
bool(true)
90+
int(128)
91+
bool(true)
7492
sodium_crypto_stream_xchacha20(): Argument #1 ($length) must be greater than 0
7593
sodium_crypto_stream_xchacha20(): Argument #2 ($nonce) must be SODIUM_CRYPTO_STREAM_XCHACHA20_NONCEBYTES bytes long
7694
sodium_crypto_stream_xchacha20(): Argument #3 ($key) must be SODIUM_CRYPTO_STREAM_XCHACHA20_KEYBYTES bytes long

0 commit comments

Comments
 (0)