]> granicus.if.org Git - php/commitdiff
sodium ext: update the crypto_kx_*() API to the libsodium one
authorFrank Denis <jedisct1@php.net>
Tue, 11 Jul 2017 20:23:44 +0000 (22:23 +0200)
committerJoe Watkins <krakjoe@php.net>
Wed, 12 Jul 2017 07:25:38 +0000 (08:25 +0100)
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.

ext/sodium/libsodium.c
ext/sodium/php_libsodium.h
ext/sodium/tests/crypto_kx.phpt

index 34bf7d4a2e52370a108f61b33dec0a1677e3ee68..a176485a8cea27c2450fd7d6676323d689d3fd4b 100644 (file)
@@ -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))
@@ -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)
@@ -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",
@@ -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)
index 93d92e3e036b0db5a19b25ee74f3fbbec2e903c0..6e6e3fbe2d81e785765cb3d7ae401d365ed40605 100644 (file)
@@ -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);
index ef8c5f7a63400b003aa1c308f3a00dfd403d0e83..a1dd00a0a73d0f76eaf8f64e312d6844eefe8593 100644 (file)
@@ -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"