Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for libsodium 1.0.19 #138

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 179 additions & 1 deletion libnacl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import sys
import os

__SONAMES = (23, 18, 17, 13, 10, 5, 4)
__SONAMES = (26, 23, 18, 17, 13, 10, 5, 4)


def _get_nacl():
Expand Down Expand Up @@ -128,6 +128,23 @@ class CryptError(Exception):
HAS_AEAD_CHACHA20POLY1305_IETF = False
HAS_AEAD_XCHACHA20POLY1305_IETF = False
HAS_AEAD = False

try:
crypto_aead_aegis256_KEYBYTES = nacl.crypto_aead_aegis256_keybytes()
crypto_aead_aegis256_NPUBBYTES = nacl.crypto_aead_aegis256_npubbytes()
crypto_aead_aegis256_ABYTES = nacl.crypto_aead_aegis256_abytes()
HAS_AEAD_AEGIS256 = True
except AttributeError:
HAS_AEAD_AEGIS256 = False

try:
crypto_aead_aegis128l_KEYBYTES = nacl.crypto_aead_aegis128l_keybytes()
crypto_aead_aegis128l_NPUBBYTES = nacl.crypto_aead_aegis128l_npubbytes()
crypto_aead_aegis128l_ABYTES = nacl.crypto_aead_aegis128l_abytes()
HAS_AEAD_AEGIS128L = True
except AttributeError:
HAS_AEAD_AEGIS128L = False


crypto_box_SECRETKEYBYTES = nacl.crypto_box_secretkeybytes()
crypto_box_SEEDBYTES = nacl.crypto_box_seedbytes()
Expand Down Expand Up @@ -185,6 +202,23 @@ class CryptError(Exception):
HAS_CRYPT_KDF = True
except AttributeError:
HAS_CRYPT_KDF = False

try:
crypto_kdf_hkdf_sha256_KEYBYTES = nacl.crypto_kdf_hkdf_sha256_keybytes()
crypto_kdf_hkdf_sha256_BYTES_MIN = nacl.crypto_kdf_hkdf_sha256_bytes_min()
crypto_kdf_hkdf_sha256_BYTES_MAX = nacl.crypto_kdf_hkdf_sha256_bytes_max()
HAS_CRYPT_KDF_HKDF_SHA256 = True
except AttributeError:
HAS_CRYPT_KDF_HKDF_SHA256 = False

try:
crypto_kdf_hkdf_sha512_KEYBYTES = nacl.crypto_kdf_hkdf_sha512_keybytes()
crypto_kdf_hkdf_sha512_BYTES_MIN = nacl.crypto_kdf_hkdf_sha512_bytes_min()
crypto_kdf_hkdf_sha512_BYTES_MAX = nacl.crypto_kdf_hkdf_sha512_bytes_max()
HAS_CRYPT_KDF_HKDF_SHA512 = True
except AttributeError:
HAS_CRYPT_KDF_HKDF_SHA512 = False


try:
crypto_kx_PUBLICKEYBYTES = nacl.crypto_kx_publickeybytes()
Expand Down Expand Up @@ -905,6 +939,100 @@ def crypto_aead_xchacha20poly1305_ietf_decrypt(ctxt, aad, nonce, key):
raise ValueError('Failed to decrypt message')
return m.raw

def crypto_aead_aegis256_encrypt(message, aad, nonce, key):
if not HAS_AEAD_AEGIS256:
raise ValueError('Underlying Sodium library does not support AEGIS256 AEAD')

if len(key) != crypto_aead_aegis256_KEYBYTES:
raise ValueError('Invalid key')

if len(key) != crypto_aead_aegis256_NPUBBYTES:
raise ValueError('Invalid nonce')

length = len(message) + crypto_aead_aegis256_ABYTES
clen = ctypes.c_ulonglong()
c = ctypes.create_string_buffer(length)
ret = nacl.crypto_aead_aegis256_encrypt(
c, ctypes.pointer(clen),
message, ctypes.c_ulonglong(len(message)),
aad, ctypes.c_ulonglong(len(aad)),
None,
nonce, key)
if ret:
raise ValueError('Failed to encrypt message')
return c.raw

def crypto_aead_aegis256_decrypt(ctxt, aad, nonce, key):
if not HAS_AEAD_AEGIS256:
raise ValueError('Underlying Sodium library does not support AEGIS256 AEAD')

if len(key) != crypto_aead_aegis256_KEYBYTES:
raise ValueError('Invalid key')

if len(nonce) != crypto_aead_aegis256_NPUBBYTES:
raise ValueError('Invalid nonce')

length = len(ctxt)-crypto_aead_aegis256_ABYTES
mlen = ctypes.c_ulonglong()
m = ctypes.create_string_buffer(length)

ret = nacl.crypto_aead_aegis256_decrypt(
m, ctypes.byref(mlen),
None,
ctxt, ctypes.c_ulonglong(len(ctxt)),
aad, ctypes.c_ulonglong(len(aad)),
nonce, key)
if ret:
raise ValueError('Failed to decrypt message')
return m.raw

def crypto_aead_aegis128l_encrypt(message, aad, nonce, key):
if not HAS_AEAD_AEGIS128L:
raise ValueError('Underlying Sodium library does not support AEGIS128L AEAD')

if len(key) != crypto_aead_aegis128l_KEYBYTES:
raise ValueError('Invalid key')

if len(key) != crypto_aead_aegis128l_NPUBBYTES:
raise ValueError('Invalid nonce')

length = len(message) + crypto_aead_aegis128l_ABYTES
clen = ctypes.c_ulonglong()
c = ctypes.create_string_buffer(length)
ret = nacl.crypto_aead_aegis128l_encrypt(
c, ctypes.pointer(clen),
message, ctypes.c_ulonglong(len(message)),
aad, ctypes.c_ulonglong(len(aad)),
None,
nonce, key)
if ret:
raise ValueError('Failed to encrypt message')
return c.raw

def crypto_aead_aegis128l_decrypt(ctxt, aad, nonce, key):
if not HAS_AEAD_AEGIS128L:
raise ValueError('Underlying Sodium library does not support AES256-GCM AEAD')

if len(key) != crypto_aead_aegis128l_KEYBYTES:
raise ValueError('Invalid key')

if len(nonce) != crypto_aead_aegis128l_NPUBBYTES:
raise ValueError('Invalid nonce')

length = len(ctxt)-crypto_aead_aegis128l_ABYTES
mlen = ctypes.c_ulonglong()
m = ctypes.create_string_buffer(length)

ret = nacl.crypto_aead_aegis128l_decrypt(
m, ctypes.byref(mlen),
None,
ctxt, ctypes.c_ulonglong(len(ctxt)),
aad, ctypes.c_ulonglong(len(aad)),
nonce, key)
if ret:
raise ValueError('Failed to decrypt message')
return m.raw

# Symmetric Encryption


Expand Down Expand Up @@ -1260,6 +1388,56 @@ def crypto_kdf_derive_from_key(subkey_size, subkey_id, context, master_key):
nacl.crypto_kdf_derive_from_key(buf, subkey_size, ctypes.c_ulonglong(subkey_id), context, master_key)
return buf.raw

def crypto_kdf_hkdf_sha256_keygen():
buf = ctypes.create_string_buffer(crypto_kdf_hkdf_sha256_KEYBYTES)
nacl.crypto_kdf_hkdf_sha256_keygen(buf)
return buf.raw

def crypto_kdf_hkdf_sha256_extract(salt, key):
prk = ctypes.create_string_buffer(crypto_kdf_hkdf_sha256_KEYBYTES)
nacl.crypto_kdf_hkdf_sha256_extract(
prk,
salt, len(salt),
key, len(key)
)
return prk.raw

def crypto_kdf_hkdf_sha256_expand(size, ctx, prk):
out = ctypes.create_string_buffer(size)
ret = nacl.crypto_kdf_hkdf_sha256_expand(
out, size,
ctx, len(ctx),
prk
)
if ret:
raise ValueError("Error")
return out.raw

def crypto_kdf_hkdf_sha512_keygen():
buf = ctypes.create_string_buffer(crypto_kdf_hkdf_sha512_KEYBYTES)
nacl.crypto_kdf_hkdf_sha512_keygen(buf)
return buf.raw

def crypto_kdf_hkdf_sha512_extract(salt, key):
prk = ctypes.create_string_buffer(crypto_kdf_hkdf_sha512_KEYBYTES)
nacl.crypto_kdf_hkdf_sha512_extract(
prk,
salt, len(salt),
key, len(key)
)
return prk.raw

def crypto_kdf_hkdf_sha512_expand(size, ctx, prk):
out = ctypes.create_string_buffer(size)
ret = nacl.crypto_kdf_hkdf_sha512_expand(
out, size,
ctx, len(ctx),
prk
)
if ret:
raise ValueError("Error")
return out.raw

# Key Exchange API

def crypto_kx_keypair():
Expand Down
78 changes: 73 additions & 5 deletions libnacl/aead.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,28 +7,53 @@
import libnacl.utils
import libnacl.base

_DEFAULT_KEY_SIZE = libnacl.crypto_aead_chacha20poly1305_ietf_KEYBYTES


class AEAD(libnacl.base.BaseKey):
'''
Manage AEAD encryption using the IETF ChaCha20-Poly1305(default) or AES-GCM algorithm
Manage AEAD encryption using the IETF ChaCha20-Poly1305(default), AES-GCM, AEGIS128l or AEGIS256 algorithm
If using AEGIS128l, you must set keysize to appropriate size, or use AEAD_AEGIS128L class.
'''

def __init__(self, key=None):
def __init__(self, key=None, keysize=_DEFAULT_KEY_SIZE):
if key is None:
key = libnacl.utils.aead_key()
if len(key) != libnacl.crypto_aead_chacha20poly1305_ietf_KEYBYTES: # same size for both
raise ValueError('Invalid key')
if keysize == _DEFAULT_KEY_SIZE:
key = libnacl.utils.aead_key()
elif keysize == libnacl.crypto_aead_aegis128l_KEYBYTES:
key = libnacl.utils.aead_aegis128l_key()

self.sk = key
if len(self.sk) != keysize: # same size for both
raise ValueError('Invalid key')
self.usingAES = False
self.usingXCHACHA = False
self.usingAEGIS256 = False
self.usingAEGIS128L = False
super().__init__()

def useAESGCM(self):
self.usingAES = True
if len(self.sk) != libnacl.crypto_aead_aes256gcm_KEYBYTES: # same size for both
raise ValueError('Invalid key')
return self

def useXCHACHA(self):
self.usingXCHACHA = True
if len(self.sk) != libnacl.crypto_aead_xchacha20poly1305_ietf_KEYBYTES: # same size for both
raise ValueError('Invalid key')
return self

def useAEGIS256(self):
if len(self.sk) != libnacl.crypto_aead_aegis256_KEYBYTES: # same size for both
raise ValueError('Invalid key')
self.usingAEGIS256 = True
return self

def useAEGIS128L(self):
if len(self.sk) != libnacl.crypto_aead_aegis128l_KEYBYTES: # same size for both
raise ValueError('Invalid key')
self.usingAEGIS128L = True
return self

def encrypt(self, msg, aad, nonce=None, pack_nonce_aad=True):
Expand All @@ -39,18 +64,33 @@ def encrypt(self, msg, aad, nonce=None, pack_nonce_aad=True):
if nonce is None:
if self.usingXCHACHA:
nonce = libnacl.utils.rand_aead_xchacha_nonce()
elif self.usingAEGIS256:
nonce = libnacl.utils.rand_aead_aegis256_nonce()
elif self.usingAEGIS128L:
nonce = libnacl.utils.rand_aead_aegis128l_nonce()
else:
nonce = libnacl.utils.rand_aead_nonce()
if self.usingXCHACHA:
if len(nonce) != libnacl.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES:
raise ValueError('Invalid nonce')
elif self.usingAEGIS256:
if len(nonce) != libnacl.crypto_aead_aegis256_NPUBBYTES:
raise ValueError('Invalid nonce')
elif self.usingAEGIS128L:
if len(nonce) != libnacl.crypto_aead_aegis128l_NPUBBYTES:
raise ValueError('Invalid nonce')
else:
if len(nonce) != libnacl.crypto_aead_aes256gcm_NPUBBYTES:
raise ValueError('Invalid nonce')

if self.usingXCHACHA:
ctxt = libnacl.crypto_aead_xchacha20poly1305_ietf_encrypt(msg, aad, nonce, self.sk)
elif self.usingAES:
ctxt = libnacl.crypto_aead_aes256gcm_encrypt(msg, aad, nonce, self.sk)
elif self.usingAEGIS256:
ctxt = libnacl.crypto_aead_aegis256_encrypt(msg, aad, nonce, self.sk)
elif self.usingAEGIS128L:
ctxt = libnacl.crypto_aead_aegis128l_encrypt(msg, aad, nonce, self.sk)
else:
ctxt = libnacl.crypto_aead_chacha20poly1305_ietf_encrypt(msg, aad, nonce, self.sk)

Expand All @@ -70,6 +110,16 @@ def decrypt(self, ctxt, aadLen):
ctxt = ctxt[aadLen+libnacl.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES:]
if len(nonce) != libnacl.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES:
raise ValueError('Invalid nonce')
elif self.usingAEGIS256:
nonce = ctxt[aadLen:aadLen+libnacl.crypto_aead_aegis256_NPUBBYTES]
ctxt = ctxt[aadLen+libnacl.crypto_aead_aegis256_NPUBBYTES:]
if len(nonce) != libnacl.crypto_aead_aegis256_NPUBBYTES:
raise ValueError('Invalid nonce')
elif self.usingAEGIS128L:
nonce = ctxt[aadLen:aadLen+libnacl.crypto_aead_aegis128l_NPUBBYTES]
ctxt = ctxt[aadLen+libnacl.crypto_aead_aegis128l_NPUBBYTES:]
if len(nonce) != libnacl.crypto_aead_aegis128l_NPUBBYTES:
raise ValueError('Invalid nonce')
else:
nonce = ctxt[aadLen:aadLen+libnacl.crypto_aead_aes256gcm_NPUBBYTES]
ctxt = ctxt[aadLen+libnacl.crypto_aead_aes256gcm_NPUBBYTES:]
Expand All @@ -79,6 +129,10 @@ def decrypt(self, ctxt, aadLen):
return libnacl.crypto_aead_xchacha20poly1305_ietf_decrypt(ctxt, aad, nonce, self.sk)
if self.usingAES:
return libnacl.crypto_aead_aes256gcm_decrypt(ctxt, aad, nonce, self.sk)
if self.usingAEGIS256:
return libnacl.crypto_aead_aegis256_decrypt(ctxt, aad, nonce, self.sk)
if self.usingAEGIS128L:
return libnacl.crypto_aead_aegis128l_decrypt(ctxt, aad, nonce, self.sk)
return libnacl.crypto_aead_chacha20poly1305_ietf_decrypt(ctxt, aad, nonce, self.sk)

def decrypt_unpacked(self, aad, nonce, ctxt):
Expand All @@ -90,6 +144,10 @@ def decrypt_unpacked(self, aad, nonce, ctxt):
raise ValueError('Invalid nonce')
if self.usingAES:
return libnacl.crypto_aead_aes256gcm_decrypt(ctxt, aad, nonce, self.sk)
if self.usingAEGIS256:
return libnacl.crypto_aead_aegis256_decrypt(ctxt, aad, nonce, self.sk)
if self.usingAEGIS128L:
return libnacl.crypto_aead_aegis128l_decrypt(ctxt, aad, nonce, self.sk)
return libnacl.crypto_aead_chacha20poly1305_ietf_decrypt(ctxt, aad, nonce, self.sk)


Expand All @@ -108,3 +166,13 @@ def __init__(self, key=None):
class AEAD_CHACHA(AEAD):
def __init__(self, key=None):
super().__init__(key)

class AEAD_AEGIS256(AEAD):
def __init__(self, key=None):
super().__init__(key)
self.useAEGIS256()

class AEAD_AEGIS128L(AEAD):
def __init__(self, key=None):
super().__init__(key, keysize=libnacl.crypto_aead_aegis128l_KEYBYTES)
self.useAEGIS128L()
19 changes: 19 additions & 0 deletions libnacl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ def aead_key():
'''
return libnacl.randombytes(libnacl.crypto_aead_aes256gcm_KEYBYTES)

def aead_aegis128l_key():
'''
Generates an AEAD key (both implementations use the same size)
'''
return libnacl.randombytes(libnacl.crypto_aead_aegis128l_KEYBYTES)


def rand_aead_nonce():
'''
Expand All @@ -93,6 +99,19 @@ def rand_aead_xchacha_nonce():
'''
return libnacl.randombytes(libnacl.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES)

def rand_aead_aegis256_nonce():
'''
Generates and returns a random bytestring of the size defined in libsodium
as crypto_aead_aegis256_NPUBBYTES
'''
return libnacl.randombytes(libnacl.crypto_aead_aegis256_NPUBBYTES)

def rand_aead_aegis128l_nonce():
'''
Generates and returns a random bytestring of the size defined in libsodium
as crypto_aead_aegis256_NPUBBYTES
'''
return libnacl.randombytes(libnacl.crypto_aead_aegis128l_NPUBBYTES)

def rand_nonce():
'''
Expand Down
Loading