From 4effe404157b2017e06d8fdd98d176e1a85ec1ef Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 29 Aug 2016 23:14:47 +0200 Subject: [PATCH] re-add legacy AES() crypto class we need it to encrypt/decrypt key files / config keys. --- src/borg/crypto/low_level.pyx | 98 +++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 154477171..989083884 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -38,6 +38,8 @@ import hashlib import hmac from math import ceil +from libc.stdlib cimport malloc, free + from cpython cimport PyMem_Malloc, PyMem_Free from cpython.buffer cimport PyBUF_SIMPLE, PyObject_GetBuffer, PyBuffer_Release from cpython.bytes cimport PyBytes_FromStringAndSize @@ -559,6 +561,102 @@ cdef class CHACHA20_POLY1305(_CHACHA_BASE): super().__init__(mac_key, enc_key, iv=iv) +cdef class AES: + """A thin wrapper around the OpenSSL EVP cipher API - for legacy code, like key file encryption""" + cdef EVP_CIPHER_CTX *ctx + cdef int is_encrypt + cdef unsigned char iv_orig[16] + cdef long long blocks + + def __cinit__(self, is_encrypt, key, iv=None): + self.ctx = EVP_CIPHER_CTX_new() + self.is_encrypt = is_encrypt + # Set cipher type and mode + cipher_mode = EVP_aes_256_ctr() + if self.is_encrypt: + if not EVP_EncryptInit_ex(self.ctx, cipher_mode, NULL, NULL, NULL): + raise Exception('EVP_EncryptInit_ex failed') + else: # decrypt + if not EVP_DecryptInit_ex(self.ctx, cipher_mode, NULL, NULL, NULL): + raise Exception('EVP_DecryptInit_ex failed') + self.reset(key, iv) + + def __dealloc__(self): + EVP_CIPHER_CTX_free(self.ctx) + + def reset(self, key=None, iv=None): + cdef const unsigned char *key2 = NULL + cdef const unsigned char *iv2 = NULL + if key: + key2 = key + if iv: + iv2 = iv + assert isinstance(iv, bytes) and len(iv) == 16 + for i in range(16): + self.iv_orig[i] = iv[i] + self.blocks = 0 # number of AES blocks encrypted starting with iv_orig + # Initialise key and IV + if self.is_encrypt: + if not EVP_EncryptInit_ex(self.ctx, NULL, NULL, key2, iv2): + raise Exception('EVP_EncryptInit_ex failed') + else: # decrypt + if not EVP_DecryptInit_ex(self.ctx, NULL, NULL, key2, iv2): + raise Exception('EVP_DecryptInit_ex failed') + + @property + def iv(self): + return increment_iv(self.iv_orig[:16], self.blocks) + + def encrypt(self, data): + cdef Py_buffer data_buf = ro_buffer(data) + cdef int inl = len(data) + cdef int ctl = 0 + cdef int outl = 0 + # note: modes that use padding, need up to one extra AES block (16b) + cdef unsigned char *out = malloc(inl+16) + if not out: + raise MemoryError + try: + if not EVP_EncryptUpdate(self.ctx, out, &outl, data_buf.buf, inl): + raise Exception('EVP_EncryptUpdate failed') + ctl = outl + if not EVP_EncryptFinal_ex(self.ctx, out+ctl, &outl): + raise Exception('EVP_EncryptFinal failed') + ctl += outl + self.blocks += num_aes_blocks(ctl) + return out[:ctl] + finally: + free(out) + PyBuffer_Release(&data_buf) + + def decrypt(self, data): + cdef Py_buffer data_buf = ro_buffer(data) + cdef int inl = len(data) + cdef int ptl = 0 + cdef int outl = 0 + # note: modes that use padding, need up to one extra AES block (16b). + # This is what the openssl docs say. I am not sure this is correct, + # but OTOH it will not cause any harm if our buffer is a little bigger. + cdef unsigned char *out = malloc(inl+16) + if not out: + raise MemoryError + try: + if not EVP_DecryptUpdate(self.ctx, out, &outl, data_buf.buf, inl): + raise Exception('EVP_DecryptUpdate failed') + ptl = outl + if EVP_DecryptFinal_ex(self.ctx, out+ptl, &outl) <= 0: + # 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') + ptl += outl + self.blocks += num_aes_blocks(inl) + return out[:ptl] + finally: + free(out) + PyBuffer_Release(&data_buf) + + def hmac_sha256(key, data): cdef Py_buffer data_buf = ro_buffer(data) cdef const unsigned char *key_ptr = key