소스 검색

re-add legacy AES() crypto class

we need it to encrypt/decrypt key files / config keys.
Thomas Waldmann 9 년 전
부모
커밋
4effe40415
1개의 변경된 파일98개의 추가작업 그리고 0개의 파일을 삭제
  1. 98 0
      src/borg/crypto/low_level.pyx

+ 98 - 0
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 = <unsigned char *>malloc(inl+16)
+        if not out:
+            raise MemoryError
+        try:
+            if not EVP_EncryptUpdate(self.ctx, out, &outl, <const unsigned char*> 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 = <unsigned char *>malloc(inl+16)
+        if not out:
+            raise MemoryError
+        try:
+            if not EVP_DecryptUpdate(self.ctx, out, &outl, <const unsigned char*> 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