From 8c8b2f8bc05a5b67f39acf9a6bc0bef6fa839166 Mon Sep 17 00:00:00 2001 From: Michael Mendoza Date: Wed, 6 Jun 2018 12:24:52 -0600 Subject: [PATCH 1/4] Added binding and test for randombytes_buf_deterministic. --- libnacl/__init__.py | 16 +++++++++++++++- tests/unit/test_raw_random.py | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/libnacl/__init__.py b/libnacl/__init__.py index 4a9fe73..9bddd15 100644 --- a/libnacl/__init__.py +++ b/libnacl/__init__.py @@ -162,9 +162,9 @@ class CryptError(Exception): crypto_verify_16_BYTES = nacl.crypto_verify_16_bytes() crypto_verify_32_BYTES = nacl.crypto_verify_32_bytes() crypto_verify_64_BYTES = nacl.crypto_verify_64_bytes() + randombytes_SEEDBYTES = nacl.randombytes_seedbytes() # pylint: enable=C0103 - # Pubkey defs @@ -1057,6 +1057,20 @@ def randombytes_buf(size): nacl.randombytes_buf(buf, size) return buf.raw +def randombytes_buf_deterministic(size, seed): + ''' + Returns a string of random byles of the given size for a given seed. + For a given seed, this function will always output the same sequence. + Size can be up to 2^70 (256 GB). + ''' + + if len(seed) != randombytes_SEEDBYTES: + raise ValueError('Invalid key seed') + + size = int(size) + buf = ctypes.create_string_buffer(size) + nacl.randombytes_buf_deterministic(buf, size, seed) + return buf.raw def randombytes_close(): ''' diff --git a/tests/unit/test_raw_random.py b/tests/unit/test_raw_random.py index b695986..8662f0b 100644 --- a/tests/unit/test_raw_random.py +++ b/tests/unit/test_raw_random.py @@ -27,3 +27,19 @@ def test_randombytes(self): self.assertEqual(256, len(freq)) self.assertTrue(all(freq.values())) + + def test_randombytes_buf_deterministic(self): + + seed = libnacl.randombytes_buf(32) + seed2 = libnacl.randombytes_buf(32) + data = libnacl.randombytes_buf_deterministic(32, seed) + data2 = libnacl.randombytes_buf_deterministic(32, seed) + data3 = libnacl.randombytes_buf_deterministic(32, seed2) + + self.assertEqual(data, data2) + self.assertEqual(len(data), len(data3)) + self.assertNotEqual(data, data3) + + + + From 71194eb8f9861ffeff27ac2cad91be94803bc8e0 Mon Sep 17 00:00:00 2001 From: Michael Mendoza Date: Wed, 6 Jun 2018 13:12:36 -0600 Subject: [PATCH 2/4] Added binding/tests for crypto_kdf_keygen and crypto_kdf_derive_from_key --- libnacl/__init__.py | 27 +++++++++++++++++++++++++++ tests/unit/test_raw_random.py | 25 ++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/libnacl/__init__.py b/libnacl/__init__.py index 9bddd15..693f0af 100644 --- a/libnacl/__init__.py +++ b/libnacl/__init__.py @@ -162,7 +162,14 @@ class CryptError(Exception): crypto_verify_16_BYTES = nacl.crypto_verify_16_bytes() crypto_verify_32_BYTES = nacl.crypto_verify_32_bytes() crypto_verify_64_BYTES = nacl.crypto_verify_64_bytes() + randombytes_SEEDBYTES = nacl.randombytes_seedbytes() + crypto_kdf_PRIMITIVE = nacl.crypto_kdf_primitive() + crypto_kdf_BYTES_MIN = nacl.crypto_kdf_bytes_min() + crypto_kdf_BYTES_MAX = nacl.crypto_kdf_bytes_max() + crypto_kdf_CONTEXTBYTES = nacl.crypto_kdf_contextbytes() + crypto_kdf_KEYBYTES = nacl.crypto_kdf_keybytes() + # pylint: enable=C0103 # Pubkey defs @@ -1103,6 +1110,24 @@ def randombytes_uniform(upper_bound): ''' return nacl.randombytes_uniform(upper_bound) +def crypto_kdf_keygen(): + ''' + Returns a string of random bytes to generate a master key + ''' + size = crypto_kdf_KEYBYTES + buf = ctypes.create_string_buffer(size) + nacl.crypto_kdf_keygen(buf) + return buf.raw + +def crypto_kdf_derive_from_key(subkey_size, subkey_id, context, master_key): + ''' + Returns a subkey generated from a master key for a given subkey_id. + For a given subkey_id, the subkey will always be the same string. + ''' + size = int(subkey_size) + buf = ctypes.create_string_buffer(size) + nacl.crypto_kdf_derive_from_key(buf, subkey_size, subkey_id, context, master_key) + return buf.raw # Utility functions @@ -1155,3 +1180,5 @@ def crypto_sign_ed25519_sk_to_curve25519(ed25519_sk): if ret: raise CryptError('Failed to generate Curve25519 secret key') return curve25519_sk.raw + + diff --git a/tests/unit/test_raw_random.py b/tests/unit/test_raw_random.py index 8662f0b..2a39079 100644 --- a/tests/unit/test_raw_random.py +++ b/tests/unit/test_raw_random.py @@ -36,10 +36,33 @@ def test_randombytes_buf_deterministic(self): data2 = libnacl.randombytes_buf_deterministic(32, seed) data3 = libnacl.randombytes_buf_deterministic(32, seed2) + self.assertEqual(32, len(data)) + self.assertEqual(32, len(data)) + self.assertEqual(32, len(data)) self.assertEqual(data, data2) - self.assertEqual(len(data), len(data3)) self.assertNotEqual(data, data3) + def test_crypto_kdf_keygen(self): + master_key = libnacl.crypto_kdf_keygen() + freq = {x: 1 for x in master_key} + + self.assertEqual(32, len(master_key)) + self.assertTrue(all(freq.values())) + + + def test_crypto_kdf_derive_from_key(self): + + master_key = libnacl.crypto_kdf_keygen() + subkey = libnacl.crypto_kdf_derive_from_key(16, 1, "Examples", master_key) + subkey2 = libnacl.crypto_kdf_derive_from_key(16, 1, "Examples", master_key) + subkey3 = libnacl.crypto_kdf_derive_from_key(16, 2, "Examples", master_key) + + self.assertEqual(16, len(subkey)) + self.assertEqual(16, len(subkey2)) + self.assertEqual(16, len(subkey3)) + self.assertEqual(subkey, subkey2) + self.assertNotEqual(subkey, subkey3) + From a30c14d2a018ccf58de8314cc9d95852975b12a7 Mon Sep 17 00:00:00 2001 From: Michael Mendoza Date: Wed, 13 Jun 2018 00:13:19 +0100 Subject: [PATCH 3/4] Added bindings for key exchange API. Including crypto_kx_keypair, crypto_kx_seed_keypair, crypto_kx_client_session_keys, crypto_kx_server_session_keys. --- libnacl/__init__.py | 49 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/libnacl/__init__.py b/libnacl/__init__.py index 693f0af..a14a4b8 100644 --- a/libnacl/__init__.py +++ b/libnacl/__init__.py @@ -169,6 +169,11 @@ class CryptError(Exception): crypto_kdf_BYTES_MAX = nacl.crypto_kdf_bytes_max() crypto_kdf_CONTEXTBYTES = nacl.crypto_kdf_contextbytes() crypto_kdf_KEYBYTES = nacl.crypto_kdf_keybytes() + crypto_kx_PUBLICKEYBYTES = nacl.crypto_kx_publickeybytes() + crypto_kx_SECRETKEYBYTES = nacl.crypto_kx_secretkeybytes() + crypto_kx_SEEDBYTES = nacl.crypto_kx_seedbytes() + crypto_kx_SESSIONKEYBYTES = nacl.crypto_kx_sessionkeybytes() + crypto_kx_PRIMITIVE = nacl.crypto_kx_primitive() # pylint: enable=C0103 @@ -1129,6 +1134,50 @@ def crypto_kdf_derive_from_key(subkey_size, subkey_id, context, master_key): nacl.crypto_kdf_derive_from_key(buf, subkey_size, subkey_id, context, master_key) return buf.raw +def crypto_kx_keypair(): + ''' + Generate and return a new keypair + ''' + pk = ctypes.create_string_buffer(crypto_kx_PUBLICKEYBYTES) + sk = ctypes.create_string_buffer(crypto_kx_SECRETKEYBYTES) + nacl.crypto_kx_keypair(pk, sk) + return pk.raw, sk.raw + +def crypto_kx_seed_keypair(seed): + ''' + Generate and return a keypair from a key seed + ''' + if len(seed) != crypto_kx_SEEDBYTES: + raise ValueError('Invalid key seed') + pk = ctypes.create_string_buffer(crypto_kx_PUBLICKEYBYTES) + sk = ctypes.create_string_buffer(crypto_kx_SECRETKEYBYTES) + nacl.crypto_kx_seed_keypair(pk, sk, seed) + return pk.raw, sk.raw + +def crypto_kx_client_session_keys(rx, tx, client_pk, client_sk, server_pk): + ''' + Computes a pair of shared keys (rx and tx) using the client's public key client_pk, + the client's secret key client_sk and the server's public key server_pk. + Status returns 0 on success, or -1 if the server's public key is not acceptable. + ''' + rx = ctypes.create_string_buffer(crypto_kx_SESSIONKEYBYTES) + tx = ctypes.create_string_buffer(crypto_kx_SESSIONKEYBYTES) + status = nacl.crypto_kx_client_session_keys(rx, tx, client_pk, client_sk, server_pk) + return rx, tx, status + +def crypto_kx_server_session_keys(rx, tx, server_pk, server_sk, client_pk): + ''' + Computes a pair of shared keys (rx and tx) using the server's public key server_pk, + the server's secret key server_sk and the client's public key client_pk. + Status returns 0 on success, or -1 if the client's public key is not acceptable. + ''' + rx = ctypes.create_string_buffer(crypto_kx_SESSIONKEYBYTES) + tx = ctypes.create_string_buffer(crypto_kx_SESSIONKEYBYTES) + status = nacl.crypto_kx_server_session_keys(rx, tx, server_pk, server_sk, client_pk) + return rx, tx, status + + + # Utility functions def sodium_library_version_major(): From 1da1e844b1e6d8a00c690b91f1b794f7cece2c0c Mon Sep 17 00:00:00 2001 From: Michael Mendoza Date: Wed, 13 Jun 2018 01:54:44 +0100 Subject: [PATCH 4/4] Added updated bindings for key exchange API. Added tests. --- libnacl/__init__.py | 10 ++++++--- tests/unit/test_raw_random.py | 40 +++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/libnacl/__init__.py b/libnacl/__init__.py index a14a4b8..1b037ac 100644 --- a/libnacl/__init__.py +++ b/libnacl/__init__.py @@ -1115,6 +1115,8 @@ def randombytes_uniform(upper_bound): ''' return nacl.randombytes_uniform(upper_bound) +# Key derivation API + def crypto_kdf_keygen(): ''' Returns a string of random bytes to generate a master key @@ -1134,6 +1136,8 @@ def crypto_kdf_derive_from_key(subkey_size, subkey_id, context, master_key): nacl.crypto_kdf_derive_from_key(buf, subkey_size, subkey_id, context, master_key) return buf.raw +# Key Exchange API + def crypto_kx_keypair(): ''' Generate and return a new keypair @@ -1154,7 +1158,7 @@ def crypto_kx_seed_keypair(seed): nacl.crypto_kx_seed_keypair(pk, sk, seed) return pk.raw, sk.raw -def crypto_kx_client_session_keys(rx, tx, client_pk, client_sk, server_pk): +def crypto_kx_client_session_keys(client_pk, client_sk, server_pk): ''' Computes a pair of shared keys (rx and tx) using the client's public key client_pk, the client's secret key client_sk and the server's public key server_pk. @@ -1163,9 +1167,9 @@ def crypto_kx_client_session_keys(rx, tx, client_pk, client_sk, server_pk): rx = ctypes.create_string_buffer(crypto_kx_SESSIONKEYBYTES) tx = ctypes.create_string_buffer(crypto_kx_SESSIONKEYBYTES) status = nacl.crypto_kx_client_session_keys(rx, tx, client_pk, client_sk, server_pk) - return rx, tx, status + return rx.raw, tx.raw, status -def crypto_kx_server_session_keys(rx, tx, server_pk, server_sk, client_pk): +def crypto_kx_server_session_keys(server_pk, server_sk, client_pk): ''' Computes a pair of shared keys (rx and tx) using the server's public key server_pk, the server's secret key server_sk and the client's public key client_pk. diff --git a/tests/unit/test_raw_random.py b/tests/unit/test_raw_random.py index 2a39079..8821731 100644 --- a/tests/unit/test_raw_random.py +++ b/tests/unit/test_raw_random.py @@ -51,7 +51,6 @@ def test_crypto_kdf_keygen(self): self.assertEqual(32, len(master_key)) self.assertTrue(all(freq.values())) - def test_crypto_kdf_derive_from_key(self): master_key = libnacl.crypto_kdf_keygen() @@ -65,4 +64,41 @@ def test_crypto_kdf_derive_from_key(self): self.assertEqual(subkey, subkey2) self.assertNotEqual(subkey, subkey3) - + def test_crypto_kx_keypair(self): + pk, sk = libnacl.crypto_kx_keypair() + self.assertEqual(32, len(pk)) + self.assertEqual(32, len(sk)) + + def test_crypto_kx_seed_keypair(self): + seed = libnacl.randombytes_buf(32) + seed2 = libnacl.randombytes_buf(32) + pk, sk = libnacl.crypto_kx_seed_keypair(seed) + pk2, sk2 = libnacl.crypto_kx_seed_keypair(seed) + pk3, sk3 = libnacl.crypto_kx_seed_keypair(seed2) + + self.assertEqual(pk, pk2) + self.assertNotEqual(pk, pk3) + self.assertEqual(sk, sk2) + self.assertNotEqual(sk, sk3) + + def test_crypto_kx_client_session_keys(self): + client_pk, client_sk = libnacl.crypto_kx_keypair() + server_pk, server_sk = libnacl.crypto_kx_keypair() + rx, tx, status = libnacl.crypto_kx_client_session_keys(client_pk, client_sk, server_pk) + rx2, tx2, status = libnacl.crypto_kx_client_session_keys(client_pk, client_sk, server_pk) + + self.assertEqual(32, len(rx)) + self.assertEqual(32, len(tx)) + self.assertEqual(rx, rx2) + self.assertEqual(tx, tx2) + + def test_crypto_kx_server_session_keys(self): + client_pk, client_sk = libnacl.crypto_kx_keypair() + server_pk, server_sk = libnacl.crypto_kx_keypair() + rx, tx, status = libnacl.crypto_kx_server_session_keys(client_pk, client_sk, server_pk) + rx2, tx2, status = libnacl.crypto_kx_server_session_keys(client_pk, client_sk, server_pk) + + self.assertEqual(32, len(rx)) + self.assertEqual(32, len(tx)) + self.assertEqual(rx, rx2) + self.assertEqual(tx, tx2)