Skip to content

Commit

Permalink
sodium ext: update the crypto_kx_*() API to the libsodium one
Browse files Browse the repository at this point in the history
The crypto_kx API initially present in the PHP bindings was a prototype
that was not part of libsodium.

This implements the one from libsodium >= 1.0.12.

We can later leverage the native libsodium functions if we decide that
1.0.12 is the minimum version we want to support.
  • Loading branch information
Frank Denis authored and krakjoe committed Jul 12, 2017
1 parent dfac71d commit 6ac6d2c
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 65 deletions.
245 changes: 206 additions & 39 deletions ext/sodium/libsodium.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,16 @@ ZEND_BEGIN_ARG_INFO_EX(AI_MaybeKeyAndLength, 0, 0, 0)
ZEND_ARG_INFO(0, length)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(AI_KXClientSession, 0, 0, 2)
ZEND_ARG_INFO(0, client_keypair)
ZEND_ARG_INFO(0, server_key)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(AI_KXServerSession, 0, 0, 2)
ZEND_ARG_INFO(0, server_keypair)
ZEND_ARG_INFO(0, client_key)
ZEND_END_ARG_INFO()

#if defined(HAVE_CRYPTO_AEAD_AES256GCM) && defined(crypto_aead_aes256gcm_KEYBYTES) && \
(defined(__amd64) || defined(__amd64__) || defined(__x86_64__) || defined(__i386__) || \
defined(_M_AMD64) || defined(_M_IX86))
Expand Down Expand Up @@ -195,7 +205,12 @@ const zend_function_entry sodium_functions[] = {
PHP_FE(sodium_crypto_box_seal_open, AI_StringAndKey)
#endif
PHP_FE(sodium_crypto_box_secretkey, AI_Key)
PHP_FE(sodium_crypto_kx, AI_FourStrings)
PHP_FE(sodium_crypto_kx_keypair, AI_None)
PHP_FE(sodium_crypto_kx_publickey, AI_Key)
PHP_FE(sodium_crypto_kx_secretkey, AI_Key)
PHP_FE(sodium_crypto_kx_seed_keypair, AI_String)
PHP_FE(sodium_crypto_kx_client_session_keys, AI_KXClientSession)
PHP_FE(sodium_crypto_kx_server_session_keys, AI_KXServerSession)
PHP_FE(sodium_crypto_generichash, AI_StringAndMaybeKeyAndLength)
PHP_FE(sodium_crypto_generichash_init, AI_MaybeKeyAndLength)
PHP_FE(sodium_crypto_generichash_update, AI_StateByReferenceAndString)
Expand Down Expand Up @@ -368,12 +383,23 @@ PHP_MINIT_FUNCTION(sodium)
crypto_box_NONCEBYTES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_SEEDBYTES",
crypto_box_SEEDBYTES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_BYTES",
crypto_kx_BYTES, CONST_CS | CONST_PERSISTENT);
#ifndef crypto_kx_SEEDBYTES
# define crypto_kx_SEEDBYTES 32
# define crypto_kx_SESSIONKEYBYTES 32
# define crypto_kx_PUBLICKEYBYTES 32
# define crypto_kx_SECRETKEYBYTES 32
#endif
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_SEEDBYTES",
crypto_kx_SEEDBYTES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_SESSIONKEYBYTES",
crypto_kx_SESSIONKEYBYTES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_PUBLICKEYBYTES",
crypto_kx_PUBLICKEYBYTES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_SECRETKEYBYTES",
crypto_kx_SECRETKEYBYTES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_KEYPAIRBYTES",
crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES,
CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_GENERICHASH_BYTES",
crypto_generichash_BYTES, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_GENERICHASH_BYTES_MIN",
Expand Down Expand Up @@ -2480,56 +2506,197 @@ PHP_FUNCTION(sodium_crypto_scalarmult)
RETURN_STR(q);
}

PHP_FUNCTION(sodium_crypto_kx)
PHP_FUNCTION(sodium_crypto_kx_seed_keypair)
{
unsigned char *sk;
unsigned char *pk;
unsigned char *seed;
size_t seed_len;
zend_string *keypair;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
&seed, &seed_len) == FAILURE) {
return;
}
if (seed_len != crypto_kx_SEEDBYTES) {
zend_throw_exception(sodium_exception_ce, "seed must be CRYPTO_KX_SEEDBYTES bytes", 0);
return;
}
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_PUBLICKEYBYTES ? 1 : -1]);
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_SECRETKEYBYTES ? 1 : -1]);
keypair = zend_string_alloc(crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES, 0);
sk = (unsigned char *) ZSTR_VAL(keypair);
pk = sk + crypto_kx_SECRETKEYBYTES;
crypto_generichash(sk, crypto_kx_SECRETKEYBYTES,
seed, crypto_kx_SEEDBYTES, NULL, 0);
if (crypto_scalarmult_base(pk, sk) != 0) {
zend_throw_exception(sodium_exception_ce, "internal error", 0);
return;
}
ZSTR_VAL(keypair)[crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES] = 0;
RETURN_STR(keypair);
}

PHP_FUNCTION(sodium_crypto_kx_keypair)
{
unsigned char *sk;
unsigned char *pk;
zend_string *keypair;

keypair = zend_string_alloc(crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES, 0);
sk = (unsigned char *) ZSTR_VAL(keypair);
pk = sk + crypto_kx_SECRETKEYBYTES;
randombytes_buf(sk, crypto_kx_SECRETKEYBYTES);
if (crypto_scalarmult_base(pk, sk) != 0) {
zend_throw_exception(sodium_exception_ce, "internal error", 0);
return;
}
RETURN_STR(keypair);
}

PHP_FUNCTION(sodium_crypto_kx_secretkey)
{
zend_string *secretkey;
unsigned char *keypair;
size_t keypair_len;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
&keypair, &keypair_len) == FAILURE) {
return;
}
if (keypair_len !=
crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES) {
zend_throw_exception(sodium_exception_ce,
"keypair should be CRYPTO_KX_KEYPAIRBYTES bytes",
0);
return;
}
secretkey = zend_string_alloc(crypto_kx_SECRETKEYBYTES, 0);
memcpy(ZSTR_VAL(secretkey), keypair, crypto_kx_SECRETKEYBYTES);
ZSTR_VAL(secretkey)[crypto_kx_SECRETKEYBYTES] = 0;

RETURN_STR(secretkey);
}

PHP_FUNCTION(sodium_crypto_kx_publickey)
{
zend_string *publickey;
unsigned char *keypair;
size_t keypair_len;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
&keypair, &keypair_len) == FAILURE) {
return;
}
if (keypair_len !=
crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES) {
zend_throw_exception(sodium_exception_ce,
"keypair should be CRYPTO_KX_KEYPAIRBYTES bytes",
0);
return;
}
publickey = zend_string_alloc(crypto_kx_PUBLICKEYBYTES, 0);
memcpy(ZSTR_VAL(publickey), keypair + crypto_kx_SECRETKEYBYTES,
crypto_kx_PUBLICKEYBYTES);
ZSTR_VAL(publickey)[crypto_kx_PUBLICKEYBYTES] = 0;

RETURN_STR(publickey);
}

PHP_FUNCTION(sodium_crypto_kx_client_session_keys)
{
crypto_generichash_state h;
unsigned char q[crypto_scalarmult_BYTES];
zend_string *sharedkey;
unsigned char *client_publickey;
unsigned char *publickey;
unsigned char *secretkey;
unsigned char *server_publickey;
size_t client_publickey_len;
size_t publickey_len;
size_t secretkey_len;
size_t server_publickey_len;
unsigned char q[crypto_scalarmult_BYTES];
unsigned char *keypair;
unsigned char *client_sk;
unsigned char *client_pk;
unsigned char *server_pk;
unsigned char session_keys[2 * crypto_kx_SESSIONKEYBYTES];
size_t keypair_len;
size_t server_pk_len;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
&secretkey, &secretkey_len,
&publickey, &publickey_len,
&client_publickey, &client_publickey_len,
&server_publickey, &server_publickey_len) == FAILURE) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
&keypair, &keypair_len,
&server_pk, &server_pk_len) == FAILURE) {
return;
}
if (secretkey_len != crypto_kx_SECRETKEYBYTES) {
zend_throw_exception(sodium_exception_ce, "crypto_kx(): secret key must be CRYPTO_KX_SECRETKEY bytes", 0);
if (keypair_len != crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES) {
zend_throw_exception(sodium_exception_ce, "keypair must be CRYPTO_KX_KEYPAIRBYTES bytes", 0);
return;
}
if (publickey_len != crypto_kx_PUBLICKEYBYTES ||
client_publickey_len != crypto_kx_PUBLICKEYBYTES ||
server_publickey_len != crypto_kx_PUBLICKEYBYTES) {
zend_throw_exception(sodium_exception_ce, "crypto_kx(): public keys must be CRYPTO_KX_PUBLICKEY bytes", 0);
if (server_pk_len != crypto_kx_PUBLICKEYBYTES) {
zend_throw_exception(sodium_exception_ce, "public keys must be CRYPTO_KX_PUBLICKEYBYTES bytes", 0);
return;
}
(void) sizeof(int[crypto_scalarmult_SCALARBYTES ==
crypto_kx_PUBLICKEYBYTES ? 1 : -1]);
(void) sizeof(int[crypto_scalarmult_SCALARBYTES ==
crypto_kx_SECRETKEYBYTES ? 1 : -1]);
if (crypto_scalarmult(q, secretkey, publickey) != 0) {
zend_throw_exception(sodium_exception_ce, "crypto_kx(): internal error", 0);
client_sk = &keypair[0];
client_pk = &keypair[crypto_kx_SECRETKEYBYTES];
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_PUBLICKEYBYTES ? 1 : -1]);
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_SECRETKEYBYTES ? 1 : -1]);
if (crypto_scalarmult(q, client_sk, server_pk) != 0) {
zend_throw_exception(sodium_exception_ce, "internal error", 0);
return;
}
sharedkey = zend_string_alloc(crypto_kx_BYTES, 0);
crypto_generichash_init(&h, NULL, 0U, crypto_generichash_BYTES);
crypto_generichash_init(&h, NULL, 0U, 2 * crypto_kx_SESSIONKEYBYTES);
crypto_generichash_update(&h, q, sizeof q);
sodium_memzero(q, sizeof q);
crypto_generichash_update(&h, client_publickey, client_publickey_len);
crypto_generichash_update(&h, server_publickey, server_publickey_len);
crypto_generichash_final(&h, (unsigned char *) ZSTR_VAL(sharedkey),
crypto_kx_BYTES);
ZSTR_VAL(sharedkey)[crypto_kx_BYTES] = 0;
crypto_generichash_update(&h, client_pk, crypto_kx_PUBLICKEYBYTES);
crypto_generichash_update(&h, server_pk, crypto_kx_PUBLICKEYBYTES);
crypto_generichash_final(&h, session_keys, 2 * crypto_kx_SESSIONKEYBYTES);
array_init(return_value);
add_next_index_stringl(return_value,
(const char *) session_keys,
crypto_kx_SESSIONKEYBYTES);
add_next_index_stringl(return_value,
(const char *) session_keys + crypto_kx_SESSIONKEYBYTES,
crypto_kx_SESSIONKEYBYTES);
}

RETURN_STR(sharedkey);
PHP_FUNCTION(sodium_crypto_kx_server_session_keys)
{
crypto_generichash_state h;
unsigned char q[crypto_scalarmult_BYTES];
unsigned char *keypair;
unsigned char *server_sk;
unsigned char *server_pk;
unsigned char *client_pk;
unsigned char session_keys[2 * crypto_kx_SESSIONKEYBYTES];
size_t keypair_len;
size_t client_pk_len;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
&keypair, &keypair_len,
&client_pk, &client_pk_len) == FAILURE) {
return;
}
if (keypair_len != crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES) {
zend_throw_exception(sodium_exception_ce, "keypair must be CRYPTO_KX_KEYPAIRBYTES bytes", 0);
return;
}
if (client_pk_len != crypto_kx_PUBLICKEYBYTES) {
zend_throw_exception(sodium_exception_ce, "public keys must be CRYPTO_KX_PUBLICKEYBYTES bytes", 0);
return;
}
server_sk = &keypair[0];
server_pk = &keypair[crypto_kx_SECRETKEYBYTES];
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_PUBLICKEYBYTES ? 1 : -1]);
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_SECRETKEYBYTES ? 1 : -1]);
if (crypto_scalarmult(q, server_sk, client_pk) != 0) {
zend_throw_exception(sodium_exception_ce, "internal error", 0);
return;
}
crypto_generichash_init(&h, NULL, 0U, 2 * crypto_kx_SESSIONKEYBYTES);
crypto_generichash_update(&h, q, sizeof q);
sodium_memzero(q, sizeof q);
crypto_generichash_update(&h, client_pk, crypto_kx_PUBLICKEYBYTES);
crypto_generichash_update(&h, server_pk, crypto_kx_PUBLICKEYBYTES);
crypto_generichash_final(&h, session_keys, 2 * crypto_kx_SESSIONKEYBYTES);
array_init(return_value);
add_next_index_stringl(return_value,
(const char *) session_keys + crypto_kx_SESSIONKEYBYTES,
crypto_kx_SESSIONKEYBYTES);
add_next_index_stringl(return_value,
(const char *) session_keys,
crypto_kx_SESSIONKEYBYTES);
}

PHP_FUNCTION(sodium_crypto_auth)
Expand Down
7 changes: 6 additions & 1 deletion ext/sodium/php_libsodium.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ PHP_FUNCTION(sodium_crypto_generichash);
PHP_FUNCTION(sodium_crypto_generichash_final);
PHP_FUNCTION(sodium_crypto_generichash_init);
PHP_FUNCTION(sodium_crypto_generichash_update);
PHP_FUNCTION(sodium_crypto_kx);
PHP_FUNCTION(sodium_crypto_kx_client_session_keys);
PHP_FUNCTION(sodium_crypto_kx_keypair);
PHP_FUNCTION(sodium_crypto_kx_publickey);
PHP_FUNCTION(sodium_crypto_kx_secretkey);
PHP_FUNCTION(sodium_crypto_kx_seed_keypair);
PHP_FUNCTION(sodium_crypto_kx_server_session_keys);
PHP_FUNCTION(sodium_crypto_pwhash);
PHP_FUNCTION(sodium_crypto_pwhash_str);
PHP_FUNCTION(sodium_crypto_pwhash_str_verify);
Expand Down
47 changes: 22 additions & 25 deletions ext/sodium/tests/crypto_kx.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,31 @@ Check for libsodium-based key exchange
<?php if (!extension_loaded("sodium")) print "skip"; ?>
--FILE--
<?php
$client_secretkey = sodium_hex2bin("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
$client_publickey = sodium_crypto_box_publickey_from_secretkey($client_secretkey);
$client_seed = sodium_hex2bin('0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef');
$client_keypair = sodium_crypto_kx_seed_keypair($client_seed);
$server_seed = sodium_hex2bin('f123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde0');
$server_keypair = sodium_crypto_kx_seed_keypair($server_seed);

$server_secretkey = sodium_hex2bin("948f00e90a246fb5909f8648c2ac6f21515771235523266439e0d775ba0c3671");
$server_publickey = sodium_crypto_box_publickey_from_secretkey($server_secretkey);
var_dump(sodium_bin2hex($client_keypair));
var_dump(sodium_bin2hex($server_keypair));

$shared_key_computed_by_client =
sodium_crypto_kx($client_secretkey, $server_publickey,
$client_publickey, $server_publickey);
$client_session_keys =
sodium_crypto_kx_client_session_keys($client_keypair,
sodium_crypto_kx_publickey($server_keypair));

$shared_key_computed_by_server =
sodium_crypto_kx($server_secretkey, $client_publickey,
$client_publickey, $server_publickey);
$server_session_keys =
sodium_crypto_kx_server_session_keys($server_keypair,
sodium_crypto_kx_publickey($client_keypair));

var_dump(sodium_bin2hex($shared_key_computed_by_client));
var_dump(sodium_bin2hex($shared_key_computed_by_server));
try {
sodium_crypto_kx(
substr($client_secretkey, 1),
$server_publickey,
$client_publickey,
$server_publickey
);
} catch (SodiumException $ex) {
var_dump(true);
}
var_dump(sodium_bin2hex($client_session_keys[0]));
var_dump(sodium_bin2hex($server_session_keys[1]));
var_dump(sodium_bin2hex($client_session_keys[1]));
var_dump(sodium_bin2hex($server_session_keys[0]));
?>
--EXPECT--
string(64) "509a1580c2ee30c565317e29e0fea0b1c232e0ef3a7871d91dc64814b19a3bd2"
string(64) "509a1580c2ee30c565317e29e0fea0b1c232e0ef3a7871d91dc64814b19a3bd2"
bool(true)
string(128) "b85c84f9828524519d32b97cd3dda961fdba2dbf407ae4601e2129229aa463c224eaf70f070a925d6d5176f20495d4d90867624d9a10379e2a9aef0955c9bf4e"
string(128) "016e814c32b8b66225a403db45bf50fdd1966fb802c3115bf8aa90738c6a02de420ccdb534930fed9aaff12188bedc76e66251f399c404f2e4a15678fd4a484a"
string(64) "99a430e61d718b71979ebcea6735c4648bc828cfb456890aeda4b628b77d5ac7"
string(64) "99a430e61d718b71979ebcea6735c4648bc828cfb456890aeda4b628b77d5ac7"
string(64) "876bef865a5ab3f4ae569ea5aaefe5014c3ec22a558c0a2f0274aa9985bd328d"
string(64) "876bef865a5ab3f4ae569ea5aaefe5014c3ec22a558c0a2f0274aa9985bd328d"

0 comments on commit 6ac6d2c

Please sign in to comment.