mirror of
https://github.com/borgbackup/borg.git
synced 2025-01-30 19:21:17 +00:00
make borg build/work on OpenSSL 1.0 and 1.1, fixes #1187
in openssl 1.1, the cipher context is opaque, members can not be accessed directly. we only used this for ctx.iv to determine the current IV (counter value). now, we just remember the original IV, count the AES blocks we process and then compute iv = iv_orig + blocks. that way, it works on OpenSSL 1.0.x and >= 1.1 in the same way.
This commit is contained in:
parent
27c0d0f074
commit
b5362fa5c8
2 changed files with 59 additions and 2 deletions
|
@ -16,7 +16,6 @@ cdef extern from "openssl/evp.h":
|
|||
ctypedef struct EVP_CIPHER:
|
||||
pass
|
||||
ctypedef struct EVP_CIPHER_CTX:
|
||||
unsigned char *iv
|
||||
pass
|
||||
ctypedef struct ENGINE:
|
||||
pass
|
||||
|
@ -40,12 +39,40 @@ import struct
|
|||
|
||||
_int = struct.Struct('>I')
|
||||
_long = struct.Struct('>Q')
|
||||
_2long = struct.Struct('>QQ')
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def bytes16_to_int(b, offset=0):
|
||||
h, l = _2long.unpack_from(b, offset)
|
||||
return (h << 64) + l
|
||||
|
||||
|
||||
def int_to_bytes16(i):
|
||||
max_uint64 = 0xffffffffffffffff
|
||||
l = i & max_uint64
|
||||
h = (i >> 64) & max_uint64
|
||||
return _2long.pack(h, l)
|
||||
|
||||
|
||||
def increment_iv(iv, amount=1):
|
||||
"""
|
||||
Increment the IV by the given amount (default 1).
|
||||
|
||||
:param iv: input IV, 16 bytes (128 bit)
|
||||
:param amount: increment value
|
||||
:return: input_IV + amount, 16 bytes (128 bit)
|
||||
"""
|
||||
assert len(iv) == 16
|
||||
iv = bytes16_to_int(iv)
|
||||
iv += amount
|
||||
iv = int_to_bytes16(iv)
|
||||
return iv
|
||||
|
||||
|
||||
def num_aes_blocks(int length):
|
||||
"""Return the number of AES blocks required to encrypt/decrypt *length* bytes of data.
|
||||
Note: this is only correct for modes without padding, like AES-CTR.
|
||||
|
@ -58,6 +85,8 @@ cdef class AES:
|
|||
"""
|
||||
cdef EVP_CIPHER_CTX *ctx
|
||||
cdef int is_encrypt
|
||||
cdef unsigned char iv_orig[16]
|
||||
cdef int blocks
|
||||
|
||||
def __cinit__(self, is_encrypt, key, iv=None):
|
||||
self.ctx = EVP_CIPHER_CTX_new()
|
||||
|
@ -82,6 +111,10 @@ cdef class AES:
|
|||
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):
|
||||
|
@ -92,7 +125,7 @@ cdef class AES:
|
|||
|
||||
@property
|
||||
def iv(self):
|
||||
return self.ctx.iv[:16]
|
||||
return increment_iv(self.iv_orig[:16], self.blocks)
|
||||
|
||||
def encrypt(self, data):
|
||||
cdef int inl = len(data)
|
||||
|
@ -109,6 +142,7 @@ cdef class AES:
|
|||
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)
|
||||
|
@ -133,6 +167,7 @@ cdef class AES:
|
|||
# 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)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from binascii import hexlify
|
||||
|
||||
from ..crypto import AES, bytes_to_long, bytes_to_int, long_to_bytes
|
||||
from ..crypto import increment_iv, bytes16_to_int, int_to_bytes16
|
||||
from . import BaseTestCase
|
||||
|
||||
|
||||
|
@ -13,6 +14,27 @@ def test_bytes_to_long(self):
|
|||
self.assert_equal(bytes_to_long(b'\0\0\0\0\0\0\0\1'), 1)
|
||||
self.assert_equal(long_to_bytes(1), b'\0\0\0\0\0\0\0\1')
|
||||
|
||||
def test_bytes16_to_int(self):
|
||||
self.assert_equal(bytes16_to_int(b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1'), 1)
|
||||
self.assert_equal(int_to_bytes16(1), b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1')
|
||||
self.assert_equal(bytes16_to_int(b'\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0'), 2 ** 64)
|
||||
self.assert_equal(int_to_bytes16(2 ** 64), b'\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\0')
|
||||
|
||||
def test_increment_iv(self):
|
||||
iv0 = b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'
|
||||
iv1 = b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1'
|
||||
iv2 = b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\2'
|
||||
self.assert_equal(increment_iv(iv0, 0), iv0)
|
||||
self.assert_equal(increment_iv(iv0, 1), iv1)
|
||||
self.assert_equal(increment_iv(iv0, 2), iv2)
|
||||
iva = b'\0\0\0\0\0\0\0\0\xff\xff\xff\xff\xff\xff\xff\xff'
|
||||
ivb = b'\0\0\0\0\0\0\0\1\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
ivc = b'\0\0\0\0\0\0\0\1\x00\x00\x00\x00\x00\x00\x00\x01'
|
||||
self.assert_equal(increment_iv(iva, 0), iva)
|
||||
self.assert_equal(increment_iv(iva, 1), ivb)
|
||||
self.assert_equal(increment_iv(iva, 2), ivc)
|
||||
self.assert_equal(increment_iv(iv0, 2**64), ivb)
|
||||
|
||||
def test_aes(self):
|
||||
key = b'X' * 32
|
||||
data = b'foo' * 10
|
||||
|
|
Loading…
Reference in a new issue