Skip to content

Commit

Permalink
Merge pull request #109 from reputage/libsodium-bindings
Browse files Browse the repository at this point in the history
Libsodium bindings
  • Loading branch information
thatch45 authored Jul 11, 2018
2 parents 74a21d2 + 1da1e84 commit ed52838
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 1 deletion.
96 changes: 95 additions & 1 deletion libnacl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,20 @@ 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()
# pylint: enable=C0103

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()
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

# Pubkey defs

Expand Down Expand Up @@ -1057,6 +1069,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():
'''
Expand Down Expand Up @@ -1089,6 +1115,72 @@ 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
'''
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

# Key Exchange API

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(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.raw, tx.raw, status

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.
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

Expand Down Expand Up @@ -1141,3 +1233,5 @@ def crypto_sign_ed25519_sk_to_curve25519(ed25519_sk):
if ret:
raise CryptError('Failed to generate Curve25519 secret key')
return curve25519_sk.raw


75 changes: 75 additions & 0 deletions tests/unit/test_raw_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,78 @@ 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(32, len(data))
self.assertEqual(32, len(data))
self.assertEqual(32, len(data))
self.assertEqual(data, data2)
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)

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)

0 comments on commit ed52838

Please sign in to comment.