diff --git a/src/borg/archive.py b/src/borg/archive.py index 728c76bb1..833616661 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -1789,7 +1789,7 @@ class ArchiveChecker: 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 @@ class ArchiveChecker: 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 @@ class ArchiveChecker: 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 92df82e11..4e6de052a 100644 --- a/src/borg/cache.py +++ b/src/borg/cache.py @@ -942,7 +942,7 @@ class LocalCache(CacheStatsMixin): 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 @@ Chunk index: {0.total_unique_chunks:20d} unknown""" 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 b8e240766..e6e0b252a 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -158,7 +158,7 @@ class KeyBase: """ raise NotImplementedError - def encrypt(self, chunk): + def encrypt(self, id, data): pass def decrypt(self, id, data, decompress=True): @@ -264,8 +264,8 @@ class PlaintextKey(KeyBase): 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 @@ class AuthenticatedKeyBase(AESKeyBase, FlexiKey): 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 84871f736..dc0580f28 100644 --- a/src/borg/helpers/manifest.py +++ b/src/borg/helpers/manifest.py @@ -261,4 +261,4 @@ class Manifest: 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 2eb552bd4..88181c31e 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -3806,7 +3806,7 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase): '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 @@ class ManifestAuthenticationTest(ArchiverTestCaseBase): 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 @@ class ManifestAuthenticationTest(ArchiverTestCaseBase): 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 1c86f5207..14297d480 100644 --- a/src/borg/testsuite/key.py +++ b/src/borg/testsuite/key.py @@ -114,18 +114,21 @@ class TestKey: 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 @@ class TestKey: 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 @@ class TestKey: 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 @@ class TestKey: 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 @@ class TestKey: 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 @@ class TestKey: 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 @@ class TestKey: 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 034ebb4ba..fe94dc414 100644 --- a/src/borg/testsuite/remote.py +++ b/src/borg/testsuite/remote.py @@ -165,7 +165,7 @@ class TestRepositoryCache: 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