Skip to content

Commit 6ac6d2c

Browse files
Frank Deniskrakjoe
Frank Denis
authored andcommitted
sodium ext: update the crypto_kx_*() API to the libsodium one
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.
1 parent dfac71d commit 6ac6d2c

File tree

3 files changed

+234
-65
lines changed

3 files changed

+234
-65
lines changed

ext/sodium/libsodium.c

Lines changed: 206 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,16 @@ ZEND_BEGIN_ARG_INFO_EX(AI_MaybeKeyAndLength, 0, 0, 0)
159159
ZEND_ARG_INFO(0, length)
160160
ZEND_END_ARG_INFO()
161161

162+
ZEND_BEGIN_ARG_INFO_EX(AI_KXClientSession, 0, 0, 2)
163+
ZEND_ARG_INFO(0, client_keypair)
164+
ZEND_ARG_INFO(0, server_key)
165+
ZEND_END_ARG_INFO()
166+
167+
ZEND_BEGIN_ARG_INFO_EX(AI_KXServerSession, 0, 0, 2)
168+
ZEND_ARG_INFO(0, server_keypair)
169+
ZEND_ARG_INFO(0, client_key)
170+
ZEND_END_ARG_INFO()
171+
162172
#if defined(HAVE_CRYPTO_AEAD_AES256GCM) && defined(crypto_aead_aes256gcm_KEYBYTES) && \
163173
(defined(__amd64) || defined(__amd64__) || defined(__x86_64__) || defined(__i386__) || \
164174
defined(_M_AMD64) || defined(_M_IX86))
@@ -195,7 +205,12 @@ const zend_function_entry sodium_functions[] = {
195205
PHP_FE(sodium_crypto_box_seal_open, AI_StringAndKey)
196206
#endif
197207
PHP_FE(sodium_crypto_box_secretkey, AI_Key)
198-
PHP_FE(sodium_crypto_kx, AI_FourStrings)
208+
PHP_FE(sodium_crypto_kx_keypair, AI_None)
209+
PHP_FE(sodium_crypto_kx_publickey, AI_Key)
210+
PHP_FE(sodium_crypto_kx_secretkey, AI_Key)
211+
PHP_FE(sodium_crypto_kx_seed_keypair, AI_String)
212+
PHP_FE(sodium_crypto_kx_client_session_keys, AI_KXClientSession)
213+
PHP_FE(sodium_crypto_kx_server_session_keys, AI_KXServerSession)
199214
PHP_FE(sodium_crypto_generichash, AI_StringAndMaybeKeyAndLength)
200215
PHP_FE(sodium_crypto_generichash_init, AI_MaybeKeyAndLength)
201216
PHP_FE(sodium_crypto_generichash_update, AI_StateByReferenceAndString)
@@ -368,12 +383,23 @@ PHP_MINIT_FUNCTION(sodium)
368383
crypto_box_NONCEBYTES, CONST_CS | CONST_PERSISTENT);
369384
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_BOX_SEEDBYTES",
370385
crypto_box_SEEDBYTES, CONST_CS | CONST_PERSISTENT);
371-
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_BYTES",
372-
crypto_kx_BYTES, CONST_CS | CONST_PERSISTENT);
386+
#ifndef crypto_kx_SEEDBYTES
387+
# define crypto_kx_SEEDBYTES 32
388+
# define crypto_kx_SESSIONKEYBYTES 32
389+
# define crypto_kx_PUBLICKEYBYTES 32
390+
# define crypto_kx_SECRETKEYBYTES 32
391+
#endif
392+
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_SEEDBYTES",
393+
crypto_kx_SEEDBYTES, CONST_CS | CONST_PERSISTENT);
394+
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_SESSIONKEYBYTES",
395+
crypto_kx_SESSIONKEYBYTES, CONST_CS | CONST_PERSISTENT);
373396
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_PUBLICKEYBYTES",
374397
crypto_kx_PUBLICKEYBYTES, CONST_CS | CONST_PERSISTENT);
375398
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_SECRETKEYBYTES",
376399
crypto_kx_SECRETKEYBYTES, CONST_CS | CONST_PERSISTENT);
400+
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_KX_KEYPAIRBYTES",
401+
crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES,
402+
CONST_CS | CONST_PERSISTENT);
377403
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_GENERICHASH_BYTES",
378404
crypto_generichash_BYTES, CONST_CS | CONST_PERSISTENT);
379405
REGISTER_LONG_CONSTANT("SODIUM_CRYPTO_GENERICHASH_BYTES_MIN",
@@ -2480,56 +2506,197 @@ PHP_FUNCTION(sodium_crypto_scalarmult)
24802506
RETURN_STR(q);
24812507
}
24822508

2483-
PHP_FUNCTION(sodium_crypto_kx)
2509+
PHP_FUNCTION(sodium_crypto_kx_seed_keypair)
2510+
{
2511+
unsigned char *sk;
2512+
unsigned char *pk;
2513+
unsigned char *seed;
2514+
size_t seed_len;
2515+
zend_string *keypair;
2516+
2517+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
2518+
&seed, &seed_len) == FAILURE) {
2519+
return;
2520+
}
2521+
if (seed_len != crypto_kx_SEEDBYTES) {
2522+
zend_throw_exception(sodium_exception_ce, "seed must be CRYPTO_KX_SEEDBYTES bytes", 0);
2523+
return;
2524+
}
2525+
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_PUBLICKEYBYTES ? 1 : -1]);
2526+
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_SECRETKEYBYTES ? 1 : -1]);
2527+
keypair = zend_string_alloc(crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES, 0);
2528+
sk = (unsigned char *) ZSTR_VAL(keypair);
2529+
pk = sk + crypto_kx_SECRETKEYBYTES;
2530+
crypto_generichash(sk, crypto_kx_SECRETKEYBYTES,
2531+
seed, crypto_kx_SEEDBYTES, NULL, 0);
2532+
if (crypto_scalarmult_base(pk, sk) != 0) {
2533+
zend_throw_exception(sodium_exception_ce, "internal error", 0);
2534+
return;
2535+
}
2536+
ZSTR_VAL(keypair)[crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES] = 0;
2537+
RETURN_STR(keypair);
2538+
}
2539+
2540+
PHP_FUNCTION(sodium_crypto_kx_keypair)
2541+
{
2542+
unsigned char *sk;
2543+
unsigned char *pk;
2544+
zend_string *keypair;
2545+
2546+
keypair = zend_string_alloc(crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES, 0);
2547+
sk = (unsigned char *) ZSTR_VAL(keypair);
2548+
pk = sk + crypto_kx_SECRETKEYBYTES;
2549+
randombytes_buf(sk, crypto_kx_SECRETKEYBYTES);
2550+
if (crypto_scalarmult_base(pk, sk) != 0) {
2551+
zend_throw_exception(sodium_exception_ce, "internal error", 0);
2552+
return;
2553+
}
2554+
RETURN_STR(keypair);
2555+
}
2556+
2557+
PHP_FUNCTION(sodium_crypto_kx_secretkey)
2558+
{
2559+
zend_string *secretkey;
2560+
unsigned char *keypair;
2561+
size_t keypair_len;
2562+
2563+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
2564+
&keypair, &keypair_len) == FAILURE) {
2565+
return;
2566+
}
2567+
if (keypair_len !=
2568+
crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES) {
2569+
zend_throw_exception(sodium_exception_ce,
2570+
"keypair should be CRYPTO_KX_KEYPAIRBYTES bytes",
2571+
0);
2572+
return;
2573+
}
2574+
secretkey = zend_string_alloc(crypto_kx_SECRETKEYBYTES, 0);
2575+
memcpy(ZSTR_VAL(secretkey), keypair, crypto_kx_SECRETKEYBYTES);
2576+
ZSTR_VAL(secretkey)[crypto_kx_SECRETKEYBYTES] = 0;
2577+
2578+
RETURN_STR(secretkey);
2579+
}
2580+
2581+
PHP_FUNCTION(sodium_crypto_kx_publickey)
2582+
{
2583+
zend_string *publickey;
2584+
unsigned char *keypair;
2585+
size_t keypair_len;
2586+
2587+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s",
2588+
&keypair, &keypair_len) == FAILURE) {
2589+
return;
2590+
}
2591+
if (keypair_len !=
2592+
crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES) {
2593+
zend_throw_exception(sodium_exception_ce,
2594+
"keypair should be CRYPTO_KX_KEYPAIRBYTES bytes",
2595+
0);
2596+
return;
2597+
}
2598+
publickey = zend_string_alloc(crypto_kx_PUBLICKEYBYTES, 0);
2599+
memcpy(ZSTR_VAL(publickey), keypair + crypto_kx_SECRETKEYBYTES,
2600+
crypto_kx_PUBLICKEYBYTES);
2601+
ZSTR_VAL(publickey)[crypto_kx_PUBLICKEYBYTES] = 0;
2602+
2603+
RETURN_STR(publickey);
2604+
}
2605+
2606+
PHP_FUNCTION(sodium_crypto_kx_client_session_keys)
24842607
{
24852608
crypto_generichash_state h;
2486-
unsigned char q[crypto_scalarmult_BYTES];
2487-
zend_string *sharedkey;
2488-
unsigned char *client_publickey;
2489-
unsigned char *publickey;
2490-
unsigned char *secretkey;
2491-
unsigned char *server_publickey;
2492-
size_t client_publickey_len;
2493-
size_t publickey_len;
2494-
size_t secretkey_len;
2495-
size_t server_publickey_len;
2609+
unsigned char q[crypto_scalarmult_BYTES];
2610+
unsigned char *keypair;
2611+
unsigned char *client_sk;
2612+
unsigned char *client_pk;
2613+
unsigned char *server_pk;
2614+
unsigned char session_keys[2 * crypto_kx_SESSIONKEYBYTES];
2615+
size_t keypair_len;
2616+
size_t server_pk_len;
24962617

2497-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssss",
2498-
&secretkey, &secretkey_len,
2499-
&publickey, &publickey_len,
2500-
&client_publickey, &client_publickey_len,
2501-
&server_publickey, &server_publickey_len) == FAILURE) {
2618+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
2619+
&keypair, &keypair_len,
2620+
&server_pk, &server_pk_len) == FAILURE) {
25022621
return;
25032622
}
2504-
if (secretkey_len != crypto_kx_SECRETKEYBYTES) {
2505-
zend_throw_exception(sodium_exception_ce, "crypto_kx(): secret key must be CRYPTO_KX_SECRETKEY bytes", 0);
2623+
if (keypair_len != crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES) {
2624+
zend_throw_exception(sodium_exception_ce, "keypair must be CRYPTO_KX_KEYPAIRBYTES bytes", 0);
25062625
return;
25072626
}
2508-
if (publickey_len != crypto_kx_PUBLICKEYBYTES ||
2509-
client_publickey_len != crypto_kx_PUBLICKEYBYTES ||
2510-
server_publickey_len != crypto_kx_PUBLICKEYBYTES) {
2511-
zend_throw_exception(sodium_exception_ce, "crypto_kx(): public keys must be CRYPTO_KX_PUBLICKEY bytes", 0);
2627+
if (server_pk_len != crypto_kx_PUBLICKEYBYTES) {
2628+
zend_throw_exception(sodium_exception_ce, "public keys must be CRYPTO_KX_PUBLICKEYBYTES bytes", 0);
25122629
return;
25132630
}
2514-
(void) sizeof(int[crypto_scalarmult_SCALARBYTES ==
2515-
crypto_kx_PUBLICKEYBYTES ? 1 : -1]);
2516-
(void) sizeof(int[crypto_scalarmult_SCALARBYTES ==
2517-
crypto_kx_SECRETKEYBYTES ? 1 : -1]);
2518-
if (crypto_scalarmult(q, secretkey, publickey) != 0) {
2519-
zend_throw_exception(sodium_exception_ce, "crypto_kx(): internal error", 0);
2631+
client_sk = &keypair[0];
2632+
client_pk = &keypair[crypto_kx_SECRETKEYBYTES];
2633+
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_PUBLICKEYBYTES ? 1 : -1]);
2634+
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_SECRETKEYBYTES ? 1 : -1]);
2635+
if (crypto_scalarmult(q, client_sk, server_pk) != 0) {
2636+
zend_throw_exception(sodium_exception_ce, "internal error", 0);
25202637
return;
25212638
}
2522-
sharedkey = zend_string_alloc(crypto_kx_BYTES, 0);
2523-
crypto_generichash_init(&h, NULL, 0U, crypto_generichash_BYTES);
2639+
crypto_generichash_init(&h, NULL, 0U, 2 * crypto_kx_SESSIONKEYBYTES);
25242640
crypto_generichash_update(&h, q, sizeof q);
25252641
sodium_memzero(q, sizeof q);
2526-
crypto_generichash_update(&h, client_publickey, client_publickey_len);
2527-
crypto_generichash_update(&h, server_publickey, server_publickey_len);
2528-
crypto_generichash_final(&h, (unsigned char *) ZSTR_VAL(sharedkey),
2529-
crypto_kx_BYTES);
2530-
ZSTR_VAL(sharedkey)[crypto_kx_BYTES] = 0;
2642+
crypto_generichash_update(&h, client_pk, crypto_kx_PUBLICKEYBYTES);
2643+
crypto_generichash_update(&h, server_pk, crypto_kx_PUBLICKEYBYTES);
2644+
crypto_generichash_final(&h, session_keys, 2 * crypto_kx_SESSIONKEYBYTES);
2645+
array_init(return_value);
2646+
add_next_index_stringl(return_value,
2647+
(const char *) session_keys,
2648+
crypto_kx_SESSIONKEYBYTES);
2649+
add_next_index_stringl(return_value,
2650+
(const char *) session_keys + crypto_kx_SESSIONKEYBYTES,
2651+
crypto_kx_SESSIONKEYBYTES);
2652+
}
25312653

2532-
RETURN_STR(sharedkey);
2654+
PHP_FUNCTION(sodium_crypto_kx_server_session_keys)
2655+
{
2656+
crypto_generichash_state h;
2657+
unsigned char q[crypto_scalarmult_BYTES];
2658+
unsigned char *keypair;
2659+
unsigned char *server_sk;
2660+
unsigned char *server_pk;
2661+
unsigned char *client_pk;
2662+
unsigned char session_keys[2 * crypto_kx_SESSIONKEYBYTES];
2663+
size_t keypair_len;
2664+
size_t client_pk_len;
2665+
2666+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss",
2667+
&keypair, &keypair_len,
2668+
&client_pk, &client_pk_len) == FAILURE) {
2669+
return;
2670+
}
2671+
if (keypair_len != crypto_kx_SECRETKEYBYTES + crypto_kx_PUBLICKEYBYTES) {
2672+
zend_throw_exception(sodium_exception_ce, "keypair must be CRYPTO_KX_KEYPAIRBYTES bytes", 0);
2673+
return;
2674+
}
2675+
if (client_pk_len != crypto_kx_PUBLICKEYBYTES) {
2676+
zend_throw_exception(sodium_exception_ce, "public keys must be CRYPTO_KX_PUBLICKEYBYTES bytes", 0);
2677+
return;
2678+
}
2679+
server_sk = &keypair[0];
2680+
server_pk = &keypair[crypto_kx_SECRETKEYBYTES];
2681+
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_PUBLICKEYBYTES ? 1 : -1]);
2682+
(void) sizeof(int[crypto_scalarmult_SCALARBYTES == crypto_kx_SECRETKEYBYTES ? 1 : -1]);
2683+
if (crypto_scalarmult(q, server_sk, client_pk) != 0) {
2684+
zend_throw_exception(sodium_exception_ce, "internal error", 0);
2685+
return;
2686+
}
2687+
crypto_generichash_init(&h, NULL, 0U, 2 * crypto_kx_SESSIONKEYBYTES);
2688+
crypto_generichash_update(&h, q, sizeof q);
2689+
sodium_memzero(q, sizeof q);
2690+
crypto_generichash_update(&h, client_pk, crypto_kx_PUBLICKEYBYTES);
2691+
crypto_generichash_update(&h, server_pk, crypto_kx_PUBLICKEYBYTES);
2692+
crypto_generichash_final(&h, session_keys, 2 * crypto_kx_SESSIONKEYBYTES);
2693+
array_init(return_value);
2694+
add_next_index_stringl(return_value,
2695+
(const char *) session_keys + crypto_kx_SESSIONKEYBYTES,
2696+
crypto_kx_SESSIONKEYBYTES);
2697+
add_next_index_stringl(return_value,
2698+
(const char *) session_keys,
2699+
crypto_kx_SESSIONKEYBYTES);
25332700
}
25342701

25352702
PHP_FUNCTION(sodium_crypto_auth)

ext/sodium/php_libsodium.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,12 @@ PHP_FUNCTION(sodium_crypto_generichash);
5959
PHP_FUNCTION(sodium_crypto_generichash_final);
6060
PHP_FUNCTION(sodium_crypto_generichash_init);
6161
PHP_FUNCTION(sodium_crypto_generichash_update);
62-
PHP_FUNCTION(sodium_crypto_kx);
62+
PHP_FUNCTION(sodium_crypto_kx_client_session_keys);
63+
PHP_FUNCTION(sodium_crypto_kx_keypair);
64+
PHP_FUNCTION(sodium_crypto_kx_publickey);
65+
PHP_FUNCTION(sodium_crypto_kx_secretkey);
66+
PHP_FUNCTION(sodium_crypto_kx_seed_keypair);
67+
PHP_FUNCTION(sodium_crypto_kx_server_session_keys);
6368
PHP_FUNCTION(sodium_crypto_pwhash);
6469
PHP_FUNCTION(sodium_crypto_pwhash_str);
6570
PHP_FUNCTION(sodium_crypto_pwhash_str_verify);

ext/sodium/tests/crypto_kx.phpt

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,31 @@ Check for libsodium-based key exchange
44
<?php if (!extension_loaded("sodium")) print "skip"; ?>
55
--FILE--
66
<?php
7-
$client_secretkey = sodium_hex2bin("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
8-
$client_publickey = sodium_crypto_box_publickey_from_secretkey($client_secretkey);
7+
$client_seed = sodium_hex2bin('0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef');
8+
$client_keypair = sodium_crypto_kx_seed_keypair($client_seed);
9+
$server_seed = sodium_hex2bin('f123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde0');
10+
$server_keypair = sodium_crypto_kx_seed_keypair($server_seed);
911

10-
$server_secretkey = sodium_hex2bin("948f00e90a246fb5909f8648c2ac6f21515771235523266439e0d775ba0c3671");
11-
$server_publickey = sodium_crypto_box_publickey_from_secretkey($server_secretkey);
12+
var_dump(sodium_bin2hex($client_keypair));
13+
var_dump(sodium_bin2hex($server_keypair));
1214

13-
$shared_key_computed_by_client =
14-
sodium_crypto_kx($client_secretkey, $server_publickey,
15-
$client_publickey, $server_publickey);
15+
$client_session_keys =
16+
sodium_crypto_kx_client_session_keys($client_keypair,
17+
sodium_crypto_kx_publickey($server_keypair));
1618

17-
$shared_key_computed_by_server =
18-
sodium_crypto_kx($server_secretkey, $client_publickey,
19-
$client_publickey, $server_publickey);
19+
$server_session_keys =
20+
sodium_crypto_kx_server_session_keys($server_keypair,
21+
sodium_crypto_kx_publickey($client_keypair));
2022

21-
var_dump(sodium_bin2hex($shared_key_computed_by_client));
22-
var_dump(sodium_bin2hex($shared_key_computed_by_server));
23-
try {
24-
sodium_crypto_kx(
25-
substr($client_secretkey, 1),
26-
$server_publickey,
27-
$client_publickey,
28-
$server_publickey
29-
);
30-
} catch (SodiumException $ex) {
31-
var_dump(true);
32-
}
23+
var_dump(sodium_bin2hex($client_session_keys[0]));
24+
var_dump(sodium_bin2hex($server_session_keys[1]));
25+
var_dump(sodium_bin2hex($client_session_keys[1]));
26+
var_dump(sodium_bin2hex($server_session_keys[0]));
3327
?>
3428
--EXPECT--
35-
string(64) "509a1580c2ee30c565317e29e0fea0b1c232e0ef3a7871d91dc64814b19a3bd2"
36-
string(64) "509a1580c2ee30c565317e29e0fea0b1c232e0ef3a7871d91dc64814b19a3bd2"
37-
bool(true)
29+
string(128) "b85c84f9828524519d32b97cd3dda961fdba2dbf407ae4601e2129229aa463c224eaf70f070a925d6d5176f20495d4d90867624d9a10379e2a9aef0955c9bf4e"
30+
string(128) "016e814c32b8b66225a403db45bf50fdd1966fb802c3115bf8aa90738c6a02de420ccdb534930fed9aaff12188bedc76e66251f399c404f2e4a15678fd4a484a"
31+
string(64) "99a430e61d718b71979ebcea6735c4648bc828cfb456890aeda4b628b77d5ac7"
32+
string(64) "99a430e61d718b71979ebcea6735c4648bc828cfb456890aeda4b628b77d5ac7"
33+
string(64) "876bef865a5ab3f4ae569ea5aaefe5014c3ec22a558c0a2f0274aa9985bd328d"
34+
string(64) "876bef865a5ab3f4ae569ea5aaefe5014c3ec22a558c0a2f0274aa9985bd328d"

0 commit comments

Comments
 (0)