From d3f069cb3bc39bfcfba157bb43af90193b4f7ea1 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 17 Mar 2022 20:40:36 +0100 Subject: [PATCH 01/34] crypto: fix/update borg version comments new AEAD crypto can be used with borg >= 1.3. old crypto is used by attic and borg < 1.3. --- src/borg/crypto/low_level.pyx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index b43d4a79fa..495845e07a 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -184,7 +184,7 @@ class UNENCRYPTED: cdef class AES256_CTR_BASE: - # Layout: HEADER + MAC 32 + IV 8 + CT (same as attic / borg < 1.2 IF HEADER = TYPE_BYTE, no AAD) + # Layout: HEADER + MAC 32 + IV 8 + CT (same as attic / borg < 1.3 IF HEADER = TYPE_BYTE, no AAD) cdef EVP_CIPHER_CTX *ctx cdef unsigned char *enc_key @@ -423,6 +423,7 @@ ctypedef const EVP_CIPHER * (* CIPHER)() cdef class _AEAD_BASE: + # new crypto used in borg >= 1.3 # Layout: HEADER + MAC 16 + IV 12 + CT cdef CIPHER cipher From aff626119aa7ee8335539de106a3597c4edb09ec Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 17 Mar 2022 20:50:37 +0100 Subject: [PATCH 02/34] crypto: cleanup, remove references to AES-GCM A lot of people have concerns about AES-GCM. Considering we can use AES-OCB, I guess we will not use AES-GCM anyway, thus no need to talk about it. --- src/borg/crypto/low_level.pyx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 495845e07a..ca6957412f 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -77,9 +77,9 @@ cdef extern from "openssl/evp.h": int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl) int EVP_CIPHER_CTX_ctrl(EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr) - int EVP_CTRL_GCM_GET_TAG - int EVP_CTRL_GCM_SET_TAG - int EVP_CTRL_GCM_SET_IVLEN + int EVP_CTRL_AEAD_GET_TAG + int EVP_CTRL_AEAD_SET_TAG + int EVP_CTRL_AEAD_SET_IVLEN const EVP_MD *EVP_sha256() nogil @@ -471,7 +471,7 @@ cdef class _AEAD_BASE: if iv is not None: self.set_iv(iv) assert self.blocks == 0, 'iv needs to be set before encrypt is called' - # AES-GCM, AES-OCB, CHACHA20 ciphers all add a internal 32bit counter to the 96bit (12Byte) + # AES-OCB, CHACHA20 ciphers all add a internal 32bit counter to the 96bit (12Byte) # 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: @@ -499,7 +499,7 @@ cdef class _AEAD_BASE: rc = EVP_EncryptInit_ex(self.ctx, self.cipher(), NULL, NULL, NULL) if not rc: raise CryptoError('EVP_EncryptInit_ex failed') - if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_GCM_SET_IVLEN, self.iv_len, NULL): + if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_IVLEN, self.iv_len, NULL): raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed') rc = EVP_EncryptInit_ex(self.ctx, NULL, NULL, self.enc_key, self.iv) if not rc: @@ -518,7 +518,7 @@ cdef class _AEAD_BASE: if not rc: raise CryptoError('EVP_EncryptFinal_ex failed') offset += olen - if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_GCM_GET_TAG, self.mac_len, odata+hlen): + if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_GET_TAG, self.mac_len, odata + hlen): raise CryptoError('EVP_CIPHER_CTX_ctrl GET TAG failed') self.blocks = block_count return odata[:offset] @@ -531,7 +531,7 @@ cdef class _AEAD_BASE: """ authenticate aad + iv + cdata, decrypt cdata, ignore header bytes up to aad_offset. """ - # AES-GCM, AES-OCB, CHACHA20 ciphers all add a internal 32bit counter to the 96bit (12Byte) + # AES-OCB, CHACHA20 ciphers all add a internal 32bit counter to the 96bit (12Byte) # 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: @@ -552,11 +552,11 @@ cdef class _AEAD_BASE: raise CryptoError('EVP_DecryptInit_ex failed') iv = self.fetch_iv( idata.buf+hlen+self.mac_len) self.set_iv(iv) - if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_GCM_SET_IVLEN, self.iv_len, NULL): + if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_IVLEN, self.iv_len, NULL): raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed') if not EVP_DecryptInit_ex(self.ctx, NULL, NULL, self.enc_key, iv): raise CryptoError('EVP_DecryptInit_ex failed') - if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_GCM_SET_TAG, self.mac_len, idata.buf+hlen): + if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_TAG, self.mac_len, idata.buf + hlen): raise CryptoError('EVP_CIPHER_CTX_ctrl SET TAG failed') rc = EVP_DecryptUpdate(self.ctx, NULL, &olen, idata.buf+aoffset, alen) if not rc: @@ -597,7 +597,7 @@ cdef class _AEAD_BASE: def next_iv(self): # call this after encrypt() to get the next iv (int) for the next encrypt() call - # AES-GCM, AES-OCB, CHACHA20 ciphers all add a internal 32bit counter to the 96bit + # AES-OCB, CHACHA20 ciphers all add a internal 32bit counter to the 96bit # (12 byte) IV we provide, thus we only need to increment the IV by 1. iv = int.from_bytes(self.iv[:self.iv_len], byteorder='big') return iv + 1 From e647360a0ec5c08d6fdfeaaa93b99dcba368bc70 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 18 Mar 2022 20:24:48 +0100 Subject: [PATCH 03/34] crypto: better raise NotImplementedError if we have no id_hash --- src/borg/crypto/key.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index 3747995718..89674e5eda 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -156,6 +156,7 @@ def __init__(self, repository): def id_hash(self, data): """Return HMAC hash using the "id" HMAC key """ + raise NotImplementedError def encrypt(self, chunk): pass From 57479fb989fc4c13c6b87a782a581bb4031e881c Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 17 Mar 2022 22:30:40 +0100 Subject: [PATCH 04/34] crypto: put the IV into the header, at the end of it one openssl call less due to simpler layout! also change default for aad_offset to 0: by default, we want to authenticate the complete header. --- src/borg/crypto/low_level.pyx | 56 +++++++++++++++++++---------------- src/borg/testsuite/crypto.py | 12 ++++---- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index ca6957412f..c3d81952b7 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -424,7 +424,7 @@ ctypedef const EVP_CIPHER * (* CIPHER)() cdef class _AEAD_BASE: # new crypto used in borg >= 1.3 - # Layout: HEADER + MAC 16 + IV 12 + CT + # Layout: HEADER + MAC 16 + CT (IV will be put into the header, at the end) cdef CIPHER cipher cdef EVP_CIPHER_CTX *ctx @@ -432,7 +432,7 @@ cdef class _AEAD_BASE: cdef int cipher_blk_len cdef int iv_len cdef int aad_offset - cdef int header_len + cdef int header_len # includes the IV at the end cdef int mac_len cdef unsigned char iv[12] cdef long long blocks @@ -442,14 +442,22 @@ cdef class _AEAD_BASE: """check whether library requirements for this ciphersuite are satisfied""" raise NotImplemented # override / implement in child class - def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=1): + def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=0): + """ + init AEAD crypto + + :param mac_key: must be None + :param enc_key: 256bit encrypt-then-mac key + :param iv: 96bit initialisation vector / nonce + :param header_len: length of header *without* IV + :param aad_offset: where in the header the authenticated data starts + """ assert mac_key is None assert isinstance(enc_key, bytes) and len(enc_key) == 32 self.iv_len = sizeof(self.iv) - self.header_len = 1 + self.header_len = header_len + self.iv_len assert aad_offset <= header_len self.aad_offset = aad_offset - self.header_len = header_len self.mac_len = 16 self.enc_key = enc_key if iv is not None: @@ -457,7 +465,7 @@ cdef class _AEAD_BASE: else: self.blocks = -1 # make sure set_iv is called before encrypt - def __cinit__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=1): + def __cinit__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=0): self.ctx = EVP_CIPHER_CTX_new() def __dealloc__(self): @@ -465,7 +473,7 @@ cdef class _AEAD_BASE: def encrypt(self, data, header=b'', iv=None): """ - encrypt data, compute mac over aad + iv + cdata, prepend header. + encrypt data, compute mac over aad + cdata, prepend header. aad_offset is the offset into the header where aad starts. """ if iv is not None: @@ -477,11 +485,12 @@ cdef class _AEAD_BASE: if block_count > 2**32: raise ValueError('too much data, would overflow internal 32bit counter') cdef int ilen = len(data) - cdef int hlen = len(header) + cdef int hl = len(header) + cdef int hlen = hl + self.iv_len assert hlen == self.header_len cdef int aoffset = self.aad_offset cdef int alen = hlen - aoffset - cdef unsigned char *odata = PyMem_Malloc(hlen + self.mac_len + self.iv_len + + cdef unsigned char *odata = PyMem_Malloc(hlen + self.mac_len + ilen + self.cipher_blk_len) if not odata: raise MemoryError @@ -491,11 +500,12 @@ cdef class _AEAD_BASE: cdef Py_buffer hdata = ro_buffer(header) try: offset = 0 - for i in range(hlen): + for i in range(hl): odata[offset+i] = header[i] - offset += hlen - offset += self.mac_len + offset = hl self.store_iv(odata+offset, self.iv) + offset = hlen + offset += self.mac_len rc = EVP_EncryptInit_ex(self.ctx, self.cipher(), NULL, NULL, NULL) if not rc: raise CryptoError('EVP_EncryptInit_ex failed') @@ -507,9 +517,6 @@ cdef class _AEAD_BASE: rc = EVP_EncryptUpdate(self.ctx, NULL, &olen, hdata.buf+aoffset, alen) if not rc: raise CryptoError('EVP_EncryptUpdate failed') - if not EVP_EncryptUpdate(self.ctx, NULL, &olen, odata+offset, self.iv_len): - raise CryptoError('EVP_EncryptUpdate failed') - offset += self.iv_len rc = EVP_EncryptUpdate(self.ctx, odata+offset, &olen, idata.buf, ilen) if not rc: raise CryptoError('EVP_EncryptUpdate failed') @@ -529,7 +536,7 @@ cdef class _AEAD_BASE: def decrypt(self, envelope): """ - authenticate aad + iv + cdata, decrypt cdata, ignore header bytes up to aad_offset. + authenticate aad + cdata, decrypt cdata, ignore header bytes up to aad_offset. """ # AES-OCB, CHACHA20 ciphers all add a internal 32bit counter to the 96bit (12Byte) # IV we provide, thus we must not decrypt more than 2^32 cipher blocks with same IV): @@ -538,7 +545,7 @@ cdef class _AEAD_BASE: raise ValueError('too much data, would overflow internal 32bit counter') cdef int ilen = len(envelope) cdef int hlen = self.header_len - assert hlen == self.header_len + cdef int hl = hlen - self.iv_len cdef int aoffset = self.aad_offset cdef int alen = hlen - aoffset cdef unsigned char *odata = PyMem_Malloc(ilen + self.cipher_blk_len) @@ -550,7 +557,7 @@ cdef class _AEAD_BASE: try: if not EVP_DecryptInit_ex(self.ctx, self.cipher(), NULL, NULL, NULL): raise CryptoError('EVP_DecryptInit_ex failed') - iv = self.fetch_iv( idata.buf+hlen+self.mac_len) + iv = self.fetch_iv( idata.buf+hl) self.set_iv(iv) if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_IVLEN, self.iv_len, NULL): raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed') @@ -561,13 +568,10 @@ cdef class _AEAD_BASE: rc = EVP_DecryptUpdate(self.ctx, NULL, &olen, idata.buf+aoffset, alen) if not rc: raise CryptoError('EVP_DecryptUpdate failed') - if not EVP_DecryptUpdate(self.ctx, NULL, &olen, - idata.buf+hlen+self.mac_len, self.iv_len): - raise CryptoError('EVP_DecryptUpdate failed') offset = 0 rc = EVP_DecryptUpdate(self.ctx, odata+offset, &olen, - idata.buf+hlen+self.mac_len+self.iv_len, - ilen-hlen-self.mac_len-self.iv_len) + idata.buf+hlen+self.mac_len, + ilen-hlen-self.mac_len) if not rc: raise CryptoError('EVP_DecryptUpdate failed') offset += olen @@ -611,7 +615,7 @@ cdef class _AEAD_BASE: iv_out[i] = iv[i] def extract_iv(self, envelope): - offset = self.header_len + self.mac_len + offset = self.header_len - self.iv_len return bytes_to_long(envelope[offset:offset+self.iv_len]) @@ -633,7 +637,7 @@ cdef class AES256_OCB(_AES_BASE): if is_libressl: raise ValueError('AES OCB is not implemented by LibreSSL (yet?).') - def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=1): + def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=0): self.requirements_check() self.cipher = EVP_aes_256_ocb super().__init__(mac_key, enc_key, iv=iv, header_len=header_len, aad_offset=aad_offset) @@ -645,7 +649,7 @@ cdef class CHACHA20_POLY1305(_CHACHA_BASE): if is_libressl: raise ValueError('CHACHA20-POLY1305 is not implemented by LibreSSL (yet?).') - def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=1): + def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=0): self.requirements_check() self.cipher = EVP_chacha20_poly1305 super().__init__(mac_key, enc_key, iv=iv, header_len=header_len, aad_offset=aad_offset) diff --git a/src/borg/testsuite/crypto.py b/src/borg/testsuite/crypto.py index 6420f36c88..d68fc549ea 100644 --- a/src/borg/testsuite/crypto.py +++ b/src/borg/testsuite/crypto.py @@ -111,11 +111,11 @@ def test_AE(self): for cs_cls, exp_mac, exp_cdata in tests: # print(repr(cs_cls)) # encrypt/mac - cs = cs_cls(mac_key, enc_key, iv, header_len=1, aad_offset=1) + cs = cs_cls(mac_key, enc_key, iv, header_len=len(header), aad_offset=1) hdr_mac_iv_cdata = cs.encrypt(data, header=header) hdr = hdr_mac_iv_cdata[0:1] - mac = hdr_mac_iv_cdata[1:17] - iv = hdr_mac_iv_cdata[17:29] + iv = hdr_mac_iv_cdata[1:13] + mac = hdr_mac_iv_cdata[13:29] cdata = hdr_mac_iv_cdata[29:] self.assert_equal(hexlify(hdr), b'23') self.assert_equal(hexlify(mac), exp_mac) @@ -155,11 +155,11 @@ def test_AEAD(self): for cs_cls, exp_mac, exp_cdata in tests: # print(repr(cs_cls)) # encrypt/mac - cs = cs_cls(mac_key, enc_key, iv, header_len=3, aad_offset=1) + cs = cs_cls(mac_key, enc_key, iv, header_len=len(header), aad_offset=1) hdr_mac_iv_cdata = cs.encrypt(data, header=header) hdr = hdr_mac_iv_cdata[0:3] - mac = hdr_mac_iv_cdata[3:19] - iv = hdr_mac_iv_cdata[19:31] + iv = hdr_mac_iv_cdata[3:15] + mac = hdr_mac_iv_cdata[15:31] cdata = hdr_mac_iv_cdata[31:] self.assert_equal(hexlify(hdr), b'123456') self.assert_equal(hexlify(mac), exp_mac) From 3473b17a8db08a244efe59398cfa28094c75ca25 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 17 Mar 2022 22:41:14 +0100 Subject: [PATCH 05/34] crypto: improve attr naming --- src/borg/crypto/low_level.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index c3d81952b7..08c51c98c2 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -432,7 +432,7 @@ cdef class _AEAD_BASE: cdef int cipher_blk_len cdef int iv_len cdef int aad_offset - cdef int header_len # includes the IV at the end + cdef int header_len_expected # includes the IV at the end cdef int mac_len cdef unsigned char iv[12] cdef long long blocks @@ -455,7 +455,7 @@ cdef class _AEAD_BASE: assert mac_key is None assert isinstance(enc_key, bytes) and len(enc_key) == 32 self.iv_len = sizeof(self.iv) - self.header_len = header_len + self.iv_len + self.header_len_expected = header_len + self.iv_len assert aad_offset <= header_len self.aad_offset = aad_offset self.mac_len = 16 @@ -487,7 +487,7 @@ cdef class _AEAD_BASE: cdef int ilen = len(data) cdef int hl = len(header) cdef int hlen = hl + self.iv_len - assert hlen == self.header_len + assert hlen == self.header_len_expected cdef int aoffset = self.aad_offset cdef int alen = hlen - aoffset cdef unsigned char *odata = PyMem_Malloc(hlen + self.mac_len + @@ -544,7 +544,7 @@ cdef class _AEAD_BASE: if approx_block_count > 2**32: raise ValueError('too much data, would overflow internal 32bit counter') cdef int ilen = len(envelope) - cdef int hlen = self.header_len + cdef int hlen = self.header_len_expected cdef int hl = hlen - self.iv_len cdef int aoffset = self.aad_offset cdef int alen = hlen - aoffset @@ -615,7 +615,7 @@ cdef class _AEAD_BASE: iv_out[i] = iv[i] def extract_iv(self, envelope): - offset = self.header_len - self.iv_len + offset = self.header_len_expected - self.iv_len return bytes_to_long(envelope[offset:offset+self.iv_len]) From 96332736225c3a77ac7e78e554787a7a6c41f0dd Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 17 Mar 2022 23:09:27 +0100 Subject: [PATCH 06/34] crypto: simplify api for new crypto, AEAD only needs 1 key --- src/borg/crypto/low_level.pyx | 28 +++++++++++++--------------- src/borg/testsuite/crypto.py | 18 ++++++++---------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 08c51c98c2..93dec0be3b 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -428,7 +428,7 @@ cdef class _AEAD_BASE: cdef CIPHER cipher cdef EVP_CIPHER_CTX *ctx - cdef unsigned char *enc_key + cdef unsigned char *key cdef int cipher_blk_len cdef int iv_len cdef int aad_offset @@ -442,30 +442,28 @@ cdef class _AEAD_BASE: """check whether library requirements for this ciphersuite are satisfied""" raise NotImplemented # override / implement in child class - def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=0): + def __init__(self, key, iv=None, header_len=1, aad_offset=0): """ init AEAD crypto - :param mac_key: must be None - :param enc_key: 256bit encrypt-then-mac key + :param key: 256bit encrypt-then-mac key :param iv: 96bit initialisation vector / nonce - :param header_len: length of header *without* IV + :param header_len: expected length of header *without* IV :param aad_offset: where in the header the authenticated data starts """ - assert mac_key is None - assert isinstance(enc_key, bytes) and len(enc_key) == 32 + assert isinstance(key, bytes) and len(key) == 32 self.iv_len = sizeof(self.iv) self.header_len_expected = header_len + self.iv_len assert aad_offset <= header_len self.aad_offset = aad_offset self.mac_len = 16 - self.enc_key = enc_key + self.key = key if iv is not None: self.set_iv(iv) else: self.blocks = -1 # make sure set_iv is called before encrypt - def __cinit__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=0): + def __cinit__(self, key, iv=None, header_len=1, aad_offset=0): self.ctx = EVP_CIPHER_CTX_new() def __dealloc__(self): @@ -511,7 +509,7 @@ cdef class _AEAD_BASE: raise CryptoError('EVP_EncryptInit_ex failed') if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_IVLEN, self.iv_len, NULL): raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed') - rc = EVP_EncryptInit_ex(self.ctx, NULL, NULL, self.enc_key, self.iv) + rc = EVP_EncryptInit_ex(self.ctx, NULL, NULL, self.key, self.iv) if not rc: raise CryptoError('EVP_EncryptInit_ex failed') rc = EVP_EncryptUpdate(self.ctx, NULL, &olen, hdata.buf+aoffset, alen) @@ -561,7 +559,7 @@ cdef class _AEAD_BASE: self.set_iv(iv) if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_IVLEN, self.iv_len, NULL): raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed') - if not EVP_DecryptInit_ex(self.ctx, NULL, NULL, self.enc_key, iv): + if not EVP_DecryptInit_ex(self.ctx, NULL, NULL, self.key, iv): raise CryptoError('EVP_DecryptInit_ex failed') if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_TAG, self.mac_len, idata.buf + hlen): raise CryptoError('EVP_CIPHER_CTX_ctrl SET TAG failed') @@ -637,10 +635,10 @@ cdef class AES256_OCB(_AES_BASE): if is_libressl: raise ValueError('AES OCB is not implemented by LibreSSL (yet?).') - def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=0): + def __init__(self, key, iv=None, header_len=1, aad_offset=0): self.requirements_check() self.cipher = EVP_aes_256_ocb - super().__init__(mac_key, enc_key, iv=iv, header_len=header_len, aad_offset=aad_offset) + super().__init__(key, iv=iv, header_len=header_len, aad_offset=aad_offset) cdef class CHACHA20_POLY1305(_CHACHA_BASE): @@ -649,10 +647,10 @@ cdef class CHACHA20_POLY1305(_CHACHA_BASE): if is_libressl: raise ValueError('CHACHA20-POLY1305 is not implemented by LibreSSL (yet?).') - def __init__(self, mac_key, enc_key, iv=None, header_len=1, aad_offset=0): + def __init__(self, key, iv=None, header_len=1, aad_offset=0): self.requirements_check() self.cipher = EVP_chacha20_poly1305 - super().__init__(mac_key, enc_key, iv=iv, header_len=header_len, aad_offset=aad_offset) + super().__init__(key, iv=iv, header_len=header_len, aad_offset=aad_offset) cdef class AES: diff --git a/src/borg/testsuite/crypto.py b/src/borg/testsuite/crypto.py index d68fc549ea..0980cbfc53 100644 --- a/src/borg/testsuite/crypto.py +++ b/src/borg/testsuite/crypto.py @@ -91,8 +91,7 @@ def test_AES256_CTR_HMAC_SHA256_aad(self): def test_AE(self): # used in legacy-like layout (1 type byte, no aad) - mac_key = None - enc_key = b'X' * 32 + key = b'X' * 32 iv = 0 data = b'foo' * 10 header = b'\x23' @@ -111,7 +110,7 @@ def test_AE(self): for cs_cls, exp_mac, exp_cdata in tests: # print(repr(cs_cls)) # encrypt/mac - cs = cs_cls(mac_key, enc_key, iv, header_len=len(header), aad_offset=1) + cs = cs_cls(key, iv, header_len=len(header), aad_offset=1) hdr_mac_iv_cdata = cs.encrypt(data, header=header) hdr = hdr_mac_iv_cdata[0:1] iv = hdr_mac_iv_cdata[1:13] @@ -123,20 +122,19 @@ def test_AE(self): self.assert_equal(hexlify(cdata), exp_cdata) self.assert_equal(cs.next_iv(), 1) # auth/decrypt - cs = cs_cls(mac_key, enc_key, header_len=len(header), aad_offset=1) + cs = cs_cls(key, header_len=len(header), aad_offset=1) pdata = cs.decrypt(hdr_mac_iv_cdata) self.assert_equal(data, pdata) self.assert_equal(cs.next_iv(), 1) # auth-failure due to corruption (corrupted data) - cs = cs_cls(mac_key, enc_key, header_len=len(header), aad_offset=1) + cs = cs_cls(key, header_len=len(header), aad_offset=1) hdr_mac_iv_cdata_corrupted = hdr_mac_iv_cdata[:29] + b'\0' + hdr_mac_iv_cdata[30:] self.assert_raises(IntegrityError, lambda: cs.decrypt(hdr_mac_iv_cdata_corrupted)) def test_AEAD(self): # test with aad - mac_key = None - enc_key = b'X' * 32 + key = b'X' * 32 iv = 0 data = b'foo' * 10 header = b'\x12\x34\x56' @@ -155,7 +153,7 @@ def test_AEAD(self): for cs_cls, exp_mac, exp_cdata in tests: # print(repr(cs_cls)) # encrypt/mac - cs = cs_cls(mac_key, enc_key, iv, header_len=len(header), aad_offset=1) + cs = cs_cls(key, iv, header_len=len(header), aad_offset=1) hdr_mac_iv_cdata = cs.encrypt(data, header=header) hdr = hdr_mac_iv_cdata[0:3] iv = hdr_mac_iv_cdata[3:15] @@ -167,12 +165,12 @@ def test_AEAD(self): self.assert_equal(hexlify(cdata), exp_cdata) self.assert_equal(cs.next_iv(), 1) # auth/decrypt - cs = cs_cls(mac_key, enc_key, header_len=len(header), aad_offset=1) + cs = cs_cls(key, header_len=len(header), aad_offset=1) pdata = cs.decrypt(hdr_mac_iv_cdata) self.assert_equal(data, pdata) self.assert_equal(cs.next_iv(), 1) # auth-failure due to corruption (corrupted aad) - cs = cs_cls(mac_key, enc_key, header_len=len(header), aad_offset=1) + cs = cs_cls(key, header_len=len(header), aad_offset=1) hdr_mac_iv_cdata_corrupted = hdr_mac_iv_cdata[:1] + b'\0' + hdr_mac_iv_cdata[2:] self.assert_raises(IntegrityError, lambda: cs.decrypt(hdr_mac_iv_cdata_corrupted)) From 0f6f278b0fa5dbdf66fca1268163f3fa1d5d08e9 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 18 Mar 2022 01:46:56 +0100 Subject: [PATCH 07/34] crypto: AEAD key classes also: cleanup class structure: less inheritance, more mixins. define type bytes using the 4:4 split upper 4 bits are ciphersuite: 0 == legacy AES-CTR based stuff 1+ == new AEAD stuff lower 4 bits are keytype: legacy: a bit mixed up, as it was... new stuff: 0=keyfile 1=repokey, ... --- src/borg/constants.py | 12 +++ src/borg/crypto/key.py | 168 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 164 insertions(+), 16 deletions(-) diff --git a/src/borg/constants.py b/src/borg/constants.py index 6b9544a311..5fb6b9a65a 100644 --- a/src/borg/constants.py +++ b/src/borg/constants.py @@ -111,6 +111,8 @@ class KeyBlobStorage: class KeyType: + # legacy crypto + # upper 4 bits are ciphersuite, 0 == legacy AES-CTR KEYFILE = 0x00 # repos with PASSPHRASE mode could not be created any more since borg 1.0, see #97. # in borg 1.3 all of its code and also the "borg key migrate-to-repokey" command was removed. @@ -123,6 +125,16 @@ class KeyType: BLAKE2REPO = 0x05 BLAKE2AUTHENTICATED = 0x06 AUTHENTICATED = 0x07 + # new crypto + # upper 4 bits are ciphersuite, lower 4 bits are keytype + AESOCBKEYFILE = 0x10 + AESOCBREPO = 0x11 + CHPOKEYFILE = 0x20 + CHPOREPO = 0x21 + BLAKE2AESOCBKEYFILE = 0x30 + BLAKE2AESOCBREPO = 0x31 + BLAKE2CHPOKEYFILE = 0x40 + BLAKE2CHPOREPO = 0x41 REPOSITORY_README = """This is a Borg Backup repository. diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index 89674e5eda..f17ed5b74c 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -23,7 +23,7 @@ from .nonces import NonceManager from .low_level import AES, bytes_to_long, long_to_bytes, bytes_to_int, num_cipher_blocks, hmac_sha256, blake2b_256, hkdf_hmac_sha512 -from .low_level import AES256_CTR_HMAC_SHA256, AES256_CTR_BLAKE2b +from .low_level import AES256_CTR_HMAC_SHA256, AES256_CTR_BLAKE2b, AES256_OCB, CHACHA20_POLY1305 class UnsupportedPayloadError(Error): @@ -336,7 +336,7 @@ class AESKeyBase(KeyBase): PAYLOAD_OVERHEAD = 1 + 32 + 8 # TYPE + HMAC + NONCE - CIPHERSUITE = AES256_CTR_HMAC_SHA256 + CIPHERSUITE = None # override in derived class logically_encrypted = True @@ -383,7 +383,7 @@ def init_ciphers(self, manifest_data=None): self.nonce_manager = NonceManager(self.repository, nonce) -class FlexiKeyBase(AESKeyBase): +class FlexiKeyBase: @classmethod def detect(cls, repository, manifest_data): key = cls(repository) @@ -496,9 +496,7 @@ def get_new_target(self, args): raise NotImplementedError -class FlexiKey(ID_HMAC_SHA_256, FlexiKeyBase): - TYPES_ACCEPTABLE = {KeyType.KEYFILE, KeyType.REPO, KeyType.PASSPHRASE} - +class FlexiKey(FlexiKeyBase): FILE_ID = 'BORG_KEY' def sanity_check(self, filename, id): @@ -625,40 +623,43 @@ def remove(self, target): raise TypeError('Unsupported borg key storage type') -class KeyfileKey(FlexiKey): +class KeyfileKey(ID_HMAC_SHA_256, AESKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.KEYFILE, KeyType.REPO, KeyType.PASSPHRASE} TYPE = KeyType.KEYFILE NAME = 'key file' ARG_NAME = 'keyfile' STORAGE = KeyBlobStorage.KEYFILE + CIPHERSUITE = AES256_CTR_HMAC_SHA256 -class RepoKey(FlexiKey): +class RepoKey(ID_HMAC_SHA_256, AESKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.KEYFILE, KeyType.REPO, KeyType.PASSPHRASE} TYPE = KeyType.REPO NAME = 'repokey' ARG_NAME = 'repokey' STORAGE = KeyBlobStorage.REPO + CIPHERSUITE = AES256_CTR_HMAC_SHA256 -class Blake2FlexiKey(ID_BLAKE2b_256, FlexiKey): +class Blake2KeyfileKey(ID_BLAKE2b_256, AESKeyBase, FlexiKey): TYPES_ACCEPTABLE = {KeyType.BLAKE2KEYFILE, KeyType.BLAKE2REPO} - CIPHERSUITE = AES256_CTR_BLAKE2b - - -class Blake2KeyfileKey(Blake2FlexiKey): TYPE = KeyType.BLAKE2KEYFILE NAME = 'key file BLAKE2b' ARG_NAME = 'keyfile-blake2' STORAGE = KeyBlobStorage.KEYFILE + CIPHERSUITE = AES256_CTR_BLAKE2b -class Blake2RepoKey(Blake2FlexiKey): +class Blake2RepoKey(ID_BLAKE2b_256, AESKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.BLAKE2KEYFILE, KeyType.BLAKE2REPO} TYPE = KeyType.BLAKE2REPO NAME = 'repokey BLAKE2b' ARG_NAME = 'repokey-blake2' STORAGE = KeyBlobStorage.REPO + CIPHERSUITE = AES256_CTR_BLAKE2b -class AuthenticatedKeyBase(FlexiKey): +class AuthenticatedKeyBase(AESKeyBase, FlexiKey): STORAGE = KeyBlobStorage.REPO # It's only authenticated, not encrypted. @@ -691,7 +692,7 @@ def decrypt(self, id, data, decompress=True): return data -class AuthenticatedKey(AuthenticatedKeyBase): +class AuthenticatedKey(ID_HMAC_SHA_256, AuthenticatedKeyBase): TYPE = KeyType.AUTHENTICATED TYPES_ACCEPTABLE = {TYPE} NAME = 'authenticated' @@ -705,8 +706,143 @@ class Blake2AuthenticatedKey(ID_BLAKE2b_256, AuthenticatedKeyBase): ARG_NAME = 'authenticated-blake2' +# ------------ new crypto ------------ + + +class AEADKeyBase(KeyBase): + """ + Chunks are encrypted and authenticated using some AEAD ciphersuite + + Payload layout: TYPE(1) + SESSIONID(24) + NONCE(12) + MAC(16) + CIPHERTEXT + ^------------- AAD ---------------^ + """ + + PAYLOAD_OVERHEAD = 1 + 24 + 12 + 16 # TYPE + SESSIONID + NONCE + MAC + + CIPHERSUITE = None # override in subclass + + logically_encrypted = True + + def encrypt(self, chunk): + data = self.compressor.compress(chunk) + header = self.TYPE_STR + self.sessionid + iv = self.cipher.next_iv() + return self.cipher.encrypt(data, header=header, iv=iv) + + def decrypt(self, id, data, decompress=True): + self.assert_type(data[0], id) + sessionid = data[1:13] # XXX + self.init_ciphers(salt=salt, context=context, iv=iv) # XXX + try: + payload = self.cipher.decrypt(data) + except IntegrityError as e: + raise IntegrityError(f"Chunk {bin_to_hex(id)}: Could not decrypt [{str(e)}]") + if not decompress: + return payload + data = self.decompress(payload) + self.assert_id(id, data) + return data + + def init_from_random_data(self): + data = os.urandom(100) + self.enc_key = data[0:32] + self.enc_hmac_key = data[32:64] + self.id_key = data[64:96] + self.chunk_seed = bytes_to_int(data[96:100]) + # Convert to signed int32 + if self.chunk_seed & 0x80000000: + self.chunk_seed = self.chunk_seed - 0xffffffff - 1 + + def init_ciphers(self, salt=b'', context=b'', iv=0): + key = hkdf_hmac_sha512( + ikm=self.enc_key + self.enc_hmac_key, + salt=salt, + info=b'borg-crypto-' + context, # XXX + output_length=32 + ) + self.cipher = self.CIPHERSUITE(key=key, header_len=1+24, aad_offset=0) # XXX + self.cipher.set_iv(iv) + + +class AESOCBKeyfileKey(ID_HMAC_SHA_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.AESOCBKEYFILE, KeyType.AESOCBREPO} + TYPE = KeyType.AESOCBKEYFILE + NAME = 'key file AES-OCB' + ARG_NAME = 'keyfile-aes-ocb' + STORAGE = KeyBlobStorage.KEYFILE + CIPHERSUITE = AES256_OCB + + +class AESOCBRepoKey(ID_HMAC_SHA_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.AESOCBKEYFILE, KeyType.AESOCBREPO} + TYPE = KeyType.AESOCBREPO + NAME = 'repokey AES-OCB' + ARG_NAME = 'repokey-aes-ocb' + STORAGE = KeyBlobStorage.REPO + CIPHERSUITE = AES256_OCB + + +class CHPOKeyfileKey(ID_HMAC_SHA_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.CHPOKEYFILE, KeyType.CHPOREPO} + TYPE = KeyType.CHPOKEYFILE + NAME = 'key file ChaCha20-Poly1305' + ARG_NAME = 'keyfile-chacha20-poly1305' + STORAGE = KeyBlobStorage.KEYFILE + CIPHERSUITE = CHACHA20_POLY1305 + + +class CHPORepoKey(ID_HMAC_SHA_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.CHPOKEYFILE, KeyType.CHPOREPO} + TYPE = KeyType.CHPOREPO + NAME = 'repokey ChaCha20-Poly1305' + ARG_NAME = 'repokey-chacha20-poly1305' + STORAGE = KeyBlobStorage.REPO + CIPHERSUITE = CHACHA20_POLY1305 + + +class Blake2AESOCBKeyfileKey(ID_BLAKE2b_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.BLAKE2AESOCBKEYFILE, KeyType.BLAKE2AESOCBREPO} + TYPE = KeyType.BLAKE2AESOCBKEYFILE + NAME = 'key file Blake2b AES-OCB' + ARG_NAME = 'keyfile-blake2-aes-ocb' + STORAGE = KeyBlobStorage.KEYFILE + CIPHERSUITE = AES256_OCB + + +class Blake2AESOCBRepoKey(ID_BLAKE2b_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.BLAKE2AESOCBKEYFILE, KeyType.BLAKE2AESOCBREPO} + TYPE = KeyType.BLAKE2AESOCBREPO + NAME = 'repokey Blake2b AES-OCB' + ARG_NAME = 'repokey-blake2-aes-ocb' + STORAGE = KeyBlobStorage.REPO + CIPHERSUITE = AES256_OCB + + +class Blake2CHPOKeyfileKey(ID_BLAKE2b_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.BLAKE2CHPOKEYFILE, KeyType.BLAKE2CHPOREPO} + TYPE = KeyType.BLAKE2CHPOKEYFILE + NAME = 'key file Blake2b ChaCha20-Poly1305' + ARG_NAME = 'keyfile-blake2-chacha20-poly1305' + STORAGE = KeyBlobStorage.KEYFILE + CIPHERSUITE = CHACHA20_POLY1305 + + +class Blake2CHPORepoKey(ID_BLAKE2b_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.BLAKE2CHPOKEYFILE, KeyType.BLAKE2CHPOREPO} + TYPE = KeyType.BLAKE2CHPOREPO + NAME = 'repokey Blake2b ChaCha20-Poly1305' + ARG_NAME = 'repokey-blake2-chacha20-poly1305' + STORAGE = KeyBlobStorage.REPO + CIPHERSUITE = CHACHA20_POLY1305 + + AVAILABLE_KEY_TYPES = ( PlaintextKey, KeyfileKey, RepoKey, AuthenticatedKey, Blake2KeyfileKey, Blake2RepoKey, Blake2AuthenticatedKey, + # new crypto + AESOCBKeyfileKey, AESOCBRepoKey, + CHPOKeyfileKey, CHPORepoKey, + Blake2AESOCBKeyfileKey, Blake2AESOCBRepoKey, + Blake2CHPOKeyfileKey, Blake2CHPORepoKey, ) From 5c66fa4caa71434b78ea9b09de0d16df465454dd Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 18 Mar 2022 19:00:58 +0100 Subject: [PATCH 08/34] crypto: layout updates, low-level does not deal with IV encrypt used to "patch" the IV into the header, decrypt used to fetch it from there. encrypt now takes the header just "as is" and also decrypt expects that the IV is already set. --- src/borg/crypto/key.py | 50 ++++++++++++++++++++++++++--------- src/borg/crypto/low_level.pyx | 34 ++++++------------------ src/borg/testsuite/crypto.py | 4 +-- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index f17ed5b74c..b8e2407663 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -713,28 +713,43 @@ class AEADKeyBase(KeyBase): """ Chunks are encrypted and authenticated using some AEAD ciphersuite - Payload layout: TYPE(1) + SESSIONID(24) + NONCE(12) + MAC(16) + CIPHERTEXT - ^------------- AAD ---------------^ + Layout: suite:4 keytype:4 reserved:8 messageIV:48 sessionID:192 auth_tag:128 payload:... [bits] + ^-------------------- AAD ----------------------------^ + Offsets:0 1 2 8 32 48 [bytes] + + suite: 1010b for new AEAD crypto, 0000b is old crypto + keytype: see constants.KeyType (suite+keytype) + reserved: all-zero, for future use + messageIV: a counter starting from 0 for all new encrypted messages of one session + sessionID: 192bit random, computed once per session (the session key is derived from this) + auth_tag: authentication tag output of the AEAD cipher (computed over payload and AAD) + payload: encrypted chunk data """ - PAYLOAD_OVERHEAD = 1 + 24 + 12 + 16 # TYPE + SESSIONID + NONCE + MAC + PAYLOAD_OVERHEAD = 1 + 1 + 6 + 24 + 16 # [bytes], see Layout CIPHERSUITE = None # override in subclass logically_encrypted = True def encrypt(self, chunk): + # to encrypt new data in this session we use always self.cipher and self.sessionid data = self.compressor.compress(chunk) - header = self.TYPE_STR + self.sessionid + reserved = b'\0' iv = self.cipher.next_iv() + iv_48bit = iv.to_bytes(6, 'big') + header = self.TYPE_STR + reserved + iv_48bit + self.sessionid return self.cipher.encrypt(data, header=header, iv=iv) def decrypt(self, id, data, decompress=True): + # to decrypt existing data, we need to get a cipher configured for the sessionid and iv from header self.assert_type(data[0], id) - sessionid = data[1:13] # XXX - self.init_ciphers(salt=salt, context=context, iv=iv) # XXX + iv_48bit = data[2:8] + sessionid = data[8:32] + iv = int.from_bytes(iv_48bit, 'big') + cipher = self._get_cipher(sessionid, iv) try: - payload = self.cipher.decrypt(data) + payload = cipher.decrypt(data) except IntegrityError as e: raise IntegrityError(f"Chunk {bin_to_hex(id)}: Could not decrypt [{str(e)}]") if not decompress: @@ -753,15 +768,26 @@ def init_from_random_data(self): if self.chunk_seed & 0x80000000: self.chunk_seed = self.chunk_seed - 0xffffffff - 1 - def init_ciphers(self, salt=b'', context=b'', iv=0): + def _get_session_key(self, sessionid): + assert len(sessionid) == 24 # 192bit key = hkdf_hmac_sha512( ikm=self.enc_key + self.enc_hmac_key, - salt=salt, - info=b'borg-crypto-' + context, # XXX + salt=sessionid, + info=b'borg-session-key-' + self.CIPHERSUITE.__name__.encode(), output_length=32 ) - self.cipher = self.CIPHERSUITE(key=key, header_len=1+24, aad_offset=0) # XXX - self.cipher.set_iv(iv) + return key + + def _get_cipher(self, sessionid, iv): + assert isinstance(iv, int) + key = self._get_session_key(sessionid) + cipher = self.CIPHERSUITE(key=key, iv=iv, header_len=1+1+6+24, aad_offset=0) + return cipher + + def init_ciphers(self, manifest_data=None, iv=0): + # in every new session we start with a fresh sessionid and at iv == 0, manifest_data and iv params are ignored + self.sessionid = os.urandom(24) + self.cipher = self._get_cipher(self.sessionid, iv=0) class AESOCBKeyfileKey(ID_HMAC_SHA_256, AEADKeyBase, FlexiKey): diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 93dec0be3b..c75808466e 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -424,7 +424,7 @@ ctypedef const EVP_CIPHER * (* CIPHER)() cdef class _AEAD_BASE: # new crypto used in borg >= 1.3 - # Layout: HEADER + MAC 16 + CT (IV will be put into the header, at the end) + # Layout: HEADER + MAC 16 + CT cdef CIPHER cipher cdef EVP_CIPHER_CTX *ctx @@ -432,7 +432,7 @@ cdef class _AEAD_BASE: cdef int cipher_blk_len cdef int iv_len cdef int aad_offset - cdef int header_len_expected # includes the IV at the end + cdef int header_len_expected cdef int mac_len cdef unsigned char iv[12] cdef long long blocks @@ -448,12 +448,12 @@ cdef class _AEAD_BASE: :param key: 256bit encrypt-then-mac key :param iv: 96bit initialisation vector / nonce - :param header_len: expected length of header *without* IV + :param header_len: expected length of header :param aad_offset: where in the header the authenticated data starts """ assert isinstance(key, bytes) and len(key) == 32 self.iv_len = sizeof(self.iv) - self.header_len_expected = header_len + self.iv_len + self.header_len_expected = header_len assert aad_offset <= header_len self.aad_offset = aad_offset self.mac_len = 16 @@ -483,8 +483,7 @@ cdef class _AEAD_BASE: if block_count > 2**32: raise ValueError('too much data, would overflow internal 32bit counter') cdef int ilen = len(data) - cdef int hl = len(header) - cdef int hlen = hl + self.iv_len + cdef int hlen = len(header) assert hlen == self.header_len_expected cdef int aoffset = self.aad_offset cdef int alen = hlen - aoffset @@ -498,11 +497,9 @@ cdef class _AEAD_BASE: cdef Py_buffer hdata = ro_buffer(header) try: offset = 0 - for i in range(hl): + for i in range(hlen): odata[offset+i] = header[i] - offset = hl - self.store_iv(odata+offset, self.iv) - offset = hlen + offset += hlen offset += self.mac_len rc = EVP_EncryptInit_ex(self.ctx, self.cipher(), NULL, NULL, NULL) if not rc: @@ -543,7 +540,6 @@ cdef class _AEAD_BASE: raise ValueError('too much data, would overflow internal 32bit counter') cdef int ilen = len(envelope) cdef int hlen = self.header_len_expected - cdef int hl = hlen - self.iv_len cdef int aoffset = self.aad_offset cdef int alen = hlen - aoffset cdef unsigned char *odata = PyMem_Malloc(ilen + self.cipher_blk_len) @@ -555,11 +551,9 @@ cdef class _AEAD_BASE: try: if not EVP_DecryptInit_ex(self.ctx, self.cipher(), NULL, NULL, NULL): raise CryptoError('EVP_DecryptInit_ex failed') - iv = self.fetch_iv( idata.buf+hl) - self.set_iv(iv) if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_IVLEN, self.iv_len, NULL): raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed') - if not EVP_DecryptInit_ex(self.ctx, NULL, NULL, self.key, iv): + if not EVP_DecryptInit_ex(self.ctx, NULL, NULL, self.key, self.iv): raise CryptoError('EVP_DecryptInit_ex failed') if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_TAG, self.mac_len, idata.buf + hlen): raise CryptoError('EVP_CIPHER_CTX_ctrl SET TAG failed') @@ -604,18 +598,6 @@ cdef class _AEAD_BASE: iv = int.from_bytes(self.iv[:self.iv_len], byteorder='big') return iv + 1 - cdef fetch_iv(self, unsigned char * iv_in): - return iv_in[0:self.iv_len] - - cdef store_iv(self, unsigned char * iv_out, unsigned char * iv): - cdef int i - for i in range(self.iv_len): - iv_out[i] = iv[i] - - def extract_iv(self, envelope): - offset = self.header_len_expected - self.iv_len - return bytes_to_long(envelope[offset:offset+self.iv_len]) - cdef class _AES_BASE(_AEAD_BASE): def __init__(self, *args, **kwargs): diff --git a/src/borg/testsuite/crypto.py b/src/borg/testsuite/crypto.py index 0980cbfc53..93b5087249 100644 --- a/src/borg/testsuite/crypto.py +++ b/src/borg/testsuite/crypto.py @@ -94,7 +94,7 @@ def test_AE(self): key = b'X' * 32 iv = 0 data = b'foo' * 10 - header = b'\x23' + header = b'\x23' + iv.to_bytes(12, 'big') tests = [ # (ciphersuite class, exp_mac, exp_cdata) ] @@ -137,7 +137,7 @@ def test_AEAD(self): key = b'X' * 32 iv = 0 data = b'foo' * 10 - header = b'\x12\x34\x56' + header = b'\x12\x34\x56' + iv.to_bytes(12, 'big') tests = [ # (ciphersuite class, exp_mac, exp_cdata) ] From c010800f55338b4773c3c3b57902afb35bba39ce Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 18 Mar 2022 21:24:19 +0100 Subject: [PATCH 09/34] header_len=0 fits header=b'' default --- src/borg/crypto/low_level.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index c75808466e..1a12aafba2 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -442,7 +442,7 @@ cdef class _AEAD_BASE: """check whether library requirements for this ciphersuite are satisfied""" raise NotImplemented # override / implement in child class - def __init__(self, key, iv=None, header_len=1, aad_offset=0): + def __init__(self, key, iv=None, header_len=0, aad_offset=0): """ init AEAD crypto @@ -463,7 +463,7 @@ cdef class _AEAD_BASE: else: self.blocks = -1 # make sure set_iv is called before encrypt - def __cinit__(self, key, iv=None, header_len=1, aad_offset=0): + def __cinit__(self, key, iv=None, header_len=0, aad_offset=0): self.ctx = EVP_CIPHER_CTX_new() def __dealloc__(self): @@ -617,7 +617,7 @@ cdef class AES256_OCB(_AES_BASE): if is_libressl: raise ValueError('AES OCB is not implemented by LibreSSL (yet?).') - def __init__(self, key, iv=None, header_len=1, aad_offset=0): + def __init__(self, key, iv=None, header_len=0, aad_offset=0): self.requirements_check() self.cipher = EVP_aes_256_ocb super().__init__(key, iv=iv, header_len=header_len, aad_offset=aad_offset) @@ -629,7 +629,7 @@ cdef class CHACHA20_POLY1305(_CHACHA_BASE): if is_libressl: raise ValueError('CHACHA20-POLY1305 is not implemented by LibreSSL (yet?).') - def __init__(self, key, iv=None, header_len=1, aad_offset=0): + def __init__(self, key, iv=None, header_len=0, aad_offset=0): self.requirements_check() self.cipher = EVP_chacha20_poly1305 super().__init__(key, iv=iv, header_len=header_len, aad_offset=aad_offset) From bb949b25eacc567826a61b8121dbcd704c4a02bc Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 18 Mar 2022 22:43:38 +0100 Subject: [PATCH 10/34] EVP_DecryptFinal_ex: fix check for return value seems like the current docs were updated. was "positive return code". now specifically mentions 0 and 1. --- src/borg/crypto/low_level.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 1a12aafba2..8a944745f6 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -568,7 +568,7 @@ cdef class _AEAD_BASE: raise CryptoError('EVP_DecryptUpdate failed') offset += olen rc = EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen) - if rc <= 0: + if not rc: # a failure here means corrupted or tampered tag (mac) or data. raise IntegrityError('Authentication / EVP_DecryptFinal_ex failed') offset += olen From 6c7b499d3f3263307b174307b7942d580863f84d Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Fri, 18 Mar 2022 23:43:42 +0100 Subject: [PATCH 11/34] set aead auth tag directly before EVP_DecryptFinal_ev https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption --- src/borg/crypto/low_level.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 8a944745f6..ac1b60532d 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -555,8 +555,6 @@ cdef class _AEAD_BASE: raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed') if not EVP_DecryptInit_ex(self.ctx, NULL, NULL, self.key, self.iv): raise CryptoError('EVP_DecryptInit_ex failed') - if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_TAG, self.mac_len, idata.buf + hlen): - raise CryptoError('EVP_CIPHER_CTX_ctrl SET TAG failed') rc = EVP_DecryptUpdate(self.ctx, NULL, &olen, idata.buf+aoffset, alen) if not rc: raise CryptoError('EVP_DecryptUpdate failed') @@ -567,6 +565,8 @@ cdef class _AEAD_BASE: if not rc: raise CryptoError('EVP_DecryptUpdate failed') offset += olen + if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_TAG, self.mac_len, idata.buf + hlen): + raise CryptoError('EVP_CIPHER_CTX_ctrl SET TAG failed') rc = EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen) if not rc: # a failure here means corrupted or tampered tag (mac) or data. From 6f2c587080a2b2fae600078ca4b3ba3b587f9d83 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 19 Mar 2022 00:30:19 +0100 Subject: [PATCH 12/34] tests: consistently give iv_int to ciphersuite --- src/borg/testsuite/crypto.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/borg/testsuite/crypto.py b/src/borg/testsuite/crypto.py index 93b5087249..456580df18 100644 --- a/src/borg/testsuite/crypto.py +++ b/src/borg/testsuite/crypto.py @@ -92,9 +92,9 @@ def test_AES256_CTR_HMAC_SHA256_aad(self): def test_AE(self): # used in legacy-like layout (1 type byte, no aad) key = b'X' * 32 - iv = 0 + iv_int = 0 data = b'foo' * 10 - header = b'\x23' + iv.to_bytes(12, 'big') + header = b'\x23' + iv_int.to_bytes(12, 'big') tests = [ # (ciphersuite class, exp_mac, exp_cdata) ] @@ -110,7 +110,7 @@ def test_AE(self): for cs_cls, exp_mac, exp_cdata in tests: # print(repr(cs_cls)) # encrypt/mac - cs = cs_cls(key, iv, header_len=len(header), aad_offset=1) + cs = cs_cls(key, iv_int, header_len=len(header), aad_offset=1) hdr_mac_iv_cdata = cs.encrypt(data, header=header) hdr = hdr_mac_iv_cdata[0:1] iv = hdr_mac_iv_cdata[1:13] @@ -122,12 +122,12 @@ def test_AE(self): self.assert_equal(hexlify(cdata), exp_cdata) self.assert_equal(cs.next_iv(), 1) # auth/decrypt - cs = cs_cls(key, header_len=len(header), aad_offset=1) + cs = cs_cls(key, iv_int, header_len=len(header), aad_offset=1) pdata = cs.decrypt(hdr_mac_iv_cdata) self.assert_equal(data, pdata) self.assert_equal(cs.next_iv(), 1) # auth-failure due to corruption (corrupted data) - cs = cs_cls(key, header_len=len(header), aad_offset=1) + cs = cs_cls(key, iv_int, header_len=len(header), aad_offset=1) hdr_mac_iv_cdata_corrupted = hdr_mac_iv_cdata[:29] + b'\0' + hdr_mac_iv_cdata[30:] self.assert_raises(IntegrityError, lambda: cs.decrypt(hdr_mac_iv_cdata_corrupted)) @@ -135,9 +135,9 @@ def test_AE(self): def test_AEAD(self): # test with aad key = b'X' * 32 - iv = 0 + iv_int = 0 data = b'foo' * 10 - header = b'\x12\x34\x56' + iv.to_bytes(12, 'big') + header = b'\x12\x34\x56' + iv_int.to_bytes(12, 'big') tests = [ # (ciphersuite class, exp_mac, exp_cdata) ] @@ -153,7 +153,7 @@ def test_AEAD(self): for cs_cls, exp_mac, exp_cdata in tests: # print(repr(cs_cls)) # encrypt/mac - cs = cs_cls(key, iv, header_len=len(header), aad_offset=1) + cs = cs_cls(key, iv_int, header_len=len(header), aad_offset=1) hdr_mac_iv_cdata = cs.encrypt(data, header=header) hdr = hdr_mac_iv_cdata[0:3] iv = hdr_mac_iv_cdata[3:15] @@ -165,12 +165,12 @@ def test_AEAD(self): self.assert_equal(hexlify(cdata), exp_cdata) self.assert_equal(cs.next_iv(), 1) # auth/decrypt - cs = cs_cls(key, header_len=len(header), aad_offset=1) + cs = cs_cls(key, iv_int, header_len=len(header), aad_offset=1) pdata = cs.decrypt(hdr_mac_iv_cdata) self.assert_equal(data, pdata) self.assert_equal(cs.next_iv(), 1) # auth-failure due to corruption (corrupted aad) - cs = cs_cls(key, header_len=len(header), aad_offset=1) + cs = cs_cls(key, iv_int, header_len=len(header), aad_offset=1) hdr_mac_iv_cdata_corrupted = hdr_mac_iv_cdata[:1] + b'\0' + hdr_mac_iv_cdata[2:] self.assert_raises(IntegrityError, lambda: cs.decrypt(hdr_mac_iv_cdata_corrupted)) From 41082f558fa5a8b752d84724c3f8706248c87bb7 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 20 Mar 2022 03:08:09 +0100 Subject: [PATCH 13/34] crypto: add some tests for new key types --- src/borg/testsuite/key.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/borg/testsuite/key.py b/src/borg/testsuite/key.py index 6d2207a9e7..1c86f5207b 100644 --- a/src/borg/testsuite/key.py +++ b/src/borg/testsuite/key.py @@ -8,7 +8,8 @@ from ..crypto.key import bin_to_hex from ..crypto.key import PlaintextKey, AuthenticatedKey, RepoKey, KeyfileKey, \ - Blake2KeyfileKey, Blake2RepoKey, Blake2AuthenticatedKey + Blake2KeyfileKey, Blake2RepoKey, Blake2AuthenticatedKey, \ + AESOCBKeyfileKey, AESOCBRepoKey, CHPOKeyfileKey, CHPORepoKey from ..crypto.key import ID_HMAC_SHA_256, ID_BLAKE2b_256 from ..crypto.key import TAMRequiredError, TAMInvalid, TAMUnsupportedSuiteError, UnsupportedManifestError from ..crypto.key import identify_key @@ -80,6 +81,8 @@ def keys_dir(self, request, monkeypatch, tmpdir): Blake2KeyfileKey, Blake2RepoKey, Blake2AuthenticatedKey, + AESOCBKeyfileKey, AESOCBRepoKey, + CHPOKeyfileKey, CHPORepoKey, )) def key(self, request, monkeypatch): monkeypatch.setenv('BORG_PASSPHRASE', 'test') From 6d6d3caa241b4c41cf311ca857dd45b4824b1f5e Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 20 Mar 2022 05:15:39 +0100 Subject: [PATCH 14/34] avoid losing the key if we just have a pointer to a bytes object which might go out of scope, we can lose it. also: cython can directly assign a bytes object into a same-size char array. --- src/borg/crypto/low_level.pyx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index ac1b60532d..fe64a3aa9e 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -428,7 +428,7 @@ cdef class _AEAD_BASE: cdef CIPHER cipher cdef EVP_CIPHER_CTX *ctx - cdef unsigned char *key + cdef unsigned char key[32] cdef int cipher_blk_len cdef int iv_len cdef int aad_offset @@ -587,8 +587,7 @@ cdef class _AEAD_BASE: if isinstance(iv, int): iv = iv.to_bytes(self.iv_len, byteorder='big') assert isinstance(iv, bytes) and len(iv) == self.iv_len - for i in range(self.iv_len): - self.iv[i] = iv[i] + self.iv = iv self.blocks = 0 # number of cipher blocks encrypted with this IV def next_iv(self): From 0b5a21275f57fb02e19fbed78ec6330718fdc716 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 20 Mar 2022 05:31:47 +0100 Subject: [PATCH 15/34] avoid losing the key (old crypto) if we just have a pointer to a bytes object which might go out of scope, we can lose it. also: cython can directly assign a bytes object into a same-size char array. --- src/borg/crypto/low_level.pyx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index fe64a3aa9e..76ae055b95 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -187,7 +187,7 @@ cdef class AES256_CTR_BASE: # Layout: HEADER + MAC 32 + IV 8 + CT (same as attic / borg < 1.3 IF HEADER = TYPE_BYTE, no AAD) cdef EVP_CIPHER_CTX *ctx - cdef unsigned char *enc_key + cdef unsigned char enc_key[32] cdef int cipher_blk_len cdef int iv_len, iv_len_short cdef int aad_offset @@ -335,8 +335,7 @@ cdef class AES256_CTR_BASE: if isinstance(iv, int): iv = iv.to_bytes(self.iv_len, byteorder='big') assert isinstance(iv, bytes) and len(iv) == self.iv_len - for i in range(self.iv_len): - self.iv[i] = iv[i] + self.iv = iv self.blocks = 0 # how many AES blocks got encrypted with this IV? def next_iv(self): @@ -360,7 +359,7 @@ cdef class AES256_CTR_BASE: cdef class AES256_CTR_HMAC_SHA256(AES256_CTR_BASE): - cdef unsigned char *mac_key + cdef unsigned char mac_key[32] 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 @@ -377,7 +376,7 @@ cdef class AES256_CTR_HMAC_SHA256(AES256_CTR_BASE): const unsigned char *data2, int data2_len, unsigned char *mac_buf): data = data1[:data1_len] + data2[:data2_len] - mac = hmac.HMAC(self.mac_key, data, hashlib.sha256).digest() + mac = hmac.HMAC(self.mac_key[:self.mac_len], data, hashlib.sha256).digest() for i in range(self.mac_len): mac_buf[i] = mac[i] @@ -390,7 +389,7 @@ cdef class AES256_CTR_HMAC_SHA256(AES256_CTR_BASE): cdef class AES256_CTR_BLAKE2b(AES256_CTR_BASE): - cdef unsigned char *mac_key + cdef unsigned char mac_key[128] 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 @@ -638,7 +637,7 @@ cdef class AES: """A thin wrapper around the OpenSSL EVP cipher API - for legacy code, like key file encryption""" cdef CIPHER cipher cdef EVP_CIPHER_CTX *ctx - cdef unsigned char *enc_key + cdef unsigned char enc_key[32] cdef int cipher_blk_len cdef int iv_len cdef unsigned char iv[16] @@ -726,8 +725,7 @@ cdef class AES: if isinstance(iv, int): iv = iv.to_bytes(self.iv_len, byteorder='big') assert isinstance(iv, bytes) and len(iv) == self.iv_len - for i in range(self.iv_len): - self.iv[i] = iv[i] + self.iv = iv self.blocks = 0 # number of cipher blocks encrypted with this IV def next_iv(self): From 74ecb636714fb5c73108784a9445a565d53b5f3d Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 20 Mar 2022 14:04:18 +0100 Subject: [PATCH 16/34] fix new crypto benchmarks for api change --- src/borg/archiver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/borg/archiver.py b/src/borg/archiver.py index b6923495ef..8845059c4d 100644 --- a/src/borg/archiver.py +++ b/src/borg/archiver.py @@ -604,9 +604,9 @@ def chunkit(chunker_name, *args, **kwargs): if not is_libressl: tests.extend([ ("aes-256-ocb", lambda: AES256_OCB( - None, key_256, iv=key_96, header_len=1, aad_offset=1).encrypt(random_10M, header=b'X')), + key_256, iv=key_96, header_len=1, aad_offset=1).encrypt(random_10M, header=b'X')), ("chacha20-poly1305", lambda: CHACHA20_POLY1305( - None, key_256, iv=key_96, header_len=1, aad_offset=1).encrypt(random_10M, header=b'X')), + key_256, iv=key_96, header_len=1, aad_offset=1).encrypt(random_10M, header=b'X')), ]) for spec, func in tests: print(f"{spec:<24} {size:<10} {timeit(func, number=100):.3f}s") From 41b8a04d82b73387dafb662064210d9fdb024b9a Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 20 Mar 2022 14:16:19 +0100 Subject: [PATCH 17/34] use faster hmac.digest api --- src/borg/crypto/low_level.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 76ae055b95..dec4f0e14b 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -376,7 +376,7 @@ cdef class AES256_CTR_HMAC_SHA256(AES256_CTR_BASE): const unsigned char *data2, int data2_len, unsigned char *mac_buf): data = data1[:data1_len] + data2[:data2_len] - mac = hmac.HMAC(self.mac_key[:self.mac_len], data, hashlib.sha256).digest() + mac = hmac.digest(self.mac_key[:self.mac_len], data, 'sha256') for i in range(self.mac_len): mac_buf[i] = mac[i] @@ -761,7 +761,7 @@ def hkdf_hmac_sha512(ikm, salt, info, output_length): # Step 1. HKDF-Extract (ikm, salt) -> prk if salt is None: salt = bytes(64) - prk = hmac.HMAC(salt, ikm, hashlib.sha512).digest() + prk = hmac.digest(salt, ikm, 'sha512') # Step 2. HKDF-Expand (prk, info, output_length) -> output key n = ceil(output_length / digest_length) @@ -769,6 +769,6 @@ def hkdf_hmac_sha512(ikm, salt, info, output_length): output = b'' for i in range(n): msg = t_n + info + (i + 1).to_bytes(1, 'little') - t_n = hmac.HMAC(prk, msg, hashlib.sha512).digest() + t_n = hmac.digest(prk, msg, 'sha512') output += t_n return output[:output_length] From d3b78a6cf56821c44142d9e2e98efdc1b8178f2d Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 21 Mar 2022 12:33:11 +0100 Subject: [PATCH 18/34] minor key.encrypt api change/cleanup we already have .decrypt(id, data, ...). i changed .encrypt(chunk) to .encrypt(id, data). the old borg crypto won't really need or use the id, but the new AEAD crypto will authenticate the id in future. --- src/borg/archive.py | 6 ++--- src/borg/cache.py | 4 ++-- src/borg/crypto/key.py | 18 +++++++-------- src/borg/helpers/manifest.py | 2 +- src/borg/testsuite/archiver.py | 8 +++---- src/borg/testsuite/key.py | 42 +++++++++++++++++++++------------- src/borg/testsuite/remote.py | 2 +- 7 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/borg/archive.py b/src/borg/archive.py index 728c76bb10..8336166615 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -1789,7 +1789,7 @@ def mark_as_possibly_superseded(id_): def add_callback(chunk): id_ = self.key.id_hash(chunk) - cdata = self.key.encrypt(chunk) + cdata = self.key.encrypt(id_, chunk) add_reference(id_, len(chunk), len(cdata), cdata) return id_ @@ -1811,7 +1811,7 @@ def verify_file_chunks(archive_name, item): def replacement_chunk(size): chunk = Chunk(None, allocation=CH_ALLOC, size=size) chunk_id, data = cached_hash(chunk, self.key.id_hash) - cdata = self.key.encrypt(data) + cdata = self.key.encrypt(chunk_id, data) csize = len(cdata) return chunk_id, size, csize, cdata @@ -1998,7 +1998,7 @@ def valid_item(obj): archive.items = items_buffer.chunks data = msgpack.packb(archive.as_dict()) new_archive_id = self.key.id_hash(data) - cdata = self.key.encrypt(data) + cdata = self.key.encrypt(new_archive_id, data) add_reference(new_archive_id, len(data), len(cdata), cdata) self.manifest.archives[info.name] = (new_archive_id, info.ts) pi.finish() diff --git a/src/borg/cache.py b/src/borg/cache.py index 92df82e118..4e6de052aa 100644 --- a/src/borg/cache.py +++ b/src/borg/cache.py @@ -942,7 +942,7 @@ def add_chunk(self, id, chunk, stats, overwrite=False, wait=True): refcount = self.seen_chunk(id, size) if refcount and not overwrite: return self.chunk_incref(id, stats) - data = self.key.encrypt(chunk) + data = self.key.encrypt(id, chunk) csize = len(data) self.repository.put(id, data, wait=wait) self.chunks.add(id, 1, size, csize) @@ -1107,7 +1107,7 @@ def add_chunk(self, id, chunk, stats, overwrite=False, wait=True): refcount = self.seen_chunk(id, size) if refcount: return self.chunk_incref(id, stats, size=size) - data = self.key.encrypt(chunk) + data = self.key.encrypt(id, chunk) csize = len(data) self.repository.put(id, data, wait=wait) self.chunks.add(id, 1, size, csize) diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index b8e2407663..e6e0b252a3 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -158,7 +158,7 @@ def id_hash(self, data): """ raise NotImplementedError - def encrypt(self, chunk): + def encrypt(self, id, data): pass def decrypt(self, id, data, decompress=True): @@ -264,8 +264,8 @@ def detect(cls, repository, manifest_data): def id_hash(self, data): return sha256(data).digest() - def encrypt(self, chunk): - data = self.compressor.compress(chunk) + def encrypt(self, id, data): + data = self.compressor.compress(data) return b''.join([self.TYPE_STR, data]) def decrypt(self, id, data, decompress=True): @@ -340,8 +340,8 @@ class AESKeyBase(KeyBase): logically_encrypted = True - def encrypt(self, chunk): - data = self.compressor.compress(chunk) + def encrypt(self, id, data): + data = self.compressor.compress(data) next_iv = self.nonce_manager.ensure_reservation(self.cipher.next_iv(), self.cipher.block_count(len(data))) return self.cipher.encrypt(data, header=self.TYPE_STR, iv=next_iv) @@ -678,8 +678,8 @@ def init_ciphers(self, manifest_data=None): if manifest_data is not None: self.assert_type(manifest_data[0]) - def encrypt(self, chunk): - data = self.compressor.compress(chunk) + def encrypt(self, id, data): + data = self.compressor.compress(data) return b''.join([self.TYPE_STR, data]) def decrypt(self, id, data, decompress=True): @@ -732,9 +732,9 @@ class AEADKeyBase(KeyBase): logically_encrypted = True - def encrypt(self, chunk): + def encrypt(self, id, data): # to encrypt new data in this session we use always self.cipher and self.sessionid - data = self.compressor.compress(chunk) + data = self.compressor.compress(data) reserved = b'\0' iv = self.cipher.next_iv() iv_48bit = iv.to_bytes(6, 'big') diff --git a/src/borg/helpers/manifest.py b/src/borg/helpers/manifest.py index 84871f736f..dc0580f289 100644 --- a/src/borg/helpers/manifest.py +++ b/src/borg/helpers/manifest.py @@ -261,4 +261,4 @@ def write(self): self.tam_verified = True data = self.key.pack_and_authenticate_metadata(manifest.as_dict()) self.id = self.key.id_hash(data) - self.repository.put(self.MANIFEST_ID, self.key.encrypt(data)) + self.repository.put(self.MANIFEST_ID, self.key.encrypt(self.MANIFEST_ID, data)) diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py index 2eb552bd40..88181c31e1 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -3806,7 +3806,7 @@ def test_manifest_rebuild_duplicate_archive(self): 'version': 1, }) archive_id = key.id_hash(archive) - repository.put(archive_id, key.encrypt(archive)) + repository.put(archive_id, key.encrypt(archive_id, archive)) repository.commit(compact=False) self.cmd('check', self.repository_location, exit_code=1) self.cmd('check', '--repair', self.repository_location, exit_code=0) @@ -3894,7 +3894,7 @@ class ManifestAuthenticationTest(ArchiverTestCaseBase): def spoof_manifest(self, repository): with repository: _, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - repository.put(Manifest.MANIFEST_ID, key.encrypt(msgpack.packb({ + repository.put(Manifest.MANIFEST_ID, key.encrypt(Manifest.MANIFEST_ID, msgpack.packb({ 'version': 1, 'archives': {}, 'config': {}, @@ -3907,7 +3907,7 @@ def test_fresh_init_tam_required(self): repository = Repository(self.repository_path, exclusive=True) with repository: manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - repository.put(Manifest.MANIFEST_ID, key.encrypt(msgpack.packb({ + repository.put(Manifest.MANIFEST_ID, key.encrypt(Manifest.MANIFEST_ID, msgpack.packb({ 'version': 1, 'archives': {}, 'timestamp': (datetime.utcnow() + timedelta(days=1)).strftime(ISO_FORMAT), @@ -3929,7 +3929,7 @@ def test_not_required(self): manifest = msgpack.unpackb(key.decrypt(None, repository.get(Manifest.MANIFEST_ID))) del manifest[b'tam'] - repository.put(Manifest.MANIFEST_ID, key.encrypt(msgpack.packb(manifest))) + repository.put(Manifest.MANIFEST_ID, key.encrypt(Manifest.MANIFEST_ID, msgpack.packb(manifest))) repository.commit(compact=False) output = self.cmd('list', '--debug', self.repository_location) assert 'archive1234' in output diff --git a/src/borg/testsuite/key.py b/src/borg/testsuite/key.py index 1c86f5207b..14297d480b 100644 --- a/src/borg/testsuite/key.py +++ b/src/borg/testsuite/key.py @@ -114,18 +114,21 @@ def load_key(self): def test_plaintext(self): key = PlaintextKey.create(None, None) chunk = b'foo' - assert hexlify(key.id_hash(chunk)) == b'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae' - assert chunk == key.decrypt(key.id_hash(chunk), key.encrypt(chunk)) + id = key.id_hash(chunk) + assert hexlify(id) == b'2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae' + assert chunk == key.decrypt(id, key.encrypt(id, chunk)) def test_keyfile(self, monkeypatch, keys_dir): monkeypatch.setenv('BORG_PASSPHRASE', 'test') key = KeyfileKey.create(self.MockRepository(), self.MockArgs()) assert key.cipher.next_iv() == 0 - manifest = key.encrypt(b'ABC') + chunk = b'ABC' + id = key.id_hash(chunk) + manifest = key.encrypt(id, chunk) assert key.cipher.extract_iv(manifest) == 0 - manifest2 = key.encrypt(b'ABC') + manifest2 = key.encrypt(id, chunk) assert manifest != manifest2 - assert key.decrypt(None, manifest) == key.decrypt(None, manifest2) + assert key.decrypt(id, manifest) == key.decrypt(id, manifest2) assert key.cipher.extract_iv(manifest2) == 1 iv = key.cipher.extract_iv(manifest) key2 = KeyfileKey.detect(self.MockRepository(), manifest) @@ -134,7 +137,8 @@ def test_keyfile(self, monkeypatch, keys_dir): assert len({key2.id_key, key2.enc_key, key2.enc_hmac_key}) == 3 assert key2.chunk_seed != 0 chunk = b'foo' - assert chunk == key2.decrypt(key.id_hash(chunk), key.encrypt(chunk)) + id = key.id_hash(chunk) + assert chunk == key2.decrypt(id, key.encrypt(id, chunk)) def test_keyfile_nonce_rollback_protection(self, monkeypatch, keys_dir): monkeypatch.setenv('BORG_PASSPHRASE', 'test') @@ -142,9 +146,11 @@ def test_keyfile_nonce_rollback_protection(self, monkeypatch, keys_dir): with open(os.path.join(get_security_dir(repository.id_str), 'nonce'), "w") as fd: fd.write("0000000000002000") key = KeyfileKey.create(repository, self.MockArgs()) - data = key.encrypt(b'ABC') + chunk = b'ABC' + id = key.id_hash(chunk) + data = key.encrypt(id, chunk) assert key.cipher.extract_iv(data) == 0x2000 - assert key.decrypt(None, data) == b'ABC' + assert key.decrypt(id, data) == chunk def test_keyfile_kfenv(self, tmpdir, monkeypatch): keyfile = tmpdir.join('keyfile') @@ -155,7 +161,7 @@ def test_keyfile_kfenv(self, tmpdir, monkeypatch): assert keyfile.exists() chunk = b'ABC' chunk_id = key.id_hash(chunk) - chunk_cdata = key.encrypt(chunk) + chunk_cdata = key.encrypt(chunk_id, chunk) key = KeyfileKey.detect(self.MockRepository(), chunk_cdata) assert chunk == key.decrypt(chunk_id, chunk_cdata) keyfile.remove() @@ -212,18 +218,20 @@ def test_decrypt_integrity(self, monkeypatch, keys_dir): def test_roundtrip(self, key): repository = key.repository plaintext = b'foo' - encrypted = key.encrypt(plaintext) + id = key.id_hash(plaintext) + encrypted = key.encrypt(id, plaintext) identified_key_class = identify_key(encrypted) assert identified_key_class == key.__class__ loaded_key = identified_key_class.detect(repository, encrypted) - decrypted = loaded_key.decrypt(None, encrypted) + decrypted = loaded_key.decrypt(id, encrypted) assert decrypted == plaintext def test_decrypt_decompress(self, key): plaintext = b'123456789' - encrypted = key.encrypt(plaintext) - assert key.decrypt(None, encrypted, decompress=False) != plaintext - assert key.decrypt(None, encrypted) == plaintext + id = key.id_hash(plaintext) + encrypted = key.encrypt(id, plaintext) + assert key.decrypt(id, encrypted, decompress=False) != plaintext + assert key.decrypt(id, encrypted) == plaintext def test_assert_id(self, key): plaintext = b'123456789' @@ -243,7 +251,8 @@ def test_authenticated_encrypt(self, monkeypatch): assert AuthenticatedKey.id_hash is ID_HMAC_SHA_256.id_hash assert len(key.id_key) == 32 plaintext = b'123456789' - authenticated = key.encrypt(plaintext) + id = key.id_hash(plaintext) + authenticated = key.encrypt(id, plaintext) # 0x07 is the key TYPE, \x0000 identifies no compression. assert authenticated == b'\x07\x00\x00' + plaintext @@ -253,7 +262,8 @@ def test_blake2_authenticated_encrypt(self, monkeypatch): assert Blake2AuthenticatedKey.id_hash is ID_BLAKE2b_256.id_hash assert len(key.id_key) == 128 plaintext = b'123456789' - authenticated = key.encrypt(plaintext) + id = key.id_hash(plaintext) + authenticated = key.encrypt(id, plaintext) # 0x06 is the key TYPE, 0x0000 identifies no compression. assert authenticated == b'\x06\x00\x00' + plaintext diff --git a/src/borg/testsuite/remote.py b/src/borg/testsuite/remote.py index 034ebb4bad..fe94dc414d 100644 --- a/src/borg/testsuite/remote.py +++ b/src/borg/testsuite/remote.py @@ -165,7 +165,7 @@ def key(self, repository, monkeypatch): def _put_encrypted_object(self, key, repository, data): id_ = key.id_hash(data) - repository.put(id_, key.encrypt(data)) + repository.put(id_, key.encrypt(id_, data)) return id_ @pytest.fixture From 8bd9477b9664264851f27ddff90172a48123eef3 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 21 Mar 2022 14:30:26 +0100 Subject: [PATCH 19/34] add aad parameter to borg.crypto.low_level api added it for all classes there, so the caller just give it. for the legacy AES-CTR based classes, the given aad is completely ignored. this is to stay compatible with repo data of borg < 1.3. for the new AEAD based classes: encrypt: the aad is fed into the auth tag computation decrypt: same. decrypt will fail on auth tag mismatch. --- src/borg/crypto/low_level.pyx | 34 +++++++++++++++++++++++++--------- src/borg/selftest.py | 2 +- src/borg/testsuite/crypto.py | 22 ++++++++++++++++++++++ 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index dec4f0e14b..6880d87362 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -152,7 +152,7 @@ class UNENCRYPTED: self.header_len = header_len self.set_iv(iv) - def encrypt(self, data, header=b'', iv=None): + def encrypt(self, data, header=b'', iv=None, aad=None): """ 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. @@ -162,7 +162,7 @@ class UNENCRYPTED: assert self.iv is not None, 'iv needs to be set before encrypt is called' return header + data - def decrypt(self, envelope): + def decrypt(self, envelope, aad=None): """ 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 @@ -235,7 +235,7 @@ cdef class AES256_CTR_BASE: """ raise NotImplementedError - def encrypt(self, data, header=b'', iv=None): + def encrypt(self, data, header=b'', iv=None, aad=None): """ encrypt data, compute mac over aad + iv + cdata, prepend header. aad_offset is the offset into the header where aad starts. @@ -285,7 +285,7 @@ cdef class AES256_CTR_BASE: PyBuffer_Release(&hdata) PyBuffer_Release(&idata) - def decrypt(self, envelope): + def decrypt(self, envelope, aad=None): """ authenticate aad + iv + cdata, decrypt cdata, ignore header bytes up to aad_offset. """ @@ -468,10 +468,13 @@ cdef class _AEAD_BASE: def __dealloc__(self): EVP_CIPHER_CTX_free(self.ctx) - def encrypt(self, data, header=b'', iv=None): + def encrypt(self, data, header=b'', iv=None, aad=b''): """ - encrypt data, compute mac over aad + cdata, prepend header. - aad_offset is the offset into the header where aad starts. + 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. """ if iv is not None: self.set_iv(iv) @@ -486,6 +489,7 @@ cdef class _AEAD_BASE: assert hlen == self.header_len_expected cdef int aoffset = self.aad_offset cdef int alen = hlen - aoffset + cdef int aadlen = len(aad) cdef unsigned char *odata = PyMem_Malloc(hlen + self.mac_len + ilen + self.cipher_blk_len) if not odata: @@ -494,6 +498,7 @@ cdef class _AEAD_BASE: cdef int offset cdef Py_buffer idata = ro_buffer(data) cdef Py_buffer hdata = ro_buffer(header) + cdef Py_buffer aadata = ro_buffer(aad) try: offset = 0 for i in range(hlen): @@ -508,6 +513,9 @@ cdef class _AEAD_BASE: rc = EVP_EncryptInit_ex(self.ctx, NULL, NULL, self.key, self.iv) if not rc: raise CryptoError('EVP_EncryptInit_ex failed') + rc = EVP_EncryptUpdate(self.ctx, NULL, &olen, aadata.buf, aadlen) + if not rc: + raise CryptoError('EVP_EncryptUpdate failed') rc = EVP_EncryptUpdate(self.ctx, NULL, &olen, hdata.buf+aoffset, alen) if not rc: raise CryptoError('EVP_EncryptUpdate failed') @@ -527,10 +535,12 @@ cdef class _AEAD_BASE: PyMem_Free(odata) PyBuffer_Release(&hdata) PyBuffer_Release(&idata) + PyBuffer_Release(&aadata) - def decrypt(self, envelope): + def decrypt(self, envelope, aad=b''): """ - authenticate aad + cdata, decrypt cdata, ignore header bytes up to aad_offset. + authenticate aad + header + cdata (from envelope), ignore header bytes up to aad_offset., + return decrypted cdata. """ # AES-OCB, CHACHA20 ciphers all add a internal 32bit counter to the 96bit (12Byte) # IV we provide, thus we must not decrypt more than 2^32 cipher blocks with same IV): @@ -541,12 +551,14 @@ cdef class _AEAD_BASE: cdef int hlen = self.header_len_expected cdef int aoffset = self.aad_offset cdef int alen = hlen - aoffset + cdef int aadlen = len(aad) cdef unsigned char *odata = PyMem_Malloc(ilen + self.cipher_blk_len) if not odata: raise MemoryError cdef int olen cdef int offset cdef Py_buffer idata = ro_buffer(envelope) + cdef Py_buffer aadata = ro_buffer(aad) try: if not EVP_DecryptInit_ex(self.ctx, self.cipher(), NULL, NULL, NULL): raise CryptoError('EVP_DecryptInit_ex failed') @@ -554,6 +566,9 @@ cdef class _AEAD_BASE: raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed') if not EVP_DecryptInit_ex(self.ctx, NULL, NULL, self.key, self.iv): raise CryptoError('EVP_DecryptInit_ex failed') + rc = EVP_DecryptUpdate(self.ctx, NULL, &olen, aadata.buf, aadlen) + if not rc: + raise CryptoError('EVP_DecryptUpdate failed') rc = EVP_DecryptUpdate(self.ctx, NULL, &olen, idata.buf+aoffset, alen) if not rc: raise CryptoError('EVP_DecryptUpdate failed') @@ -576,6 +591,7 @@ cdef class _AEAD_BASE: finally: PyMem_Free(odata) PyBuffer_Release(&idata) + PyBuffer_Release(&aadata) def block_count(self, length): return num_cipher_blocks(length, self.cipher_blk_len) diff --git a/src/borg/selftest.py b/src/borg/selftest.py index dfe7abb545..bacdcea2a5 100644 --- a/src/borg/selftest.py +++ b/src/borg/selftest.py @@ -29,7 +29,7 @@ ChunkerTestCase, ] -SELFTEST_COUNT = 35 +SELFTEST_COUNT = 36 class SelfTestResult(TestResult): diff --git a/src/borg/testsuite/crypto.py b/src/borg/testsuite/crypto.py index 456580df18..baf8cba2cd 100644 --- a/src/borg/testsuite/crypto.py +++ b/src/borg/testsuite/crypto.py @@ -175,6 +175,28 @@ def test_AEAD(self): self.assert_raises(IntegrityError, lambda: cs.decrypt(hdr_mac_iv_cdata_corrupted)) + def test_AEAD_with_more_AAD(self): + # test giving extra aad to the .encrypt() and .decrypt() calls + key = b'X' * 32 + iv_int = 0 + data = b'foo' * 10 + header = b'\x12\x34' + tests = [] + if not is_libressl: + tests += [AES256_OCB, CHACHA20_POLY1305] + for cs_cls in tests: + # encrypt/mac + cs = cs_cls(key, iv_int, header_len=len(header), aad_offset=0) + hdr_mac_iv_cdata = cs.encrypt(data, header=header, aad=b'correct_chunkid') + # successful auth/decrypt (correct aad) + cs = cs_cls(key, iv_int, header_len=len(header), aad_offset=0) + pdata = cs.decrypt(hdr_mac_iv_cdata, aad=b'correct_chunkid') + self.assert_equal(data, pdata) + # unsuccessful auth (incorrect aad) + cs = cs_cls(key, iv_int, header_len=len(header), aad_offset=0) + self.assert_raises(IntegrityError, + lambda: cs.decrypt(hdr_mac_iv_cdata, aad=b'incorrect_chunkid')) + # These test vectors come from https://www.kullo.net/blog/hkdf-sha-512-test-vectors/ # who claims to have verified these against independent Python and C++ implementations. From c50e1124b5512475273d25d7fdd7d731e6c43d66 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 21 Mar 2022 14:45:17 +0100 Subject: [PATCH 20/34] also authenticate the chunkid when using the AEAD ciphers (AES-OCB/CHACHA-POLY) --- src/borg/crypto/key.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index e6e0b252a3..4e26b52630 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -739,7 +739,7 @@ def encrypt(self, id, data): iv = self.cipher.next_iv() iv_48bit = iv.to_bytes(6, 'big') header = self.TYPE_STR + reserved + iv_48bit + self.sessionid - return self.cipher.encrypt(data, header=header, iv=iv) + return self.cipher.encrypt(data, header=header, iv=iv, aad=id) def decrypt(self, id, data, decompress=True): # to decrypt existing data, we need to get a cipher configured for the sessionid and iv from header @@ -749,7 +749,7 @@ def decrypt(self, id, data, decompress=True): iv = int.from_bytes(iv_48bit, 'big') cipher = self._get_cipher(sessionid, iv) try: - payload = cipher.decrypt(data) + payload = cipher.decrypt(data, aad=id) except IntegrityError as e: raise IntegrityError(f"Chunk {bin_to_hex(id)}: Could not decrypt [{str(e)}]") if not decompress: From f4a6ad080b130cfd8a296924c2882c029be5e3b7 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 21 Mar 2022 19:43:52 +0100 Subject: [PATCH 21/34] docs: add new AEAD modes to security docs --- docs/internals/data-structures.rst | 28 ++++++++++ docs/internals/security.rst | 88 +++++++++++++++++++++++++++++- 2 files changed, 113 insertions(+), 3 deletions(-) diff --git a/docs/internals/data-structures.rst b/docs/internals/data-structures.rst index 9e1c067f24..3d70e7807d 100644 --- a/docs/internals/data-structures.rst +++ b/docs/internals/data-structures.rst @@ -865,6 +865,31 @@ Encryption .. seealso:: The :ref:`borgcrypto` section for an in-depth review. +AEAD modes +~~~~~~~~~~ + +Uses modern AEAD ciphers: AES-OCB or CHACHA20-POLY1305. +For each borg invocation, a new sessionkey is derived from the borg key material +and the 48bit IV starts from 0 again (both ciphers internally add a 32bit counter +to our IV, so we'll just count up by 1 per chunk). + +The chunk layout is best seen at the bottom of this diagram: + +.. figure:: encryption-aead.png + :figwidth: 100% + :width: 100% + +No special IV/counter management is needed here due to the use of session keys. + +A 48 bit IV is way more than needed: If you only backed up 4kiB chunks (2^12B), +the IV would "limit" the data encrypted in one session to 2^(12+48)B == 2.3 exabytes, +meaning you would run against other limitations (RAM, storage, time) way before that. +In practice, chunks are usually bigger, for big files even much bigger, giving an +even higher limit. + +Legacy modes +~~~~~~~~~~~~ + AES_-256 is used in CTR mode (so no need for padding). A 64 bit initialization vector is used, a MAC is computed on the encrypted chunk and both are stored in the chunk. Encryption and MAC use two different keys. @@ -884,6 +909,9 @@ To reduce payload size, only 8 bytes of the 16 bytes nonce is saved in the payload, the first 8 bytes are always zeros. This does not affect security but limits the maximum repository capacity to only 295 exabytes (2**64 * 16 bytes). +Both modes +~~~~~~~~~~ + Encryption keys (and other secrets) are kept either in a key file on the client ('keyfile' mode) or in the repository config on the server ('repokey' mode). In both cases, the secrets are generated from random and then encrypted by a diff --git a/docs/internals/security.rst b/docs/internals/security.rst index 1b9764e1b9..2efb428b03 100644 --- a/docs/internals/security.rst +++ b/docs/internals/security.rst @@ -124,7 +124,88 @@ prompt is a set BORG_PASSPHRASE. See issue :issue:`2169` for details. Encryption ---------- -Encryption is currently based on the Encrypt-then-MAC construction, +AEAD modes +~~~~~~~~~~ + +Modes: --encryption (repokey|keyfile)-[blake2-](aes-ocb|chacha20-poly1305) + +Supported: borg 1.3+ + +Encryption with these modes is based on AEAD ciphers (authenticated encryption +with associated data) and session keys. + +Depending on the chosen mode (see :ref:`borg_init`) different AEAD ciphers are used: + +- AES-256-OCB - super fast, single-pass algorithm IF you have hw accelerated AES. +- chacha20-poly1305 - very fast, purely software based AEAD cipher. + +The chunk ID is derived via a MAC over the plaintext (mac key taken from borg key): + +- HMAC-SHA256 - super fast IF you have hw accelerated SHA256. +- Blake2b - very fast, purely software based algorithm. + +For each borg invocation, a new session id is generated by `os.urandom`_. + +From that session id, the initial key material (ikm, taken from the borg key) +and an application and cipher specific salt, borg derives a session key via HKDF. + +For each session key, IVs (nonces) are generated by a counter which increments for +each encrypted message. + +Session:: + + sessionid = os.urandom(24) + ikm = enc_key || enc_hmac_key + salt = "borg-session-key-CIPHERNAME" + sessionkey = HKDF(ikm, sessionid, salt) + message_iv = 0 + +Encryption:: + + id = MAC(id_key, data) + compressed = compress(data) + + header = type-byte || 00h || message_iv || sessionid + aad = id || header + message_iv++ + encrypted, auth_tag = AEAD_encrypt(session_key, message_iv, compressed, aad) + authenticated = header || auth_tag || encrypted + +Decryption:: + + # Given: input *authenticated* data and a *chunk-id* to assert + type-byte, past_message_iv, past_sessionid, auth_tag, encrypted = SPLIT(authenticated) + + ASSERT(type-byte is correct) + + past_key = HKDF(ikm, past_sessionid, salt) + decrypted = AEAD_decrypt(past_key, past_message_iv, authenticated) + + decompressed = decompress(decrypted) + + ASSERT( CONSTANT-TIME-COMPARISON( chunk-id, MAC(id_key, decompressed) ) ) + +Notable: + +- More modern and often faster AEAD ciphers instead of self-assembled stuff. +- Due to the usage of session keys, IVs (nonces) do not need special care here as + they did for the legacy encryption modes. +- The id is now also input into the authentication tag computation. + This strongly associates the id with the written data (== associates the key with + the value). When later reading the data for some id, authentication will only + succeed if what we get was really written by us for that id. + + +Legacy modes +~~~~~~~~~~~~ + +Modes: --encryption (repokey|keyfile)-[blake2] + +Supported: all borg versions, blake2 since 1.1 + +DEPRECATED. We strongly suggest you use the safer AEAD modes, see above. + +Encryption with these modes is based on the Encrypt-then-MAC construction, which is generally seen as the most robust way to create an authenticated encryption scheme from encryption and message authentication primitives. @@ -253,7 +334,7 @@ Implementations used We do not implement cryptographic primitives ourselves, but rely on widely used libraries providing them: -- AES-CTR and HMAC-SHA-256 from OpenSSL 1.0 / 1.1 are used, +- AES-CTR, AES-OCB, CHACHA20-POLY1305 and HMAC-SHA-256 from OpenSSL 1.1 are used, which is also linked into the static binaries we provide. We think this is not an additional risk, since we don't ever use OpenSSL's networking, TLS or X.509 code, but only their @@ -268,7 +349,8 @@ on widely used libraries providing them: Implemented cryptographic constructions are: -- Encrypt-then-MAC based on AES-256-CTR and either HMAC-SHA-256 +- AEAD modes: AES-OCB and CHACHA20-POLY1305 are straight from OpenSSL. +- Legacy modes: Encrypt-then-MAC based on AES-256-CTR and either HMAC-SHA-256 or keyed BLAKE2b256 as described above under Encryption_. - Encrypt-and-MAC based on AES-256-CTR and HMAC-SHA-256 as described above under `Offline key security`_. From 948d67efd8e188a2b9c3ecd8b94bf9fc3e5158b3 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 21 Mar 2022 22:29:18 +0100 Subject: [PATCH 22/34] crypto.low_level: simplify return code checks (AEAD) --- src/borg/crypto/low_level.pyx | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 6880d87362..8a6e4d817a 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -505,26 +505,20 @@ cdef class _AEAD_BASE: odata[offset+i] = header[i] offset += hlen offset += self.mac_len - rc = EVP_EncryptInit_ex(self.ctx, self.cipher(), NULL, NULL, NULL) - if not rc: + if not EVP_EncryptInit_ex(self.ctx, self.cipher(), NULL, NULL, NULL): raise CryptoError('EVP_EncryptInit_ex failed') if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_IVLEN, self.iv_len, NULL): raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed') - rc = EVP_EncryptInit_ex(self.ctx, NULL, NULL, self.key, self.iv) - if not rc: + if not EVP_EncryptInit_ex(self.ctx, NULL, NULL, self.key, self.iv): raise CryptoError('EVP_EncryptInit_ex failed') - rc = EVP_EncryptUpdate(self.ctx, NULL, &olen, aadata.buf, aadlen) - if not rc: + if not EVP_EncryptUpdate(self.ctx, NULL, &olen, aadata.buf, aadlen): raise CryptoError('EVP_EncryptUpdate failed') - rc = EVP_EncryptUpdate(self.ctx, NULL, &olen, hdata.buf+aoffset, alen) - if not rc: + if not EVP_EncryptUpdate(self.ctx, NULL, &olen, hdata.buf+aoffset, alen): raise CryptoError('EVP_EncryptUpdate failed') - rc = EVP_EncryptUpdate(self.ctx, odata+offset, &olen, idata.buf, ilen) - if not rc: + if not EVP_EncryptUpdate(self.ctx, odata+offset, &olen, idata.buf, ilen): raise CryptoError('EVP_EncryptUpdate failed') offset += olen - rc = EVP_EncryptFinal_ex(self.ctx, odata+offset, &olen) - if not rc: + if not EVP_EncryptFinal_ex(self.ctx, odata+offset, &olen): raise CryptoError('EVP_EncryptFinal_ex failed') offset += olen if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_GET_TAG, self.mac_len, odata + hlen): @@ -566,23 +560,19 @@ cdef class _AEAD_BASE: raise CryptoError('EVP_CIPHER_CTX_ctrl SET IVLEN failed') if not EVP_DecryptInit_ex(self.ctx, NULL, NULL, self.key, self.iv): raise CryptoError('EVP_DecryptInit_ex failed') - rc = EVP_DecryptUpdate(self.ctx, NULL, &olen, aadata.buf, aadlen) - if not rc: + if not EVP_DecryptUpdate(self.ctx, NULL, &olen, aadata.buf, aadlen): raise CryptoError('EVP_DecryptUpdate failed') - rc = EVP_DecryptUpdate(self.ctx, NULL, &olen, idata.buf+aoffset, alen) - if not rc: + if not EVP_DecryptUpdate(self.ctx, NULL, &olen, idata.buf+aoffset, alen): raise CryptoError('EVP_DecryptUpdate failed') offset = 0 - rc = EVP_DecryptUpdate(self.ctx, odata+offset, &olen, - idata.buf+hlen+self.mac_len, - ilen-hlen-self.mac_len) - if not rc: + if not EVP_DecryptUpdate(self.ctx, odata+offset, &olen, + idata.buf+hlen+self.mac_len, + ilen-hlen-self.mac_len): raise CryptoError('EVP_DecryptUpdate failed') offset += olen if not EVP_CIPHER_CTX_ctrl(self.ctx, EVP_CTRL_AEAD_SET_TAG, self.mac_len, idata.buf + hlen): raise CryptoError('EVP_CIPHER_CTX_ctrl SET TAG failed') - rc = EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen) - if not rc: + if not EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen): # a failure here means corrupted or tampered tag (mac) or data. raise IntegrityError('Authentication / EVP_DecryptFinal_ex failed') offset += olen From e1313ccc05a5eb89df5e9622c55589a1f46ef33f Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 21 Mar 2022 22:33:44 +0100 Subject: [PATCH 23/34] crypto.low_level: simplify return code checks (legacy) --- src/borg/crypto/low_level.pyx | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 8a6e4d817a..50c78f5f31 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -264,15 +264,12 @@ cdef class AES256_CTR_BASE: offset += self.mac_len self.store_iv(odata+offset, self.iv) offset += self.iv_len_short - rc = EVP_EncryptInit_ex(self.ctx, EVP_aes_256_ctr(), NULL, self.enc_key, self.iv) - if not rc: + if not EVP_EncryptInit_ex(self.ctx, EVP_aes_256_ctr(), NULL, self.enc_key, self.iv): raise CryptoError('EVP_EncryptInit_ex failed') - rc = EVP_EncryptUpdate(self.ctx, odata+offset, &olen, idata.buf, ilen) - if not rc: + if not EVP_EncryptUpdate(self.ctx, odata+offset, &olen, idata.buf, ilen): raise CryptoError('EVP_EncryptUpdate failed') offset += olen - rc = EVP_EncryptFinal_ex(self.ctx, odata+offset, &olen) - if not rc: + if not EVP_EncryptFinal_ex(self.ctx, odata+offset, &olen): raise CryptoError('EVP_EncryptFinal_ex failed') offset += olen self.mac_compute( hdata.buf+aoffset, alen, @@ -311,14 +308,12 @@ cdef class AES256_CTR_BASE: if not EVP_DecryptInit_ex(self.ctx, EVP_aes_256_ctr(), NULL, self.enc_key, iv): raise CryptoError('EVP_DecryptInit_ex failed') offset = 0 - rc = EVP_DecryptUpdate(self.ctx, odata+offset, &olen, - idata.buf+hlen+self.mac_len+self.iv_len_short, - ilen-hlen-self.mac_len-self.iv_len_short) - if not rc: + if not EVP_DecryptUpdate(self.ctx, odata+offset, &olen, + idata.buf+hlen+self.mac_len+self.iv_len_short, + ilen-hlen-self.mac_len-self.iv_len_short): raise CryptoError('EVP_DecryptUpdate failed') offset += olen - rc = EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen) - if rc <= 0: + if EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen) <= 0: raise CryptoError('EVP_DecryptFinal_ex failed') offset += olen self.blocks += self.block_count(offset) From ccf0875053de2785b09766627cd0ae7d8448ec34 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 21 Mar 2022 22:36:16 +0100 Subject: [PATCH 24/34] EVP_DecryptFinal_ex: fix check for return value seems like the current docs were updated. was "positive return code". now specifically mentions 0 and 1. --- src/borg/crypto/low_level.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 50c78f5f31..43e531c9ec 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -313,7 +313,7 @@ cdef class AES256_CTR_BASE: ilen-hlen-self.mac_len-self.iv_len_short): raise CryptoError('EVP_DecryptUpdate failed') offset += olen - if EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen) <= 0: + if not EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen): raise CryptoError('EVP_DecryptFinal_ex failed') offset += olen self.blocks += self.block_count(offset) @@ -705,7 +705,7 @@ cdef class AES: if not EVP_DecryptUpdate(self.ctx, odata, &olen, idata.buf, ilen): raise Exception('EVP_DecryptUpdate failed') offset += olen - if EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen) <= 0: + if not EVP_DecryptFinal_ex(self.ctx, odata+offset, &olen): # 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. From b3383a4d53cf10dc159083cf7db5c34b71acc0d2 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 22 Mar 2022 00:01:07 +0100 Subject: [PATCH 25/34] update borg init docs --- src/borg/archiver.py | 115 ++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 72 deletions(-) diff --git a/src/borg/archiver.py b/src/borg/archiver.py index 8845059c4d..9be09948ee 100644 --- a/src/borg/archiver.py +++ b/src/borg/archiver.py @@ -4169,22 +4169,19 @@ def define_borg_mount(parser): Encryption mode TLDR ++++++++++++++++++++ - The encryption mode can only be configured when creating a new repository - - you can neither configure it on a per-archive basis nor change the - encryption mode of an existing repository. + The encryption mode can only be configured when creating a new repository - you can + neither configure it on a per-archive basis nor change the mode of an existing repository. + This example will likely NOT give optimum performance on your machine (performance + tips will come below): - Use ``repokey``:: + :: borg init --encryption repokey /path/to/repo - Or ``repokey-blake2`` depending on which is faster on your client machines (see below):: - - borg init --encryption repokey-blake2 /path/to/repo - Borg will: 1. Ask you to come up with a passphrase. - 2. Create a borg key (which contains 3 random secrets. See :ref:`key_files`). + 2. Create a borg key (which contains some random secrets. See :ref:`key_files`). 3. Encrypt the key with your passphrase. 4. Store the encrypted borg key inside the repository directory (in the repo config). This is why it is essential to use a secure passphrase. @@ -4220,79 +4217,53 @@ def define_borg_mount(parser): You can change your passphrase for existing repos at any time, it won't affect the encryption/decryption key or other secrets. - More encryption modes - +++++++++++++++++++++ + Choosing an encryption mode + +++++++++++++++++++++++++++ - Only use ``--encryption none`` if you are OK with anyone who has access to - your repository being able to read your backups and tamper with their - contents without you noticing. + Depending on your hardware, hashing and crypto performance may vary widely. + The easiest way to find out about what's fastest is to run ``borg benchmark cpu``. - If you want "passphrase and having-the-key" security, use ``--encryption keyfile``. - The key will be stored in your home directory (in ``~/.config/borg/keys``). + `repokey` modes: if you want ease-of-use and "passphrase" security is good enough - + the key will be stored in the repository (in ``repo_dir/config``). - If you do **not** want to encrypt the contents of your backups, but still - want to detect malicious tampering use ``--encryption authenticated``. + `keyfile` modes: if you rather want "passphrase and having-the-key" security - + the key will be stored in your home directory (in ``~/.config/borg/keys``). - If ``BLAKE2b`` is faster than ``SHA-256`` on your hardware, use ``--encryption authenticated-blake2``, - ``--encryption repokey-blake2`` or ``--encryption keyfile-blake2``. Note: for remote backups - the hashing is done on your local machine. + The following table is roughly sorted in order of preference, the better ones are + in the upper part of the table, in the lower part is the old and/or unsafe(r) stuff: .. nanorst: inline-fill - +----------+---------------+------------------------+--------------------------+ - | Hash/MAC | Not encrypted | Not encrypted, | Encrypted (AEAD w/ AES) | - | | no auth | but authenticated | and authenticated | - +----------+---------------+------------------------+--------------------------+ - | SHA-256 | none | `authenticated` | repokey | - | | | | keyfile | - +----------+---------------+------------------------+--------------------------+ - | BLAKE2b | n/a | `authenticated-blake2` | `repokey-blake2` | - | | | | `keyfile-blake2` | - +----------+---------------+------------------------+--------------------------+ + +---------------------------------+---------------+---------------+------------------+-------+ + |**mode (* = keyfile or repokey)**|**ID-Hash** |**Encryption** |**Authentication**|**V>=**| + +---------------------------------+---------------+---------------+------------------+-------+ + | ``*-blake2-chacha20-poly1305`` | BLAKE2b | CHACHA20 | POLY1305 | 1.3 | + +---------------------------------+---------------+---------------+------------------+-------+ + | ``*-chacha20-poly1305`` | HMAC-SHA-256 | CHACHA20 | POLY1305 | 1.3 | + +---------------------------------+---------------+---------------+------------------+-------+ + | ``*-blake2-aes-ocb`` | BLAKE2b | AES256-OCB | AES256-OCB | 1.3 | + +---------------------------------+---------------+---------------+------------------+-------+ + | ``*-aes-ocb`` | HMAC-SHA-256 | AES256-OCB | AES256-OCB | 1.3 | + +---------------------------------+---------------+---------------+------------------+-------+ + | ``*-blake2`` | BLAKE2b | AES256-CTR | BLAKE2b | 1.1 | + +---------------------------------+---------------+---------------+------------------+-------+ + | ``*`` | HMAC-SHA-256 | AES256-CTR | HMAC-SHA256 | any | + +---------------------------------+---------------+---------------+------------------+-------+ + | authenticated-blake2 | BLAKE2b | none | BLAKE2b | 1.1 | + +---------------------------------+---------------+---------------+------------------+-------+ + | authenticated | HMAC-SHA-256 | none | HMAC-SHA256 | 1.1 | + +---------------------------------+---------------+---------------+------------------+-------+ + | none | SHA-256 | none | none | any | + +---------------------------------+---------------+---------------+------------------+-------+ .. nanorst: inline-replace - Modes `marked like this` in the above table are new in Borg 1.1 and are not - backwards-compatible with Borg 1.0.x. - - On modern Intel/AMD CPUs (except very cheap ones), AES is usually - hardware-accelerated. - BLAKE2b is faster than SHA256 on Intel/AMD 64-bit CPUs - (except AMD Ryzen and future CPUs with SHA extensions), - which makes `authenticated-blake2` faster than `none` and `authenticated`. - - On modern ARM CPUs, NEON provides hardware acceleration for SHA256 making it faster - than BLAKE2b-256 there. NEON accelerates AES as well. - - Hardware acceleration is always used automatically when available. - - `repokey` and `keyfile` use AES-CTR-256 for encryption and HMAC-SHA256 for - authentication in an encrypt-then-MAC (EtM) construction. The chunk ID hash - is HMAC-SHA256 as well (with a separate key). - These modes are compatible with Borg 1.0.x. - - `repokey-blake2` and `keyfile-blake2` are also authenticated encryption modes, - but use BLAKE2b-256 instead of HMAC-SHA256 for authentication. The chunk ID - hash is a keyed BLAKE2b-256 hash. - These modes are new and *not* compatible with Borg 1.0.x. - - `authenticated` mode uses no encryption, but authenticates repository contents - through the same HMAC-SHA256 hash as the `repokey` and `keyfile` modes (it uses it - as the chunk ID hash). The key is stored like `repokey`. - This mode is new and *not* compatible with Borg 1.0.x. - - `authenticated-blake2` is like `authenticated`, but uses the keyed BLAKE2b-256 hash - from the other blake2 modes. - This mode is new and *not* compatible with Borg 1.0.x. - - `none` mode uses no encryption and no authentication. It uses SHA256 as chunk - ID hash. This mode is not recommended, you should rather consider using an authenticated - or authenticated/encrypted mode. This mode has possible denial-of-service issues - when running ``borg create`` on contents controlled by an attacker. - Use it only for new repositories where no encryption is wanted **and** when compatibility - with 1.0.x is important. If compatibility with 1.0.x is not important, use - `authenticated-blake2` or `authenticated` instead. - This mode is compatible with Borg 1.0.x. + `none` mode uses no encryption and no authentication. You're advised to NOT use this mode + as it would expose you to all sorts of issues (DoS, confidentiality, tampering, ...) in + case of malicious activity in the repository. + + If you do **not** want to encrypt the contents of your backups, but still want to detect + malicious tampering use an `authenticated` mode. It's like `repokey` minus encryption. """) subparser = subparsers.add_parser('init', parents=[common_parser], add_help=False, description=self.do_init.__doc__, epilog=init_epilog, From 298c5ee539aad8a5bf6aa257785e70156069129d Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 22 Mar 2022 01:38:03 +0100 Subject: [PATCH 26/34] docs: security infos only applying to legacy encryption --- docs/faq.rst | 35 +++++++++++++++++++++-------------- docs/internals/security.rst | 2 +- docs/quickstart.rst | 1 + 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/docs/faq.rst b/docs/faq.rst index d982aec2c9..b3c3d73171 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -89,7 +89,7 @@ Also, you must not run borg against multiple instances of the same repo (which is an issue if they happen to be not the same). See :issue:`4272` for an example. - Encryption security issues if you would update repo and copy-of-repo - independently, due to AES counter reuse. + independently, due to AES counter reuse (when using legacy encryption modes). See also: :ref:`faq_corrupt_repo` @@ -246,6 +246,8 @@ then use ``tar`` to perform the comparison: My repository is corrupt, how can I restore from an older copy of it? --------------------------------------------------------------------- +Note: this is only required for repos using legacy encryption modes. + If your repositories are encrypted and have the same ID, the recommended method is to delete the corrupted repository, but keep its security info, and then copy the working repository to the same location: @@ -473,8 +475,11 @@ Security .. _borg_security_critique: -Isn't BorgBackup's AES-CTR crypto broken? ------------------------------------------ +Isn't BorgBackup's legacy AES-CTR-based crypto broken? +------------------------------------------------------ + +Note: in borg 1.3 new AEAD cipher based modes with session keys were added, +solving the issues of the legacy modes. If a nonce (counter) value is reused, AES-CTR mode crypto is broken. @@ -713,6 +718,8 @@ Please disclose security issues responsibly. How important are the nonce files? ------------------------------------ +This only applies to repositories using legacy encryption modes. + Borg uses :ref:`AES-CTR encryption `. An essential part of AES-CTR is a sequential counter that must **never** repeat. If the same value of the counter is used twice in the same repository, @@ -881,14 +888,14 @@ What's the expected backup performance? --------------------------------------- Compared to simply copying files (e.g. with ``rsync``), Borg has more work to do. -This can make creation of the first archive slower, but saves time +This can make creation of the first archive slower, but saves time and disk space on subsequent runs. Here what Borg does when you run ``borg create``: - Borg chunks the file (using the relatively expensive buzhash algorithm) -- It then computes the "id" of the chunk (hmac-sha256 (often slow, except +- It then computes the "id" of the chunk (hmac-sha256 (often slow, except if your CPU has sha256 acceleration) or blake2b (fast, in software)) -- Then it checks whether this chunk is already in the repo (local hashtable lookup, - fast). If so, the processing of the chunk is completed here. Otherwise it needs to +- Then it checks whether this chunk is already in the repo (local hashtable lookup, + fast). If so, the processing of the chunk is completed here. Otherwise it needs to process the chunk: - Compresses (the default lz4 is super fast) - Encrypts (AES, usually fast if your CPU has AES acceleration as usual @@ -896,9 +903,9 @@ and disk space on subsequent runs. Here what Borg does when you run ``borg creat - Authenticates ("signs") using hmac-sha256 or blake2b (see above), - Transmits to repo. If the repo is remote, this usually involves an SSH connection (does its own encryption / authentication). -- Stores the chunk into a key/value store (the key is the chunk id, the value +- Stores the chunk into a key/value store (the key is the chunk id, the value is the data). While doing that, it computes a CRC32 of the data (repo low-level - checksum, used by borg check --repository) and also updates the repo index + checksum, used by borg check --repository) and also updates the repo index (another hashtable). Subsequent backups are usually very fast if most files are unchanged and only @@ -928,14 +935,14 @@ If you feel your Borg backup is too slow somehow, here is what you can do: - Make sure Borg has enough RAM (depends on how big your repo is / how many files you have) -- Use one of the blake2 modes for --encryption except if you positively know +- Use one of the blake2 modes for --encryption except if you positively know your CPU (and openssl) accelerates sha256 (then stay with hmac-sha256). - Don't use any expensive compression. The default is lz4 and super fast. Uncompressed is often slower than lz4. - Just wait. You can also interrupt it and start it again as often as you like, it will converge against a valid "completed" state (see ``--checkpoint-interval``, maybe use the default, but in any case don't make it too short). It is starting - from the beginning each time, but it is still faster then as it does not store + from the beginning each time, but it is still faster then as it does not store data into the repo which it already has there from last checkpoint. - If you don’t need additional file attributes, you can disable them with ``--noflags``, ``--noacls``, ``--noxattrs``. This can lead to noticable performance improvements @@ -945,12 +952,12 @@ If you feel that Borg "freezes" on a file, it could be in the middle of processi large file (like ISOs or VM images). Borg < 1.2 announces file names *after* finishing with the file. This can lead to displaying the name of a small file, while processing the next (larger) file. For very big files this can lead to the progress display show some -previous short file for a long time while it processes the big one. With Borg 1.2 this +previous short file for a long time while it processes the big one. With Borg 1.2 this was changed to announcing the filename before starting to process it. To see what files have changed and take more time processing, you can also add -``--list --filter=AME --stats`` to your ``borg create`` call to produce more log output, -including a file list (with file status characters) and also some statistics at +``--list --filter=AME --stats`` to your ``borg create`` call to produce more log output, +including a file list (with file status characters) and also some statistics at the end of the backup. Then you do the backup and look at the log output: diff --git a/docs/internals/security.rst b/docs/internals/security.rst index 2efb428b03..84f443f320 100644 --- a/docs/internals/security.rst +++ b/docs/internals/security.rst @@ -218,7 +218,7 @@ in the future. Depending on the chosen mode (see :ref:`borg_init`) different primitives are used: -- The actual encryption is currently always AES-256 in CTR mode. The +- Legacy encryption modes use AES-256 in CTR mode. The counter is added in plaintext, since it is needed for decryption, and is also tracked locally on the client to avoid counter reuse. diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 52fad66392..dd9d45eaae 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -387,6 +387,7 @@ For automated backups the passphrase can be specified using the A backup inside of the backup that is encrypted with that key/passphrase won't help you with that, of course. + Only applies to repos using legacy encryption modes: In case you lose your repository and the security information, but have an older copy of it to restore from, don't use that later for creating new backups – you would run into security issues (reuse of nonce counter From ce247526c2843b2b83b7160611dc4a0e27bc778a Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 22 Mar 2022 01:55:49 +0100 Subject: [PATCH 27/34] docs: update borg init examples --- docs/usage/init.rst | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/usage/init.rst b/docs/usage/init.rst index ae0fd8b423..73e62bb79f 100644 --- a/docs/usage/init.rst +++ b/docs/usage/init.rst @@ -4,16 +4,19 @@ Examples ~~~~~~~~ :: - # Local repository, repokey encryption, BLAKE2b (often faster, since Borg 1.1) - $ borg init --encryption=repokey-blake2 /path/to/repo + # Local repository, recommended repokey AEAD crypto modes + $ borg init --encryption=repokey-aes-ocb /path/to/repo + $ borg init --encryption=repokey-chacha20-poly1305 /path/to/repo + $ borg init --encryption=repokey-blake2-aes-ocb /path/to/repo + $ borg init --encryption=repokey-blake2-chacha20-poly1305 /path/to/repo - # Local repository (no encryption) + # Local repository (no encryption), not recommended $ borg init --encryption=none /path/to/repo # Remote repository (accesses a remote borg via ssh) # repokey: stores the (encrypted) key into /config - $ borg init --encryption=repokey-blake2 user@hostname:backup + $ borg init --encryption=repokey-aes-ocb user@hostname:backup # Remote repository (accesses a remote borg via ssh) # keyfile: stores the (encrypted) key into ~/.config/borg/keys/ - $ borg init --encryption=keyfile user@hostname:backup + $ borg init --encryption=keyfile-aes-ocb user@hostname:backup From 900a812e9cb6d66aed04dbdb304e85477398b2af Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 22 Mar 2022 01:59:51 +0100 Subject: [PATCH 28/34] crypto: bump API_VERSION to 1.3_01 --- src/borg/crypto/low_level.pyx | 2 +- src/borg/helpers/checks.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 43e531c9ec..83e1879baa 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -42,7 +42,7 @@ from cpython cimport PyMem_Malloc, PyMem_Free from cpython.buffer cimport PyBUF_SIMPLE, PyObject_GetBuffer, PyBuffer_Release from cpython.bytes cimport PyBytes_FromStringAndSize -API_VERSION = '1.2_01' +API_VERSION = '1.3_01' cdef extern from "openssl/crypto.h": int CRYPTO_memcmp(const void *a, const void *b, size_t len) diff --git a/src/borg/helpers/checks.py b/src/borg/helpers/checks.py index 1fe2c1439c..1d14788b95 100644 --- a/src/borg/helpers/checks.py +++ b/src/borg/helpers/checks.py @@ -31,7 +31,7 @@ def check_extension_modules(): raise ExtensionModuleError if compress.API_VERSION != '1.2_02': raise ExtensionModuleError - if borg.crypto.low_level.API_VERSION != '1.2_01': + if borg.crypto.low_level.API_VERSION != '1.3_01': raise ExtensionModuleError if item.API_VERSION != '1.2_01': raise ExtensionModuleError From e4b65dea76c1c01663a744ea42208ab0139380e1 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 22 Mar 2022 02:26:16 +0100 Subject: [PATCH 29/34] crypto: add IV overflow check will never happen, but better play safe. --- src/borg/crypto/key.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index 4e26b52630..ab8116591b 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -732,11 +732,15 @@ class AEADKeyBase(KeyBase): logically_encrypted = True + MAX_IV = 2 ** 48 - 1 + def encrypt(self, id, data): # to encrypt new data in this session we use always self.cipher and self.sessionid data = self.compressor.compress(data) reserved = b'\0' iv = self.cipher.next_iv() + if iv > self.MAX_IV: # see the data-structures docs about why the IV range is enough + raise IntegrityError("IV overflow, should never happen.") iv_48bit = iv.to_bytes(6, 'big') header = self.TYPE_STR + reserved + iv_48bit + self.sessionid return self.cipher.encrypt(data, header=header, iv=iv, aad=id) From 3a0e1a1cc2583cfff42238f77666ba840fd101ac Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 22 Mar 2022 02:57:28 +0100 Subject: [PATCH 30/34] crypto: low_level: reduce class inheritance depth --- src/borg/crypto/low_level.pyx | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 83e1879baa..baac7c1baa 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -598,19 +598,7 @@ cdef class _AEAD_BASE: return iv + 1 -cdef class _AES_BASE(_AEAD_BASE): - def __init__(self, *args, **kwargs): - self.cipher_blk_len = 16 - super().__init__(*args, **kwargs) - - -cdef class _CHACHA_BASE(_AEAD_BASE): - def __init__(self, *args, **kwargs): - self.cipher_blk_len = 64 - super().__init__(*args, **kwargs) - - -cdef class AES256_OCB(_AES_BASE): +cdef class AES256_OCB(_AEAD_BASE): @classmethod def requirements_check(cls): if is_libressl: @@ -619,10 +607,11 @@ cdef class AES256_OCB(_AES_BASE): def __init__(self, key, iv=None, header_len=0, aad_offset=0): self.requirements_check() self.cipher = EVP_aes_256_ocb + self.cipher_blk_len = 16 super().__init__(key, iv=iv, header_len=header_len, aad_offset=aad_offset) -cdef class CHACHA20_POLY1305(_CHACHA_BASE): +cdef class CHACHA20_POLY1305(_AEAD_BASE): @classmethod def requirements_check(cls): if is_libressl: @@ -631,6 +620,7 @@ cdef class CHACHA20_POLY1305(_CHACHA_BASE): def __init__(self, key, iv=None, header_len=0, aad_offset=0): self.requirements_check() self.cipher = EVP_chacha20_poly1305 + self.cipher_blk_len = 64 super().__init__(key, iv=iv, header_len=header_len, aad_offset=aad_offset) @@ -735,7 +725,6 @@ cdef class AES: return iv + self.blocks - def hmac_sha256(key, data): return hmac.digest(key, data, 'sha256') From dd2a054ac42147b47ddfb70d4541ac33100072a3 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 22 Mar 2022 03:06:54 +0100 Subject: [PATCH 31/34] crypto: key: reduce class inheritance depth --- src/borg/crypto/key.py | 20 +++----------------- src/borg/testsuite/archiver.py | 4 ++-- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index ab8116591b..3691051b0a 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -383,7 +383,9 @@ def init_ciphers(self, manifest_data=None): self.nonce_manager = NonceManager(self.repository, nonce) -class FlexiKeyBase: +class FlexiKey: + FILE_ID = 'BORG_KEY' + @classmethod def detect(cls, repository, manifest_data): key = cls(repository) @@ -406,12 +408,6 @@ def detect(cls, repository, manifest_data): key._passphrase = passphrase return key - def find_key(self): - raise NotImplementedError - - def load(self, target, passphrase): - raise NotImplementedError - def _load(self, key_data, passphrase): cdata = a2b_base64(key_data) data = self.decrypt_key_file(cdata, passphrase) @@ -489,16 +485,6 @@ def create(cls, repository, args): logger.info('Keep this key safe. Your data will be inaccessible without it.') return key - def save(self, target, passphrase, create=False): - raise NotImplementedError - - def get_new_target(self, args): - raise NotImplementedError - - -class FlexiKey(FlexiKeyBase): - FILE_ID = 'BORG_KEY' - def sanity_check(self, filename, id): file_id = self.FILE_ID.encode() + b' ' repo_id = hexlify(id) diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py index 88181c31e1..62a33fb898 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -36,7 +36,7 @@ from ..chunker import has_seek_hole from ..constants import * # NOQA from ..crypto.low_level import bytes_to_long, num_cipher_blocks -from ..crypto.key import FlexiKeyBase, RepoKey, KeyfileKey, Passphrase, TAMRequiredError +from ..crypto.key import FlexiKey, RepoKey, KeyfileKey, Passphrase, TAMRequiredError from ..crypto.keymanager import RepoIdMismatch, NotABorgKeyFile from ..crypto.file_integrity import FileIntegrityError from ..helpers import Location, get_security_dir @@ -2882,7 +2882,7 @@ def test_init_interrupt(self): def raise_eof(*args): raise EOFError - with patch.object(FlexiKeyBase, 'create', raise_eof): + with patch.object(FlexiKey, 'create', raise_eof): self.cmd('init', '--encryption=repokey', self.repository_location, exit_code=1) assert not os.path.exists(self.repository_location) From af26835dfc5ab52a0576f30664637dfd6da6ba1a Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 22 Mar 2022 14:24:17 +0100 Subject: [PATCH 32/34] delete pointless assert thanks @hexagonrecursion for finding this. --- src/borg/crypto/low_level.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index baac7c1baa..4e960ece38 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -288,7 +288,6 @@ cdef class AES256_CTR_BASE: """ cdef int ilen = len(envelope) cdef int hlen = self.header_len - assert hlen == self.header_len cdef int aoffset = self.aad_offset cdef int alen = hlen - aoffset cdef unsigned char *odata = PyMem_Malloc(ilen + self.cipher_blk_len) # play safe, 1 extra blk From 10cbdcc67edc06a828508eb6ffad34c422f1183e Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 22 Mar 2022 19:36:50 +0100 Subject: [PATCH 33/34] add encryption-aead diagram --- docs/internals/encryption-aead.odg | Bin 0 -> 23341 bytes docs/internals/encryption-aead.png | Bin 0 -> 159745 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/internals/encryption-aead.odg create mode 100644 docs/internals/encryption-aead.png diff --git a/docs/internals/encryption-aead.odg b/docs/internals/encryption-aead.odg new file mode 100644 index 0000000000000000000000000000000000000000..0f74fb428d9b00a63e61b31a1076c5e2664c4e1c GIT binary patch literal 23341 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 literal 0 HcmV?d00001 diff --git a/docs/internals/encryption-aead.png b/docs/internals/encryption-aead.png new file mode 100644 index 0000000000000000000000000000000000000000..b9eb2339b28ab42d3df6ee68348db4dde5baaddd GIT binary patch literal 159745 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∈4&?Ms< z9-(jdAAmEC7FCz`Aa47*8~gwvSR!b<)@25G&>4UV-Spn7ilvELo*UkW)WC)A04-mT4S)EYrpTpoTy6 zxrW26p`3*}U6ekthMiwrg`+rM-mlHRCM6 zmm9i%0rX;n?-(}&4WhzIyl~pl{nCYMSf)$CV407384dO=Ce&>T0x!}jk_{{*W{cTl zpKpcB^_zy0WTEwh|2I4O+t>e5Hvbv6|Hgv{$LW8G hS%Rd?zaOn`^@>-Lq{=ge#+#OloR7O7t8xms`ETJFdP4vJ literal 0 HcmV?d00001 From c66826528b9d6552dd1d10b46579ae33095d158a Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 22 Mar 2022 20:19:21 +0100 Subject: [PATCH 34/34] init olen to avoid some (false positive) compiler warnings olen is assigned by OpenSSL, but the compiler can't know that and generates these warnings: warning: src/borg/crypto/low_level.pyx:271:22: local variable 'olen' referenced before assignment warning: src/borg/crypto/low_level.pyx:274:22: local variable 'olen' referenced before assignment warning: src/borg/crypto/low_level.pyx:314:22: local variable 'olen' referenced before assignment warning: src/borg/crypto/low_level.pyx:317:22: local variable 'olen' referenced before assignment warning: src/borg/crypto/low_level.pyx:514:22: local variable 'olen' referenced before assignment warning: src/borg/crypto/low_level.pyx:517:22: local variable 'olen' referenced before assignment warning: src/borg/crypto/low_level.pyx:566:22: local variable 'olen' referenced before assignment warning: src/borg/crypto/low_level.pyx:572:22: local variable 'olen' referenced before assignment --- src/borg/crypto/low_level.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/borg/crypto/low_level.pyx b/src/borg/crypto/low_level.pyx index 4e960ece38..5692fa23c9 100644 --- a/src/borg/crypto/low_level.pyx +++ b/src/borg/crypto/low_level.pyx @@ -252,7 +252,7 @@ cdef class AES256_CTR_BASE: ilen + self.cipher_blk_len) # play safe, 1 extra blk if not odata: raise MemoryError - cdef int olen + cdef int olen = 0 cdef int offset cdef Py_buffer idata = ro_buffer(data) cdef Py_buffer hdata = ro_buffer(header) @@ -293,7 +293,7 @@ cdef class AES256_CTR_BASE: cdef unsigned char *odata = PyMem_Malloc(ilen + self.cipher_blk_len) # play safe, 1 extra blk if not odata: raise MemoryError - cdef int olen + cdef int olen = 0 cdef int offset cdef unsigned char mac_buf[32] assert sizeof(mac_buf) == self.mac_len @@ -488,7 +488,7 @@ cdef class _AEAD_BASE: ilen + self.cipher_blk_len) if not odata: raise MemoryError - cdef int olen + cdef int olen = 0 cdef int offset cdef Py_buffer idata = ro_buffer(data) cdef Py_buffer hdata = ro_buffer(header) @@ -543,7 +543,7 @@ cdef class _AEAD_BASE: cdef unsigned char *odata = PyMem_Malloc(ilen + self.cipher_blk_len) if not odata: raise MemoryError - cdef int olen + cdef int olen = 0 cdef int offset cdef Py_buffer idata = ro_buffer(envelope) cdef Py_buffer aadata = ro_buffer(aad)