2016-05-09 01:46:54 +00:00
|
|
|
"""An AEAD style OpenSSL wrapper
|
|
|
|
|
|
|
|
API:
|
|
|
|
|
|
|
|
encrypt(data, header=b'', aad_offset=0) -> envelope
|
|
|
|
decrypt(envelope, header_len=0, aad_offset=0) -> data
|
|
|
|
|
|
|
|
Envelope layout:
|
|
|
|
|
|
|
|
|<--------------------------- envelope ------------------------------------------>|
|
|
|
|
|<------------ header ----------->|<---------- ciphersuite specific ------------->|
|
|
|
|
|<-- not auth data -->|<-- aad -->|<-- e.g.: S(aad, iv, E(data)), iv, E(data) -->|
|
|
|
|
|
|
|
|
|--- #aad_offset ---->|
|
|
|
|
|------------- #header_len ------>|
|
|
|
|
|
|
|
|
S means a cryptographic signature function (like HMAC or GMAC).
|
|
|
|
E means a encryption function (like AES).
|
|
|
|
iv is the initialization vector / nonce, if needed.
|
|
|
|
|
|
|
|
The split of header into not authenticated data and aad (additional authenticated
|
|
|
|
data) is done to support the legacy envelope layout as used in attic and early borg
|
|
|
|
(where the TYPE byte was not authenticated) and avoid unneeded memcpy and string
|
|
|
|
garbage.
|
|
|
|
|
|
|
|
Newly designed envelope layouts can just authenticate the whole header.
|
|
|
|
|
|
|
|
IV handling:
|
|
|
|
|
|
|
|
iv = ... # just never repeat!
|
|
|
|
cs = CS(hmac_key, enc_key, iv=iv)
|
|
|
|
envelope = cs.encrypt(data, header, aad_offset)
|
|
|
|
iv = cs.next_iv(len(data))
|
|
|
|
(repeat)
|
|
|
|
"""
|
|
|
|
|
2016-12-16 23:51:25 +00:00
|
|
|
import hashlib
|
|
|
|
import hmac
|
|
|
|
from math import ceil
|
|
|
|
|
2016-08-28 23:53:09 +00:00
|
|
|
from cpython cimport PyMem_Malloc, PyMem_Free
|
2016-03-27 00:12:23 +00:00
|
|
|
from cpython.buffer cimport PyBUF_SIMPLE, PyObject_GetBuffer, PyBuffer_Release
|
2014-03-10 21:50:38 +00:00
|
|
|
|
2022-03-22 00:59:51 +00:00
|
|
|
API_VERSION = '1.3_01'
|
2014-03-18 21:04:08 +00:00
|
|
|
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef extern from "openssl/crypto.h":
|
|
|
|
int CRYPTO_memcmp(const void *a, const void *b, size_t len)
|
2016-03-27 00:12:23 +00:00
|
|
|
|
2022-04-14 17:16:25 +00:00
|
|
|
cdef extern from "openssl/opensslv.h":
|
|
|
|
long OPENSSL_VERSION_NUMBER
|
2016-11-07 21:08:11 +00:00
|
|
|
|
2014-03-10 21:50:38 +00:00
|
|
|
cdef extern from "openssl/evp.h":
|
|
|
|
ctypedef struct EVP_MD:
|
|
|
|
pass
|
2014-05-13 20:27:57 +00:00
|
|
|
ctypedef struct EVP_CIPHER:
|
|
|
|
pass
|
|
|
|
ctypedef struct EVP_CIPHER_CTX:
|
|
|
|
pass
|
|
|
|
ctypedef struct ENGINE:
|
|
|
|
pass
|
2016-05-09 01:46:54 +00:00
|
|
|
|
2014-05-13 20:27:57 +00:00
|
|
|
const EVP_CIPHER *EVP_aes_256_ctr()
|
2016-08-16 04:34:52 +00:00
|
|
|
const EVP_CIPHER *EVP_aes_256_ocb()
|
|
|
|
const EVP_CIPHER *EVP_chacha20_poly1305()
|
2016-05-09 01:46:54 +00:00
|
|
|
|
2023-05-11 18:09:47 +00:00
|
|
|
EVP_CIPHER_CTX *EVP_CIPHER_CTX_new()
|
|
|
|
void EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *a)
|
2016-05-09 01:46:54 +00:00
|
|
|
void EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *a)
|
|
|
|
void EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *a)
|
2014-05-13 20:27:57 +00:00
|
|
|
|
2015-03-03 19:11:28 +00:00
|
|
|
int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl,
|
2014-05-13 20:27:57 +00:00
|
|
|
const unsigned char *key, const unsigned char *iv)
|
2015-03-03 19:11:28 +00:00
|
|
|
int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ENGINE *impl,
|
cleanup crypto.pyx, make it easier to adapt to other modes
There were some small issues:
a) it never called EVP_EncryptFinal_ex.
For CTR mode, this had no visible consequences as EVP_EncryptUpdate already yielded all ciphertext.
For cleanliness and to have correctness even in other modes, the missing call was added.
b) decrypt = encrypt hack
This is a nice hack to abbreviate, but it only works for modes without padding and without authentication.
For cleanliness and to have correctness even in other modes, the missing usage of the decrypt api was added.
c) outl == inl assumption
Again, True for CTR mode, but not for padding or authenticating modes.
Fixed so it computes the ciphertext / plaintext length based on api return values.
Other changes:
As encrypt and decrypt API calls are different even for initialization/reset, added a is_encrypt flag.
Defensive output buffer allocation. Added the length of one extra AES block (16bytes) so it would
work even with padding modes. 16bytes are needed because a full block of padding might get
added when the plaintext was a multiple of aes block size.
These changes are based on some experimental code I did for aes-cbc and aes-gcm.
While we likely won't ever want aes-cbc in attic (maybe gcm though?), I think it is cleaner
to not make too many mode specific assumptions and hacks, but just use the API as it
was meant to be used.
2015-03-03 18:19:28 +00:00
|
|
|
const unsigned char *key, const unsigned char *iv)
|
2015-03-03 19:11:28 +00:00
|
|
|
int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
|
|
|
|
const unsigned char *in_, int inl)
|
|
|
|
int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
|
|
|
|
const unsigned char *in_, int inl)
|
|
|
|
int EVP_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
|
|
|
|
int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl)
|
2014-05-13 20:27:57 +00:00
|
|
|
|
2016-05-09 01:46:54 +00:00
|
|
|
int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
|
2022-03-17 19:50:37 +00:00
|
|
|
int EVP_CTRL_AEAD_GET_TAG
|
|
|
|
int EVP_CTRL_AEAD_SET_TAG
|
|
|
|
int EVP_CTRL_AEAD_SET_IVLEN
|
2016-03-27 00:12:23 +00:00
|
|
|
|
2016-07-14 20:35:50 +00:00
|
|
|
|
2014-03-10 21:50:38 +00:00
|
|
|
import struct
|
|
|
|
|
|
|
|
_int = struct.Struct('>I')
|
|
|
|
_long = struct.Struct('>Q')
|
|
|
|
|
|
|
|
bytes_to_int = lambda x, offset=0: _int.unpack_from(x, offset)[0]
|
|
|
|
bytes_to_long = lambda x, offset=0: _long.unpack_from(x, offset)[0]
|
|
|
|
long_to_bytes = lambda x: _long.pack(x)
|
|
|
|
|
|
|
|
|
2016-09-02 19:51:22 +00:00
|
|
|
def num_cipher_blocks(length, blocksize=16):
|
|
|
|
"""Return the number of cipher blocks required to encrypt/decrypt <length> bytes of data.
|
|
|
|
|
|
|
|
For a precise computation, <blocksize> must be the used cipher's block size (AES: 16, CHACHA20: 64).
|
|
|
|
|
|
|
|
For a safe-upper-boundary computation, <blocksize> must be the MINIMUM of the block sizes (in
|
|
|
|
bytes) of ALL supported ciphers. This can be used to adjust a counter if the used cipher is not
|
|
|
|
known (yet).
|
|
|
|
The default value of blocksize must be adjusted so it reflects this minimum, so a call of this
|
|
|
|
function without a blocksize is "safe-upper-boundary by default".
|
|
|
|
|
|
|
|
Padding cipher modes are not supported.
|
2014-03-10 21:50:38 +00:00
|
|
|
"""
|
2016-09-02 19:51:22 +00:00
|
|
|
return (length + blocksize - 1) // blocksize
|
2014-03-10 21:50:38 +00:00
|
|
|
|
|
|
|
|
2016-05-09 01:46:54 +00:00
|
|
|
class CryptoError(Exception):
|
|
|
|
"""Malfunction in the crypto module."""
|
|
|
|
|
|
|
|
|
|
|
|
class IntegrityError(CryptoError):
|
|
|
|
"""Integrity checks failed. Corrupted or tampered data."""
|
|
|
|
|
|
|
|
|
2016-06-25 15:18:14 +00:00
|
|
|
cdef Py_buffer ro_buffer(object data) except *:
|
|
|
|
cdef Py_buffer view
|
|
|
|
PyObject_GetBuffer(data, &view, PyBUF_SIMPLE)
|
|
|
|
return view
|
|
|
|
|
|
|
|
|
2016-08-31 01:30:18 +00:00
|
|
|
class UNENCRYPTED:
|
|
|
|
# Layout: HEADER + PlainText
|
|
|
|
|
2016-09-02 23:35:34 +00:00
|
|
|
def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=1):
|
2016-08-31 01:30:18 +00:00
|
|
|
assert mac_key is None
|
|
|
|
assert enc_key is None
|
2016-09-02 23:35:34 +00:00
|
|
|
self.header_len = header_len
|
2016-08-31 01:30:18 +00:00
|
|
|
self.set_iv(iv)
|
|
|
|
|
2022-03-21 13:30:26 +00:00
|
|
|
def encrypt(self, data, header=b'', iv=None, aad=None):
|
2016-08-31 01:30:18 +00:00
|
|
|
"""
|
|
|
|
IMPORTANT: it is called encrypt to satisfy the crypto api naming convention,
|
|
|
|
but this does NOT encrypt and it does NOT compute and store a MAC either.
|
|
|
|
"""
|
|
|
|
if iv is not None:
|
|
|
|
self.set_iv(iv)
|
|
|
|
assert self.iv is not None, 'iv needs to be set before encrypt is called'
|
|
|
|
return header + data
|
|
|
|
|
2022-03-21 13:30:26 +00:00
|
|
|
def decrypt(self, envelope, aad=None):
|
2016-08-31 01:30:18 +00:00
|
|
|
"""
|
|
|
|
IMPORTANT: it is called decrypt to satisfy the crypto api naming convention,
|
|
|
|
but this does NOT decrypt and it does NOT verify a MAC either, because data
|
|
|
|
is not encrypted and there is no MAC.
|
|
|
|
"""
|
2016-09-02 23:35:34 +00:00
|
|
|
return memoryview(envelope)[self.header_len:]
|
2016-08-31 01:30:18 +00:00
|
|
|
|
|
|
|
def block_count(self, length):
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def set_iv(self, iv):
|
|
|
|
self.iv = iv
|
|
|
|
|
|
|
|
def next_iv(self):
|
|
|
|
return self.iv
|
|
|
|
|
2016-09-02 21:43:15 +00:00
|
|
|
def extract_iv(self, envelope):
|
|
|
|
return 0
|
|
|
|
|
2016-08-31 01:30:18 +00:00
|
|
|
|
2016-12-04 04:20:19 +00:00
|
|
|
cdef class AES256_CTR_BASE:
|
2023-05-11 18:10:55 +00:00
|
|
|
# Layout: HEADER + MAC 32 + IV 8 + CT (same as attic / borg < 2.0 IF HEADER = TYPE_BYTE, no AAD)
|
2016-05-09 01:46:54 +00:00
|
|
|
|
2016-06-20 21:40:30 +00:00
|
|
|
cdef EVP_CIPHER_CTX *ctx
|
2022-03-20 04:31:47 +00:00
|
|
|
cdef unsigned char enc_key[32]
|
2016-08-29 14:06:40 +00:00
|
|
|
cdef int cipher_blk_len
|
2016-08-29 13:00:10 +00:00
|
|
|
cdef int iv_len, iv_len_short
|
2016-09-02 23:35:34 +00:00
|
|
|
cdef int aad_offset
|
|
|
|
cdef int header_len
|
2016-08-29 13:00:10 +00:00
|
|
|
cdef int mac_len
|
2016-08-29 18:36:32 +00:00
|
|
|
cdef unsigned char iv[16]
|
2016-06-26 18:45:22 +00:00
|
|
|
cdef long long blocks
|
2014-03-10 21:50:38 +00:00
|
|
|
|
2017-07-29 10:28:33 +00:00
|
|
|
@classmethod
|
|
|
|
def requirements_check(cls):
|
2022-02-22 21:00:22 +00:00
|
|
|
pass
|
2016-09-07 15:37:37 +00:00
|
|
|
|
2016-09-02 23:35:34 +00:00
|
|
|
def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=1):
|
2016-09-07 15:37:37 +00:00
|
|
|
self.requirements_check()
|
2016-05-09 01:46:54 +00:00
|
|
|
assert isinstance(enc_key, bytes) and len(enc_key) == 32
|
2016-08-29 14:06:40 +00:00
|
|
|
self.cipher_blk_len = 16
|
2016-08-29 18:36:32 +00:00
|
|
|
self.iv_len = sizeof(self.iv)
|
2016-08-29 13:00:10 +00:00
|
|
|
self.iv_len_short = 8
|
2016-09-02 23:35:34 +00:00
|
|
|
assert aad_offset <= header_len
|
|
|
|
self.aad_offset = aad_offset
|
|
|
|
self.header_len = header_len
|
2016-08-29 13:00:10 +00:00
|
|
|
self.mac_len = 32
|
2016-05-09 01:46:54 +00:00
|
|
|
self.enc_key = enc_key
|
|
|
|
if iv is not None:
|
|
|
|
self.set_iv(iv)
|
2016-08-29 18:55:51 +00:00
|
|
|
else:
|
|
|
|
self.blocks = -1 # make sure set_iv is called before encrypt
|
2016-05-09 01:46:54 +00:00
|
|
|
|
2016-09-02 23:35:34 +00:00
|
|
|
def __cinit__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=1):
|
2016-06-20 21:40:30 +00:00
|
|
|
self.ctx = EVP_CIPHER_CTX_new()
|
2014-03-10 21:50:38 +00:00
|
|
|
|
2014-05-13 20:27:57 +00:00
|
|
|
def __dealloc__(self):
|
2016-06-20 21:40:30 +00:00
|
|
|
EVP_CIPHER_CTX_free(self.ctx)
|
2016-12-04 04:20:19 +00:00
|
|
|
|
|
|
|
cdef mac_compute(self, const unsigned char *data1, int data1_len,
|
|
|
|
const unsigned char *data2, int data2_len,
|
2017-07-29 10:28:06 +00:00
|
|
|
unsigned char *mac_buf):
|
2016-12-04 04:20:19 +00:00
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
cdef mac_verify(self, const unsigned char *data1, int data1_len,
|
|
|
|
const unsigned char *data2, int data2_len,
|
2017-07-29 10:28:06 +00:00
|
|
|
unsigned char *mac_buf, const unsigned char *mac_wanted):
|
|
|
|
"""
|
|
|
|
Calculate MAC of *data1*, *data2*, write result to *mac_buf*, and verify against *mac_wanted.*
|
|
|
|
"""
|
2016-12-04 04:20:19 +00:00
|
|
|
raise NotImplementedError
|
2016-05-09 01:46:54 +00:00
|
|
|
|
2022-03-21 13:30:26 +00:00
|
|
|
def encrypt(self, data, header=b'', iv=None, aad=None):
|
2016-05-09 01:46:54 +00:00
|
|
|
"""
|
|
|
|
encrypt data, compute mac over aad + iv + cdata, prepend header.
|
|
|
|
aad_offset is the offset into the header where aad starts.
|
|
|
|
"""
|
2016-08-29 19:16:51 +00:00
|
|
|
if iv is not None:
|
|
|
|
self.set_iv(iv)
|
|
|
|
assert self.blocks == 0, 'iv needs to be set before encrypt is called'
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int ilen = len(data)
|
|
|
|
cdef int hlen = len(header)
|
2016-09-02 23:35:34 +00:00
|
|
|
assert hlen == self.header_len
|
|
|
|
cdef int aoffset = self.aad_offset
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int alen = hlen - aoffset
|
2016-08-29 14:06:40 +00:00
|
|
|
cdef unsigned char *odata = <unsigned char *>PyMem_Malloc(hlen + self.mac_len + self.iv_len_short +
|
|
|
|
ilen + self.cipher_blk_len) # play safe, 1 extra blk
|
2016-05-09 01:46:54 +00:00
|
|
|
if not odata:
|
|
|
|
raise MemoryError
|
2022-03-22 19:19:21 +00:00
|
|
|
cdef int olen = 0
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int offset
|
|
|
|
cdef Py_buffer idata = ro_buffer(data)
|
|
|
|
cdef Py_buffer hdata = ro_buffer(header)
|
|
|
|
try:
|
|
|
|
offset = 0
|
|
|
|
for i in range(hlen):
|
|
|
|
odata[offset+i] = header[i]
|
|
|
|
offset += hlen
|
2016-08-29 13:00:10 +00:00
|
|
|
offset += self.mac_len
|
2016-05-09 01:46:54 +00:00
|
|
|
self.store_iv(odata+offset, self.iv)
|
2016-08-29 13:00:10 +00:00
|
|
|
offset += self.iv_len_short
|
2022-03-21 21:33:44 +00:00
|
|
|
if not EVP_EncryptInit_ex(self.ctx, EVP_aes_256_ctr(), NULL, self.enc_key, self.iv):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_EncryptInit_ex failed')
|
2022-03-21 21:33:44 +00:00
|
|
|
if not EVP_EncryptUpdate(self.ctx, odata+offset, &olen, <const unsigned char*> idata.buf, ilen):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_EncryptUpdate failed')
|
|
|
|
offset += olen
|
2022-03-21 21:33:44 +00:00
|
|
|
if not EVP_EncryptFinal_ex(self.ctx, odata+offset, &olen):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_EncryptFinal_ex failed')
|
|
|
|
offset += olen
|
2016-12-04 04:20:19 +00:00
|
|
|
self.mac_compute(<const unsigned char *> hdata.buf+aoffset, alen,
|
|
|
|
odata+hlen+self.mac_len, offset-hlen-self.mac_len,
|
|
|
|
odata+hlen)
|
2016-08-29 14:06:40 +00:00
|
|
|
self.blocks += self.block_count(ilen)
|
2016-05-09 01:46:54 +00:00
|
|
|
return odata[:offset]
|
|
|
|
finally:
|
2016-08-28 23:53:09 +00:00
|
|
|
PyMem_Free(odata)
|
2016-05-09 01:46:54 +00:00
|
|
|
PyBuffer_Release(&hdata)
|
|
|
|
PyBuffer_Release(&idata)
|
|
|
|
|
2022-03-21 13:30:26 +00:00
|
|
|
def decrypt(self, envelope, aad=None):
|
2016-05-09 01:46:54 +00:00
|
|
|
"""
|
|
|
|
authenticate aad + iv + cdata, decrypt cdata, ignore header bytes up to aad_offset.
|
|
|
|
"""
|
|
|
|
cdef int ilen = len(envelope)
|
2016-09-02 23:35:34 +00:00
|
|
|
cdef int hlen = self.header_len
|
|
|
|
cdef int aoffset = self.aad_offset
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int alen = hlen - aoffset
|
2016-08-29 14:06:40 +00:00
|
|
|
cdef unsigned char *odata = <unsigned char *>PyMem_Malloc(ilen + self.cipher_blk_len) # play safe, 1 extra blk
|
2016-05-09 01:46:54 +00:00
|
|
|
if not odata:
|
|
|
|
raise MemoryError
|
2022-03-22 19:19:21 +00:00
|
|
|
cdef int olen = 0
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int offset
|
2016-12-04 04:20:19 +00:00
|
|
|
cdef unsigned char mac_buf[32]
|
|
|
|
assert sizeof(mac_buf) == self.mac_len
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef Py_buffer idata = ro_buffer(envelope)
|
|
|
|
try:
|
2016-12-04 04:20:19 +00:00
|
|
|
self.mac_verify(<const unsigned char *> idata.buf+aoffset, alen,
|
|
|
|
<const unsigned char *> idata.buf+hlen+self.mac_len, ilen-hlen-self.mac_len,
|
|
|
|
mac_buf, <const unsigned char *> idata.buf+hlen)
|
2016-08-29 13:00:10 +00:00
|
|
|
iv = self.fetch_iv(<unsigned char *> idata.buf+hlen+self.mac_len)
|
2016-05-09 01:46:54 +00:00
|
|
|
self.set_iv(iv)
|
|
|
|
if not EVP_DecryptInit_ex(self.ctx, EVP_aes_256_ctr(), NULL, self.enc_key, iv):
|
|
|
|
raise CryptoError('EVP_DecryptInit_ex failed')
|
|
|
|
offset = 0
|
2022-03-21 21:33:44 +00:00
|
|
|
if not EVP_DecryptUpdate(self.ctx, odata+offset, &olen,
|
|
|
|
<const unsigned char*> idata.buf+hlen+self.mac_len+self.iv_len_short,
|
|
|
|
ilen-hlen-self.mac_len-self.iv_len_short):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_DecryptUpdate failed')
|
|
|
|
offset += olen
|
2022-03-21 21:36:16 +00:00
|
|
|
if not EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_DecryptFinal_ex failed')
|
|
|
|
offset += olen
|
2016-08-29 14:06:40 +00:00
|
|
|
self.blocks += self.block_count(offset)
|
2016-05-09 01:46:54 +00:00
|
|
|
return odata[:offset]
|
|
|
|
finally:
|
2016-08-28 23:53:09 +00:00
|
|
|
PyMem_Free(odata)
|
2016-05-09 01:46:54 +00:00
|
|
|
PyBuffer_Release(&idata)
|
|
|
|
|
2016-08-29 14:06:40 +00:00
|
|
|
def block_count(self, length):
|
2016-09-02 19:51:22 +00:00
|
|
|
return num_cipher_blocks(length, self.cipher_blk_len)
|
2016-08-29 14:06:40 +00:00
|
|
|
|
2016-05-09 01:46:54 +00:00
|
|
|
def set_iv(self, iv):
|
2016-08-29 18:55:51 +00:00
|
|
|
# set_iv needs to be called before each encrypt() call
|
2016-09-08 03:13:23 +00:00
|
|
|
if isinstance(iv, int):
|
|
|
|
iv = iv.to_bytes(self.iv_len, byteorder='big')
|
2016-08-29 16:11:12 +00:00
|
|
|
assert isinstance(iv, bytes) and len(iv) == self.iv_len
|
2022-03-20 04:31:47 +00:00
|
|
|
self.iv = iv
|
2017-07-25 23:21:24 +00:00
|
|
|
self.blocks = 0 # how many AES blocks got encrypted with this IV?
|
2016-05-09 01:46:54 +00:00
|
|
|
|
|
|
|
def next_iv(self):
|
2016-09-08 03:13:23 +00:00
|
|
|
# call this after encrypt() to get the next iv (int) for the next encrypt() call
|
|
|
|
iv = int.from_bytes(self.iv[:self.iv_len], byteorder='big')
|
|
|
|
return iv + self.blocks
|
2016-05-09 01:46:54 +00:00
|
|
|
|
|
|
|
cdef fetch_iv(self, unsigned char * iv_in):
|
2016-08-29 13:00:10 +00:00
|
|
|
# fetch lower self.iv_len_short bytes of iv and add upper zero bytes
|
|
|
|
return b'\0' * (self.iv_len - self.iv_len_short) + iv_in[0:self.iv_len_short]
|
2016-05-09 01:46:54 +00:00
|
|
|
|
|
|
|
cdef store_iv(self, unsigned char * iv_out, unsigned char * iv):
|
2016-08-29 13:00:10 +00:00
|
|
|
# store only lower self.iv_len_short bytes, upper bytes are assumed to be 0
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int i
|
2016-08-29 13:00:10 +00:00
|
|
|
for i in range(self.iv_len_short):
|
|
|
|
iv_out[i] = iv[(self.iv_len-self.iv_len_short)+i]
|
2016-05-09 01:46:54 +00:00
|
|
|
|
2016-09-02 21:43:15 +00:00
|
|
|
def extract_iv(self, envelope):
|
2016-09-02 23:35:34 +00:00
|
|
|
offset = self.header_len + self.mac_len
|
2016-09-02 21:43:15 +00:00
|
|
|
return bytes_to_long(envelope[offset:offset+self.iv_len_short])
|
|
|
|
|
2016-05-09 01:46:54 +00:00
|
|
|
|
2016-12-04 04:20:19 +00:00
|
|
|
cdef class AES256_CTR_HMAC_SHA256(AES256_CTR_BASE):
|
2022-03-20 04:31:47 +00:00
|
|
|
cdef unsigned char mac_key[32]
|
2016-12-04 04:20:19 +00:00
|
|
|
|
|
|
|
def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=1):
|
|
|
|
assert isinstance(mac_key, bytes) and len(mac_key) == 32
|
|
|
|
self.mac_key = mac_key
|
|
|
|
super().__init__(mac_key, enc_key, iv=iv, header_len=header_len, aad_offset=aad_offset)
|
|
|
|
|
|
|
|
def __cinit__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=1):
|
2022-02-23 02:04:33 +00:00
|
|
|
pass
|
2016-12-04 04:20:19 +00:00
|
|
|
|
|
|
|
def __dealloc__(self):
|
2022-02-23 02:04:33 +00:00
|
|
|
pass
|
2016-12-04 04:20:19 +00:00
|
|
|
|
|
|
|
cdef mac_compute(self, const unsigned char *data1, int data1_len,
|
|
|
|
const unsigned char *data2, int data2_len,
|
2017-07-29 10:28:06 +00:00
|
|
|
unsigned char *mac_buf):
|
2022-02-23 02:04:33 +00:00
|
|
|
data = data1[:data1_len] + data2[:data2_len]
|
2022-03-20 13:16:19 +00:00
|
|
|
mac = hmac.digest(self.mac_key[:self.mac_len], data, 'sha256')
|
2022-02-23 02:04:33 +00:00
|
|
|
for i in range(self.mac_len):
|
|
|
|
mac_buf[i] = mac[i]
|
2016-12-04 04:20:19 +00:00
|
|
|
|
|
|
|
cdef mac_verify(self, const unsigned char *data1, int data1_len,
|
|
|
|
const unsigned char *data2, int data2_len,
|
2017-07-29 10:28:06 +00:00
|
|
|
unsigned char *mac_buf, const unsigned char *mac_wanted):
|
2016-12-04 04:20:19 +00:00
|
|
|
self.mac_compute(data1, data1_len, data2, data2_len, mac_buf)
|
|
|
|
if CRYPTO_memcmp(mac_buf, mac_wanted, self.mac_len):
|
|
|
|
raise IntegrityError('MAC Authentication failed')
|
|
|
|
|
|
|
|
|
|
|
|
cdef class AES256_CTR_BLAKE2b(AES256_CTR_BASE):
|
2022-03-20 04:31:47 +00:00
|
|
|
cdef unsigned char mac_key[128]
|
2016-12-04 04:20:19 +00:00
|
|
|
|
|
|
|
def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=1):
|
|
|
|
assert isinstance(mac_key, bytes) and len(mac_key) == 128
|
|
|
|
self.mac_key = mac_key
|
|
|
|
super().__init__(mac_key, enc_key, iv=iv, header_len=header_len, aad_offset=aad_offset)
|
|
|
|
|
|
|
|
def __cinit__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=1):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def __dealloc__(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
cdef mac_compute(self, const unsigned char *data1, int data1_len,
|
|
|
|
const unsigned char *data2, int data2_len,
|
2017-07-29 10:28:06 +00:00
|
|
|
unsigned char *mac_buf):
|
2021-01-24 19:09:19 +00:00
|
|
|
data = self.mac_key[:128] + data1[:data1_len] + data2[:data2_len]
|
|
|
|
mac = hashlib.blake2b(data, digest_size=self.mac_len).digest()
|
|
|
|
for i in range(self.mac_len):
|
|
|
|
mac_buf[i] = mac[i]
|
2016-12-04 04:20:19 +00:00
|
|
|
|
|
|
|
cdef mac_verify(self, const unsigned char *data1, int data1_len,
|
|
|
|
const unsigned char *data2, int data2_len,
|
2017-07-29 10:28:06 +00:00
|
|
|
unsigned char *mac_buf, const unsigned char *mac_wanted):
|
2016-12-04 04:20:19 +00:00
|
|
|
self.mac_compute(data1, data1_len, data2, data2_len, mac_buf)
|
|
|
|
if CRYPTO_memcmp(mac_buf, mac_wanted, self.mac_len):
|
|
|
|
raise IntegrityError('MAC Authentication failed')
|
2016-12-03 23:49:11 +00:00
|
|
|
|
|
|
|
|
2016-08-16 04:34:52 +00:00
|
|
|
ctypedef const EVP_CIPHER * (* CIPHER)()
|
|
|
|
|
2014-05-13 20:27:57 +00:00
|
|
|
|
2016-08-16 04:34:52 +00:00
|
|
|
cdef class _AEAD_BASE:
|
2023-05-11 18:10:55 +00:00
|
|
|
# new crypto used in borg >= 2.0
|
2022-03-18 18:00:58 +00:00
|
|
|
# Layout: HEADER + MAC 16 + CT
|
2016-08-16 04:34:52 +00:00
|
|
|
|
|
|
|
cdef CIPHER cipher
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef EVP_CIPHER_CTX *ctx
|
2022-03-20 04:15:39 +00:00
|
|
|
cdef unsigned char key[32]
|
2016-08-29 14:06:40 +00:00
|
|
|
cdef int cipher_blk_len
|
2016-08-29 13:00:10 +00:00
|
|
|
cdef int iv_len
|
2016-09-02 23:35:34 +00:00
|
|
|
cdef int aad_offset
|
2022-03-18 18:00:58 +00:00
|
|
|
cdef int header_len_expected
|
2016-08-29 13:00:10 +00:00
|
|
|
cdef int mac_len
|
2016-08-29 18:36:32 +00:00
|
|
|
cdef unsigned char iv[12]
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef long long blocks
|
|
|
|
|
2017-07-29 10:28:33 +00:00
|
|
|
@classmethod
|
|
|
|
def requirements_check(cls):
|
2016-09-07 15:37:37 +00:00
|
|
|
"""check whether library requirements for this ciphersuite are satisfied"""
|
|
|
|
raise NotImplemented # override / implement in child class
|
|
|
|
|
2022-03-18 20:24:19 +00:00
|
|
|
def __init__(self, key, iv=None, header_len=0, aad_offset=0):
|
2022-03-17 21:30:40 +00:00
|
|
|
"""
|
|
|
|
init AEAD crypto
|
|
|
|
|
2022-03-17 22:09:27 +00:00
|
|
|
:param key: 256bit encrypt-then-mac key
|
2022-03-17 21:30:40 +00:00
|
|
|
:param iv: 96bit initialisation vector / nonce
|
2022-03-18 18:00:58 +00:00
|
|
|
:param header_len: expected length of header
|
2022-03-17 21:30:40 +00:00
|
|
|
:param aad_offset: where in the header the authenticated data starts
|
|
|
|
"""
|
2022-03-17 22:09:27 +00:00
|
|
|
assert isinstance(key, bytes) and len(key) == 32
|
2016-08-29 18:36:32 +00:00
|
|
|
self.iv_len = sizeof(self.iv)
|
2022-03-18 18:00:58 +00:00
|
|
|
self.header_len_expected = header_len
|
2016-09-02 23:35:34 +00:00
|
|
|
assert aad_offset <= header_len
|
|
|
|
self.aad_offset = aad_offset
|
2016-08-29 13:00:10 +00:00
|
|
|
self.mac_len = 16
|
2022-03-17 22:09:27 +00:00
|
|
|
self.key = key
|
2016-05-09 01:46:54 +00:00
|
|
|
if iv is not None:
|
|
|
|
self.set_iv(iv)
|
2016-08-29 18:55:51 +00:00
|
|
|
else:
|
|
|
|
self.blocks = -1 # make sure set_iv is called before encrypt
|
2016-05-09 01:46:54 +00:00
|
|
|
|
2022-03-18 20:24:19 +00:00
|
|
|
def __cinit__(self, key, iv=None, header_len=0, aad_offset=0):
|
2016-05-09 01:46:54 +00:00
|
|
|
self.ctx = EVP_CIPHER_CTX_new()
|
|
|
|
|
|
|
|
def __dealloc__(self):
|
|
|
|
EVP_CIPHER_CTX_free(self.ctx)
|
|
|
|
|
2022-03-21 13:30:26 +00:00
|
|
|
def encrypt(self, data, header=b'', iv=None, aad=b''):
|
2016-05-09 01:46:54 +00:00
|
|
|
"""
|
2022-03-21 13:30:26 +00:00
|
|
|
encrypt data, compute auth tag over aad + header + cdata.
|
|
|
|
return header + auth tag + cdata.
|
|
|
|
aad_offset is the offset into the header where the authenticated header part starts.
|
|
|
|
aad is additional authenticated data, which won't be included in the returned data,
|
|
|
|
but only used for the auth tag computation.
|
2016-05-09 01:46:54 +00:00
|
|
|
"""
|
2016-08-29 19:16:51 +00:00
|
|
|
if iv is not None:
|
|
|
|
self.set_iv(iv)
|
|
|
|
assert self.blocks == 0, 'iv needs to be set before encrypt is called'
|
2022-03-17 19:50:37 +00:00
|
|
|
# AES-OCB, CHACHA20 ciphers all add a internal 32bit counter to the 96bit (12Byte)
|
2016-09-10 13:32:32 +00:00
|
|
|
# IV we provide, thus we must not encrypt more than 2^32 cipher blocks with same IV).
|
|
|
|
block_count = self.block_count(len(data))
|
|
|
|
if block_count > 2**32:
|
|
|
|
raise ValueError('too much data, would overflow internal 32bit counter')
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int ilen = len(data)
|
2022-03-18 18:00:58 +00:00
|
|
|
cdef int hlen = len(header)
|
2022-03-17 21:41:14 +00:00
|
|
|
assert hlen == self.header_len_expected
|
2016-09-02 23:35:34 +00:00
|
|
|
cdef int aoffset = self.aad_offset
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int alen = hlen - aoffset
|
2022-03-21 13:30:26 +00:00
|
|
|
cdef int aadlen = len(aad)
|
2022-03-17 21:30:40 +00:00
|
|
|
cdef unsigned char *odata = <unsigned char *>PyMem_Malloc(hlen + self.mac_len +
|
2016-08-29 14:06:40 +00:00
|
|
|
ilen + self.cipher_blk_len)
|
2016-05-09 01:46:54 +00:00
|
|
|
if not odata:
|
2014-03-10 21:50:38 +00:00
|
|
|
raise MemoryError
|
2022-03-22 19:19:21 +00:00
|
|
|
cdef int olen = 0
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int offset
|
|
|
|
cdef Py_buffer idata = ro_buffer(data)
|
|
|
|
cdef Py_buffer hdata = ro_buffer(header)
|
2022-03-21 13:30:26 +00:00
|
|
|
cdef Py_buffer aadata = ro_buffer(aad)
|
2014-03-10 21:50:38 +00:00
|
|
|
try:
|
2016-05-09 01:46:54 +00:00
|
|
|
offset = 0
|
2022-03-18 18:00:58 +00:00
|
|
|
for i in range(hlen):
|
2016-05-09 01:46:54 +00:00
|
|
|
odata[offset+i] = header[i]
|
2022-03-18 18:00:58 +00:00
|
|
|
offset += hlen
|
2022-03-17 21:30:40 +00:00
|
|
|
offset += self.mac_len
|
2022-03-21 21:29:18 +00:00
|
|
|
if not EVP_EncryptInit_ex(self.ctx, self.cipher(), NULL, NULL, NULL):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_EncryptInit_ex failed')
|
2022-03-17 19:50:37 +00:00
|
|
|
if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_IVLEN, self.iv_len, NULL):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed')
|
2022-03-21 21:29:18 +00:00
|
|
|
if not EVP_EncryptInit_ex(self.ctx, NULL, NULL, self.key, self.iv):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_EncryptInit_ex failed')
|
2022-03-21 21:29:18 +00:00
|
|
|
if not EVP_EncryptUpdate(self.ctx, NULL, &olen, <const unsigned char*> aadata.buf, aadlen):
|
2022-03-21 13:30:26 +00:00
|
|
|
raise CryptoError('EVP_EncryptUpdate failed')
|
2022-03-21 21:29:18 +00:00
|
|
|
if not EVP_EncryptUpdate(self.ctx, NULL, &olen, <const unsigned char*> hdata.buf+aoffset, alen):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_EncryptUpdate failed')
|
2022-03-21 21:29:18 +00:00
|
|
|
if not EVP_EncryptUpdate(self.ctx, odata+offset, &olen, <const unsigned char*> idata.buf, ilen):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_EncryptUpdate failed')
|
|
|
|
offset += olen
|
2022-03-21 21:29:18 +00:00
|
|
|
if not EVP_EncryptFinal_ex(self.ctx, odata+offset, &olen):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_EncryptFinal_ex failed')
|
|
|
|
offset += olen
|
2022-03-17 19:50:37 +00:00
|
|
|
if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_GET_TAG, self.mac_len, odata + hlen):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_CIPHER_CTX_ctrl GET TAG failed')
|
2016-09-10 13:32:32 +00:00
|
|
|
self.blocks = block_count
|
2016-05-09 01:46:54 +00:00
|
|
|
return odata[:offset]
|
2014-03-10 21:50:38 +00:00
|
|
|
finally:
|
2016-08-28 23:53:09 +00:00
|
|
|
PyMem_Free(odata)
|
2016-05-09 01:46:54 +00:00
|
|
|
PyBuffer_Release(&hdata)
|
|
|
|
PyBuffer_Release(&idata)
|
2022-03-21 13:30:26 +00:00
|
|
|
PyBuffer_Release(&aadata)
|
2016-05-09 01:46:54 +00:00
|
|
|
|
2022-03-21 13:30:26 +00:00
|
|
|
def decrypt(self, envelope, aad=b''):
|
2016-05-09 01:46:54 +00:00
|
|
|
"""
|
2022-03-21 13:30:26 +00:00
|
|
|
authenticate aad + header + cdata (from envelope), ignore header bytes up to aad_offset.,
|
|
|
|
return decrypted cdata.
|
2016-05-09 01:46:54 +00:00
|
|
|
"""
|
2022-03-17 19:50:37 +00:00
|
|
|
# AES-OCB, CHACHA20 ciphers all add a internal 32bit counter to the 96bit (12Byte)
|
2016-09-10 13:32:32 +00:00
|
|
|
# IV we provide, thus we must not decrypt more than 2^32 cipher blocks with same IV):
|
|
|
|
approx_block_count = self.block_count(len(envelope)) # sloppy, but good enough for borg
|
|
|
|
if approx_block_count > 2**32:
|
|
|
|
raise ValueError('too much data, would overflow internal 32bit counter')
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int ilen = len(envelope)
|
2022-03-17 21:41:14 +00:00
|
|
|
cdef int hlen = self.header_len_expected
|
2016-09-02 23:35:34 +00:00
|
|
|
cdef int aoffset = self.aad_offset
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int alen = hlen - aoffset
|
2022-03-21 13:30:26 +00:00
|
|
|
cdef int aadlen = len(aad)
|
2016-08-29 14:06:40 +00:00
|
|
|
cdef unsigned char *odata = <unsigned char *>PyMem_Malloc(ilen + self.cipher_blk_len)
|
2016-05-09 01:46:54 +00:00
|
|
|
if not odata:
|
cleanup crypto.pyx, make it easier to adapt to other modes
There were some small issues:
a) it never called EVP_EncryptFinal_ex.
For CTR mode, this had no visible consequences as EVP_EncryptUpdate already yielded all ciphertext.
For cleanliness and to have correctness even in other modes, the missing call was added.
b) decrypt = encrypt hack
This is a nice hack to abbreviate, but it only works for modes without padding and without authentication.
For cleanliness and to have correctness even in other modes, the missing usage of the decrypt api was added.
c) outl == inl assumption
Again, True for CTR mode, but not for padding or authenticating modes.
Fixed so it computes the ciphertext / plaintext length based on api return values.
Other changes:
As encrypt and decrypt API calls are different even for initialization/reset, added a is_encrypt flag.
Defensive output buffer allocation. Added the length of one extra AES block (16bytes) so it would
work even with padding modes. 16bytes are needed because a full block of padding might get
added when the plaintext was a multiple of aes block size.
These changes are based on some experimental code I did for aes-cbc and aes-gcm.
While we likely won't ever want aes-cbc in attic (maybe gcm though?), I think it is cleaner
to not make too many mode specific assumptions and hacks, but just use the API as it
was meant to be used.
2015-03-03 18:19:28 +00:00
|
|
|
raise MemoryError
|
2022-03-22 19:19:21 +00:00
|
|
|
cdef int olen = 0
|
2016-05-09 01:46:54 +00:00
|
|
|
cdef int offset
|
|
|
|
cdef Py_buffer idata = ro_buffer(envelope)
|
2022-03-21 13:30:26 +00:00
|
|
|
cdef Py_buffer aadata = ro_buffer(aad)
|
cleanup crypto.pyx, make it easier to adapt to other modes
There were some small issues:
a) it never called EVP_EncryptFinal_ex.
For CTR mode, this had no visible consequences as EVP_EncryptUpdate already yielded all ciphertext.
For cleanliness and to have correctness even in other modes, the missing call was added.
b) decrypt = encrypt hack
This is a nice hack to abbreviate, but it only works for modes without padding and without authentication.
For cleanliness and to have correctness even in other modes, the missing usage of the decrypt api was added.
c) outl == inl assumption
Again, True for CTR mode, but not for padding or authenticating modes.
Fixed so it computes the ciphertext / plaintext length based on api return values.
Other changes:
As encrypt and decrypt API calls are different even for initialization/reset, added a is_encrypt flag.
Defensive output buffer allocation. Added the length of one extra AES block (16bytes) so it would
work even with padding modes. 16bytes are needed because a full block of padding might get
added when the plaintext was a multiple of aes block size.
These changes are based on some experimental code I did for aes-cbc and aes-gcm.
While we likely won't ever want aes-cbc in attic (maybe gcm though?), I think it is cleaner
to not make too many mode specific assumptions and hacks, but just use the API as it
was meant to be used.
2015-03-03 18:19:28 +00:00
|
|
|
try:
|
2016-08-16 04:34:52 +00:00
|
|
|
if not EVP_DecryptInit_ex(self.ctx, self.cipher(), NULL, NULL, NULL):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_DecryptInit_ex failed')
|
2022-03-17 19:50:37 +00:00
|
|
|
if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_IVLEN, self.iv_len, NULL):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed')
|
2022-03-18 18:00:58 +00:00
|
|
|
if not EVP_DecryptInit_ex(self.ctx, NULL, NULL, self.key, self.iv):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_DecryptInit_ex failed')
|
2022-03-21 21:29:18 +00:00
|
|
|
if not EVP_DecryptUpdate(self.ctx, NULL, &olen, <const unsigned char*> aadata.buf, aadlen):
|
2022-03-21 13:30:26 +00:00
|
|
|
raise CryptoError('EVP_DecryptUpdate failed')
|
2022-03-21 21:29:18 +00:00
|
|
|
if not EVP_DecryptUpdate(self.ctx, NULL, &olen, <const unsigned char*> idata.buf+aoffset, alen):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_DecryptUpdate failed')
|
|
|
|
offset = 0
|
2022-03-21 21:29:18 +00:00
|
|
|
if not EVP_DecryptUpdate(self.ctx, odata+offset, &olen,
|
|
|
|
<const unsigned char*> idata.buf+hlen+self.mac_len,
|
|
|
|
ilen-hlen-self.mac_len):
|
2016-05-09 01:46:54 +00:00
|
|
|
raise CryptoError('EVP_DecryptUpdate failed')
|
|
|
|
offset += olen
|
2022-03-18 22:43:42 +00:00
|
|
|
if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_TAG, self.mac_len, <unsigned char *> idata.buf + hlen):
|
|
|
|
raise CryptoError('EVP_CIPHER_CTX_ctrl SET TAG failed')
|
2022-03-21 21:29:18 +00:00
|
|
|
if not EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen):
|
2016-05-09 01:46:54 +00:00
|
|
|
# a failure here means corrupted or tampered tag (mac) or data.
|
2016-08-16 04:34:52 +00:00
|
|
|
raise IntegrityError('Authentication / EVP_DecryptFinal_ex failed')
|
2016-05-09 01:46:54 +00:00
|
|
|
offset += olen
|
2016-08-29 18:55:51 +00:00
|
|
|
self.blocks = self.block_count(offset)
|
2016-05-09 01:46:54 +00:00
|
|
|
return odata[:offset]
|
cleanup crypto.pyx, make it easier to adapt to other modes
There were some small issues:
a) it never called EVP_EncryptFinal_ex.
For CTR mode, this had no visible consequences as EVP_EncryptUpdate already yielded all ciphertext.
For cleanliness and to have correctness even in other modes, the missing call was added.
b) decrypt = encrypt hack
This is a nice hack to abbreviate, but it only works for modes without padding and without authentication.
For cleanliness and to have correctness even in other modes, the missing usage of the decrypt api was added.
c) outl == inl assumption
Again, True for CTR mode, but not for padding or authenticating modes.
Fixed so it computes the ciphertext / plaintext length based on api return values.
Other changes:
As encrypt and decrypt API calls are different even for initialization/reset, added a is_encrypt flag.
Defensive output buffer allocation. Added the length of one extra AES block (16bytes) so it would
work even with padding modes. 16bytes are needed because a full block of padding might get
added when the plaintext was a multiple of aes block size.
These changes are based on some experimental code I did for aes-cbc and aes-gcm.
While we likely won't ever want aes-cbc in attic (maybe gcm though?), I think it is cleaner
to not make too many mode specific assumptions and hacks, but just use the API as it
was meant to be used.
2015-03-03 18:19:28 +00:00
|
|
|
finally:
|
2016-08-28 23:53:09 +00:00
|
|
|
PyMem_Free(odata)
|
2016-05-09 01:46:54 +00:00
|
|
|
PyBuffer_Release(&idata)
|
2022-03-21 13:30:26 +00:00
|
|
|
PyBuffer_Release(&aadata)
|
2016-05-09 01:46:54 +00:00
|
|
|
|
2016-08-29 14:06:40 +00:00
|
|
|
def block_count(self, length):
|
2016-09-02 19:51:22 +00:00
|
|
|
return num_cipher_blocks(length, self.cipher_blk_len)
|
2016-08-29 14:06:40 +00:00
|
|
|
|
2016-05-09 01:46:54 +00:00
|
|
|
def set_iv(self, iv):
|
2016-08-29 18:55:51 +00:00
|
|
|
# set_iv needs to be called before each encrypt() call,
|
|
|
|
# because encrypt does a full initialisation of the cipher context.
|
2016-09-08 03:13:23 +00:00
|
|
|
if isinstance(iv, int):
|
|
|
|
iv = iv.to_bytes(self.iv_len, byteorder='big')
|
2016-08-29 16:11:12 +00:00
|
|
|
assert isinstance(iv, bytes) and len(iv) == self.iv_len
|
2022-03-20 04:15:39 +00:00
|
|
|
self.iv = iv
|
2017-07-25 23:21:24 +00:00
|
|
|
self.blocks = 0 # number of cipher blocks encrypted with this IV
|
2016-05-09 01:46:54 +00:00
|
|
|
|
|
|
|
def next_iv(self):
|
2016-09-08 03:13:23 +00:00
|
|
|
# call this after encrypt() to get the next iv (int) for the next encrypt() call
|
2022-03-17 19:50:37 +00:00
|
|
|
# AES-OCB, CHACHA20 ciphers all add a internal 32bit counter to the 96bit
|
2016-09-10 13:32:32 +00:00
|
|
|
# (12 byte) IV we provide, thus we only need to increment the IV by 1.
|
2016-09-08 03:13:23 +00:00
|
|
|
iv = int.from_bytes(self.iv[:self.iv_len], byteorder='big')
|
|
|
|
return iv + 1
|
2016-05-09 01:46:54 +00:00
|
|
|
|
2016-03-27 00:12:23 +00:00
|
|
|
|
2022-03-22 01:57:28 +00:00
|
|
|
cdef class AES256_OCB(_AEAD_BASE):
|
2017-07-29 10:28:33 +00:00
|
|
|
@classmethod
|
|
|
|
def requirements_check(cls):
|
2022-04-14 17:16:25 +00:00
|
|
|
pass
|
2016-09-07 15:37:37 +00:00
|
|
|
|
2022-03-18 20:24:19 +00:00
|
|
|
def __init__(self, key, iv=None, header_len=0, aad_offset=0):
|
2016-09-07 15:37:37 +00:00
|
|
|
self.requirements_check()
|
2016-08-16 04:34:52 +00:00
|
|
|
self.cipher = EVP_aes_256_ocb
|
2022-03-22 01:57:28 +00:00
|
|
|
self.cipher_blk_len = 16
|
2022-03-17 22:09:27 +00:00
|
|
|
super().__init__(key, iv=iv, header_len=header_len, aad_offset=aad_offset)
|
2016-08-16 04:34:52 +00:00
|
|
|
|
|
|
|
|
2022-03-22 01:57:28 +00:00
|
|
|
cdef class CHACHA20_POLY1305(_AEAD_BASE):
|
2017-07-29 10:28:33 +00:00
|
|
|
@classmethod
|
|
|
|
def requirements_check(cls):
|
2022-04-14 17:16:25 +00:00
|
|
|
pass
|
2016-09-07 15:37:37 +00:00
|
|
|
|
2022-03-18 20:24:19 +00:00
|
|
|
def __init__(self, key, iv=None, header_len=0, aad_offset=0):
|
2016-09-07 15:37:37 +00:00
|
|
|
self.requirements_check()
|
2016-08-16 04:34:52 +00:00
|
|
|
self.cipher = EVP_chacha20_poly1305
|
2022-03-22 01:57:28 +00:00
|
|
|
self.cipher_blk_len = 64
|
2022-03-17 22:09:27 +00:00
|
|
|
super().__init__(key, iv=iv, header_len=header_len, aad_offset=aad_offset)
|
2016-08-16 04:34:52 +00:00
|
|
|
|
|
|
|
|
2022-06-29 23:13:49 +00:00
|
|
|
cdef class AES: # legacy
|
2016-08-29 21:14:47 +00:00
|
|
|
"""A thin wrapper around the OpenSSL EVP cipher API - for legacy code, like key file encryption"""
|
2016-08-30 02:03:27 +00:00
|
|
|
cdef CIPHER cipher
|
2016-08-29 21:14:47 +00:00
|
|
|
cdef EVP_CIPHER_CTX *ctx
|
2022-03-20 04:31:47 +00:00
|
|
|
cdef unsigned char enc_key[32]
|
2016-08-30 02:03:27 +00:00
|
|
|
cdef int cipher_blk_len
|
|
|
|
cdef int iv_len
|
|
|
|
cdef unsigned char iv[16]
|
2016-08-29 21:14:47 +00:00
|
|
|
cdef long long blocks
|
|
|
|
|
2016-08-30 02:03:27 +00:00
|
|
|
def __init__(self, enc_key, iv=None):
|
|
|
|
assert isinstance(enc_key, bytes) and len(enc_key) == 32
|
|
|
|
self.enc_key = enc_key
|
|
|
|
self.iv_len = 16
|
|
|
|
assert sizeof(self.iv) == self.iv_len
|
|
|
|
self.cipher = EVP_aes_256_ctr
|
|
|
|
self.cipher_blk_len = 16
|
|
|
|
if iv is not None:
|
|
|
|
self.set_iv(iv)
|
|
|
|
else:
|
|
|
|
self.blocks = -1 # make sure set_iv is called before encrypt
|
|
|
|
|
|
|
|
def __cinit__(self, enc_key, iv=None):
|
2016-08-29 21:14:47 +00:00
|
|
|
self.ctx = EVP_CIPHER_CTX_new()
|
|
|
|
|
|
|
|
def __dealloc__(self):
|
|
|
|
EVP_CIPHER_CTX_free(self.ctx)
|
|
|
|
|
2016-08-30 02:03:27 +00:00
|
|
|
def encrypt(self, data, iv=None):
|
|
|
|
if iv is not None:
|
|
|
|
self.set_iv(iv)
|
|
|
|
assert self.blocks == 0, 'iv needs to be set before encrypt is called'
|
|
|
|
cdef Py_buffer idata = ro_buffer(data)
|
|
|
|
cdef int ilen = len(data)
|
|
|
|
cdef int offset
|
2018-03-03 03:07:25 +00:00
|
|
|
cdef int olen = 0
|
2016-08-30 02:03:27 +00:00
|
|
|
cdef unsigned char *odata = <unsigned char *>PyMem_Malloc(ilen + self.cipher_blk_len)
|
|
|
|
if not odata:
|
2016-08-29 21:14:47 +00:00
|
|
|
raise MemoryError
|
|
|
|
try:
|
2016-08-30 02:03:27 +00:00
|
|
|
if not EVP_EncryptInit_ex(self.ctx, self.cipher(), NULL, self.enc_key, self.iv):
|
|
|
|
raise Exception('EVP_EncryptInit_ex failed')
|
|
|
|
offset = 0
|
|
|
|
if not EVP_EncryptUpdate(self.ctx, odata, &olen, <const unsigned char*> idata.buf, ilen):
|
2016-08-29 21:14:47 +00:00
|
|
|
raise Exception('EVP_EncryptUpdate failed')
|
2016-08-30 02:03:27 +00:00
|
|
|
offset += olen
|
|
|
|
if not EVP_EncryptFinal_ex(self.ctx, odata+offset, &olen):
|
2016-08-29 21:14:47 +00:00
|
|
|
raise Exception('EVP_EncryptFinal failed')
|
2016-08-30 02:03:27 +00:00
|
|
|
offset += olen
|
|
|
|
self.blocks = self.block_count(offset)
|
|
|
|
return odata[:offset]
|
2016-08-29 21:14:47 +00:00
|
|
|
finally:
|
2016-08-30 02:03:27 +00:00
|
|
|
PyMem_Free(odata)
|
|
|
|
PyBuffer_Release(&idata)
|
2016-08-29 21:14:47 +00:00
|
|
|
|
|
|
|
def decrypt(self, data):
|
2016-08-30 02:03:27 +00:00
|
|
|
cdef Py_buffer idata = ro_buffer(data)
|
|
|
|
cdef int ilen = len(data)
|
|
|
|
cdef int offset
|
2018-03-03 03:07:25 +00:00
|
|
|
cdef int olen = 0
|
2016-08-30 02:03:27 +00:00
|
|
|
cdef unsigned char *odata = <unsigned char *>PyMem_Malloc(ilen + self.cipher_blk_len)
|
|
|
|
if not odata:
|
2016-08-29 21:14:47 +00:00
|
|
|
raise MemoryError
|
|
|
|
try:
|
2016-08-30 02:03:27 +00:00
|
|
|
# Set cipher type and mode
|
|
|
|
if not EVP_DecryptInit_ex(self.ctx, self.cipher(), NULL, self.enc_key, self.iv):
|
|
|
|
raise Exception('EVP_DecryptInit_ex failed')
|
|
|
|
offset = 0
|
|
|
|
if not EVP_DecryptUpdate(self.ctx, odata, &olen, <const unsigned char*> idata.buf, ilen):
|
2016-08-29 21:14:47 +00:00
|
|
|
raise Exception('EVP_DecryptUpdate failed')
|
2016-08-30 02:03:27 +00:00
|
|
|
offset += olen
|
2022-03-21 21:36:16 +00:00
|
|
|
if not EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen):
|
2016-08-29 21:14:47 +00:00
|
|
|
# this error check is very important for modes with padding or
|
|
|
|
# authentication. for them, a failure here means corrupted data.
|
|
|
|
# CTR mode does not use padding nor authentication.
|
|
|
|
raise Exception('EVP_DecryptFinal failed')
|
2016-08-30 02:03:27 +00:00
|
|
|
offset += olen
|
|
|
|
self.blocks = self.block_count(ilen)
|
|
|
|
return odata[:offset]
|
2016-08-29 21:14:47 +00:00
|
|
|
finally:
|
2016-08-30 02:03:27 +00:00
|
|
|
PyMem_Free(odata)
|
|
|
|
PyBuffer_Release(&idata)
|
|
|
|
|
|
|
|
def block_count(self, length):
|
2016-09-02 19:51:22 +00:00
|
|
|
return num_cipher_blocks(length, self.cipher_blk_len)
|
2016-08-30 02:03:27 +00:00
|
|
|
|
|
|
|
def set_iv(self, iv):
|
|
|
|
# set_iv needs to be called before each encrypt() call,
|
|
|
|
# because encrypt does a full initialisation of the cipher context.
|
2016-09-08 03:13:23 +00:00
|
|
|
if isinstance(iv, int):
|
|
|
|
iv = iv.to_bytes(self.iv_len, byteorder='big')
|
2016-08-30 02:03:27 +00:00
|
|
|
assert isinstance(iv, bytes) and len(iv) == self.iv_len
|
2022-03-20 04:31:47 +00:00
|
|
|
self.iv = iv
|
2017-07-25 23:21:24 +00:00
|
|
|
self.blocks = 0 # number of cipher blocks encrypted with this IV
|
2016-08-30 02:03:27 +00:00
|
|
|
|
|
|
|
def next_iv(self):
|
2016-09-08 03:13:23 +00:00
|
|
|
# call this after encrypt() to get the next iv (int) for the next encrypt() call
|
|
|
|
iv = int.from_bytes(self.iv[:self.iv_len], byteorder='big')
|
|
|
|
return iv + self.blocks
|
|
|
|
|
2016-08-29 21:14:47 +00:00
|
|
|
|
2016-03-27 00:12:23 +00:00
|
|
|
def hmac_sha256(key, data):
|
2022-03-05 20:22:24 +00:00
|
|
|
return hmac.digest(key, data, 'sha256')
|
2016-11-07 21:08:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
def blake2b_256(key, data):
|
2021-01-24 19:09:19 +00:00
|
|
|
return hashlib.blake2b(key+data, digest_size=32).digest()
|
2016-12-16 23:51:25 +00:00
|
|
|
|
|
|
|
|
2017-07-02 19:45:34 +00:00
|
|
|
def blake2b_128(data):
|
2021-01-24 19:09:19 +00:00
|
|
|
return hashlib.blake2b(data, digest_size=16).digest()
|