diff --git a/docs/usage/transfer.rst b/docs/usage/transfer.rst index fe186e69b..8632cf9d0 100644 --- a/docs/usage/transfer.rst +++ b/docs/usage/transfer.rst @@ -2,33 +2,69 @@ Examples ~~~~~~~~ + +To keep the following examples short and readable, we export the repository +locations and passphrases first: + :: - # 0. Have Borg 2.0 installed on the client AND server; have a b12 repository copy for testing. + export BORG_REPO=ssh://borg2@borgbackup/./tests/b20 + export BORG_PASSPHRASE='your-borg2-repo-passphrase' + export BORG_OTHER_REPO=ssh://borg2@borgbackup/./tests/b1x + export BORG_OTHER_PASSPHRASE='your-borg1-repo-passphrase' + +:: + + # borg 1.x repo -> borg 2.0 repo (hmac-sha256 -> hmac-sha256, keeping same chunk ID algorithm) + + # 0. Have Borg 2.0 installed on the client AND server; have a b1x repository copy for testing. # 1. Create a new "related" repository: - # Here, the existing Borg 1.2 repository used repokey-blake2 (and AES-CTR mode), - # thus we use repokey-blake2-aes-ocb for the new Borg 2.0 repository. - # Staying with the same chunk ID algorithm (BLAKE2) and with the same - # key material (via --other-repo ) will make deduplication work + # Here, the existing Borg 1.x repository used repokey (and AES-CTR mode), + # thus we use repokey-aes-ocb for the new Borg 2.0 repository. + # Staying with the same chunk ID algorithm (hmac-sha256) and with the same + # key material (via BORG_OTHER_REPO) will make deduplication work # between old archives (copied with borg transfer) and future ones. # The AEAD cipher does not matter (everything must be re-encrypted and - # re-authenticated anyway); you could also choose repokey-blake2-chacha20-poly1305. - # In case your old Borg repository did not use BLAKE2, just remove the "-blake2". - $ borg --repo ssh://borg2@borgbackup/./tests/b20 repo-create \ - --other-repo ssh://borg2@borgbackup/./tests/b12 -e repokey-blake2-aes-ocb + # re-authenticated anyway); you could also choose repokey-chacha20-poly1305. + $ borg repo-create -e repokey-aes-ocb # 2. Check what and how much it would transfer: - $ borg --repo ssh://borg2@borgbackup/./tests/b20 transfer --upgrader=From12To20 \ - --other-repo ssh://borg2@borgbackup/./tests/b12 --dry-run + $ borg transfer --from-borg1 --dry-run # 3. Transfer (copy) archives from the old repository into the new repository (takes time and space!): - $ borg --repo ssh://borg2@borgbackup/./tests/b20 transfer --upgrader=From12To20 \ - --other-repo ssh://borg2@borgbackup/./tests/b12 + $ borg transfer --from-borg1 # 4. Check whether we have everything (same as step 2): - $ borg --repo ssh://borg2@borgbackup/./tests/b20 transfer --upgrader=From12To20 \ - --other-repo ssh://borg2@borgbackup/./tests/b12 --dry-run + $ borg transfer --from-borg1 --dry-run + +:: + + # borg 1.x repo -> borg 2.0 repo (blake2 -> blake3, changing chunk ID algorithm) + + # 0. Have Borg 2.0 installed on the client AND server; have a b1x repository copy for testing. + + # 1. Create a new "related" repository: + # Here, the existing Borg 1.x repository used repokey-blake2 (and AES-CTR mode), + # thus we use repokey-blake3-aes-ocb for the new Borg 2.0 repository. + # We need to change from blake2 to blake3, because blake2 is not supported + # for borg2 repos (blake3 is much faster). Because we change how chunk IDs are + # computed, we need to re-chunk everything while doing the transfer. + # The chunker parameters you provide here should be the same as you will + # use for all future Borg 2.0 archives. + # The AEAD cipher does not matter (everything must be re-encrypted and + # re-authenticated anyway); you could also choose repokey-blake3-chacha20-poly1305. + $ borg repo-create -e repokey-blake3-aes-ocb + $ export CHUNKER_PARAMS="buzhash64,19,23,21,4095" + + # 2. Check what and how much it would transfer: + $ borg transfer --from-borg1 --chunker-params=$CHUNKER_PARAMS --dry-run + + # 3. Transfer (copy) archives from the old repository into the new repository (takes time and space!): + $ borg transfer --from-borg1 --chunker-params=$CHUNKER_PARAMS + + # 4. Check whether we have everything (same as step 2): + $ borg transfer --from-borg1 --chunker-params=$CHUNKER_PARAMS --dry-run Keyfile considerations when upgrading from borg 1.x ++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/src/borg/archiver/key_cmds.py b/src/borg/archiver/key_cmds.py index 2eac652c9..10d8e3dd7 100644 --- a/src/borg/archiver/key_cmds.py +++ b/src/borg/archiver/key_cmds.py @@ -1,8 +1,8 @@ import os from ..constants import * # NOQA -from ..crypto.key import AESOCBRepoKey, CHPORepoKey, Blake2AESOCBRepoKey, Blake2CHPORepoKey -from ..crypto.key import AESOCBKeyfileKey, CHPOKeyfileKey, Blake2AESOCBKeyfileKey, Blake2CHPOKeyfileKey +from ..crypto.key import AESOCBRepoKey, CHPORepoKey, Blake3AESOCBRepoKey, Blake3CHPORepoKey +from ..crypto.key import AESOCBKeyfileKey, CHPOKeyfileKey, Blake3AESOCBKeyfileKey, Blake3CHPOKeyfileKey from ..crypto.keymanager import KeyManager from ..helpers import PathSpec, CommandError from ..helpers.argparsing import ArgumentParser @@ -40,10 +40,10 @@ class KeysMixIn: key_new = AESOCBKeyfileKey(repository) elif isinstance(key, CHPORepoKey): key_new = CHPOKeyfileKey(repository) - elif isinstance(key, Blake2AESOCBRepoKey): - key_new = Blake2AESOCBKeyfileKey(repository) - elif isinstance(key, Blake2CHPORepoKey): - key_new = Blake2CHPOKeyfileKey(repository) + elif isinstance(key, Blake3AESOCBRepoKey): + key_new = Blake3AESOCBKeyfileKey(repository) + elif isinstance(key, Blake3CHPORepoKey): + key_new = Blake3CHPOKeyfileKey(repository) else: print("Change not needed or not supported.") return @@ -52,10 +52,10 @@ class KeysMixIn: key_new = AESOCBRepoKey(repository) elif isinstance(key, CHPOKeyfileKey): key_new = CHPORepoKey(repository) - elif isinstance(key, Blake2AESOCBKeyfileKey): - key_new = Blake2AESOCBRepoKey(repository) - elif isinstance(key, Blake2CHPOKeyfileKey): - key_new = Blake2CHPORepoKey(repository) + elif isinstance(key, Blake3AESOCBKeyfileKey): + key_new = Blake3AESOCBRepoKey(repository) + elif isinstance(key, Blake3CHPOKeyfileKey): + key_new = Blake3CHPORepoKey(repository) else: print("Change not needed or not supported.") return diff --git a/src/borg/archiver/transfer_cmd.py b/src/borg/archiver/transfer_cmd.py index 0a8bf415e..b12d5c340 100644 --- a/src/borg/archiver/transfer_cmd.py +++ b/src/borg/archiver/transfer_cmd.py @@ -135,12 +135,11 @@ class TransferMixIn: """archives transfer from other repository, optionally upgrade data format""" key = manifest.key other_key = other_manifest.key - if not uses_same_id_hash(other_key, key): - raise Error( - "You must keep the same ID hash ([HMAC-]SHA256 or BLAKE2b) or deduplication will break. " - "Use a related repository!" - ) - if not uses_same_chunker_secret(other_key, key): + using_same_id_hash = uses_same_id_hash(other_key, key) + rechunking = args.chunker_params is not None + if not using_same_id_hash and not rechunking: + raise Error("You must either keep the same ID hash or use --chunker-params.") + if not rechunking and not uses_same_chunker_secret(other_key, key): raise Error( "You must use the same chunker secret or deduplication will break. " "Use a related repository!" ) diff --git a/src/borg/constants.py b/src/borg/constants.py index 487a4ff96..6ea8e5b4c 100644 --- a/src/borg/constants.py +++ b/src/borg/constants.py @@ -189,10 +189,11 @@ class KeyType: AESOCBREPO = 0x11 CHPOKEYFILE = 0x20 CHPOREPO = 0x21 - BLAKE2AESOCBKEYFILE = 0x30 - BLAKE2AESOCBREPO = 0x31 - BLAKE2CHPOKEYFILE = 0x40 - BLAKE2CHPOREPO = 0x41 + BLAKE3AESOCBKEYFILE = 0x30 + BLAKE3AESOCBREPO = 0x31 + BLAKE3CHPOKEYFILE = 0x40 + BLAKE3CHPOREPO = 0x41 + BLAKE3AUTHENTICATED = 0x50 CACHE_TAG_NAME = "CACHEDIR.TAG" diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index 4456a6503..ff31830d8 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -11,6 +11,7 @@ from ..logger import create_logger logger = create_logger() +from blake3 import blake3 import argon2.low_level from ..constants import * # NOQA @@ -28,7 +29,7 @@ from ..platform import SaveFile from ..repoobj import RepoObj -from .low_level import bytes_to_int, num_cipher_blocks, hmac_sha256, blake2b_256 +from .low_level import bytes_to_int, num_cipher_blocks, hmac_sha256 from .low_level import AES256_OCB, CHACHA20_POLY1305 from . import low_level @@ -70,7 +71,7 @@ def keyfile_parse(data: str | bytes, repoid: Optional[str] = None) -> tuple[str, return repoid, b64data -# workaround for lost passphrase or key in "authenticated" or "authenticated-blake2" mode +# workaround for lost passphrase or key in "authenticated*" modes AUTHENTICATED_NO_KEY = "authenticated_no_key" in workarounds @@ -161,19 +162,19 @@ def uses_same_id_hash(other_key, key): new_sha256_ids = (PlaintextKey,) old_hmac_sha256_ids = (RepoKey, KeyfileKey, AuthenticatedKey) new_hmac_sha256_ids = (AESOCBRepoKey, AESOCBKeyfileKey, CHPORepoKey, CHPOKeyfileKey, AuthenticatedKey) - old_blake2_ids = (Blake2RepoKey, Blake2KeyfileKey, Blake2AuthenticatedKey) - new_blake2_ids = ( - Blake2AESOCBRepoKey, - Blake2AESOCBKeyfileKey, - Blake2CHPORepoKey, - Blake2CHPOKeyfileKey, - Blake2AuthenticatedKey, + # note: we do not support blake2b for new repos, see #8867 + new_blake3_ids = ( + Blake3AESOCBRepoKey, + Blake3AESOCBKeyfileKey, + Blake3CHPORepoKey, + Blake3CHPOKeyfileKey, + Blake3AuthenticatedKey, ) same_ids = ( isinstance(other_key, old_hmac_sha256_ids + new_hmac_sha256_ids) and isinstance(key, new_hmac_sha256_ids) - or isinstance(other_key, old_blake2_ids + new_blake2_ids) - and isinstance(key, new_blake2_ids) + or isinstance(other_key, new_blake3_ids) + and isinstance(key, new_blake3_ids) or isinstance(other_key, old_sha256_ids + new_sha256_ids) and isinstance(key, new_sha256_ids) ) @@ -314,38 +315,6 @@ class PlaintextKey(KeyBase): return memoryview(data)[1:] -def random_blake2b_256_key(): - # This might look a bit curious, but is the same construction used in the keyed mode of BLAKE2b. - # Why limit the key to 64 bytes and pad it with 64 nulls nonetheless? The answer is that BLAKE2b - # has a 128 byte block size, but only 64 bytes of internal state (this is also referred to as a - # "local wide pipe" design, because the compression function transforms (block, state) => state, - # and len(block) >= len(state), hence wide.) - # In other words, a key longer than 64 bytes would have simply no advantage, since the function - # has no way of propagating more than 64 bytes of entropy internally. - # It's padded to a full block so that the key is never buffered internally by blake2b_update, ie. - # it remains in a single memory location that can be tracked and could be erased securely, if we - # wanted to. - return os.urandom(64) + bytes(64) - - -class ID_BLAKE2b_256: - """ - Key mix-in class for using BLAKE2b-256 for the id key. - - The id_key length must be 32 bytes. - """ - - def id_hash(self, data): - return blake2b_256(self.id_key, data) - - def init_from_random_data(self): - super().init_from_random_data() - enc_key = os.urandom(32) - enc_hmac_key = random_blake2b_256_key() - self.crypt_key = enc_key + enc_hmac_key - self.id_key = random_blake2b_256_key() - - class ID_HMAC_SHA_256: """ Key mix-in class for using HMAC-SHA-256 for the id key. @@ -570,15 +539,18 @@ class FlexiKey: if isinstance(key, AESKeyBase): # user must use an AEADKeyBase subclass (AEAD modes with session keys) raise Error("Copying key material to an AES-CTR based mode is insecure and unsupported.") - if not uses_same_id_hash(other_key, key): - raise Error("You must keep the same ID hash (HMAC-SHA256 or BLAKE2b) or deduplication will break.") if other_key.copy_crypt_key: # give the user the option to use the same authenticated encryption (AE) key crypt_key = other_key.crypt_key else: # borg transfer re-encrypts all data anyway, thus we can default to a new, random AE key crypt_key = os.urandom(64) - key.init_from_given_data(crypt_key=crypt_key, id_key=other_key.id_key, chunk_seed=other_key.chunk_seed) + if len(other_key.id_key) == 128: # blake2b id key from borg 1.x is not supported anymore + id_key = os.urandom(32) # hmac-sha256 and blake3 use 32 bytes + else: + id_key = other_key.id_key + chunk_seed = other_key.chunk_seed + key.init_from_given_data(crypt_key=crypt_key, id_key=id_key, chunk_seed=chunk_seed) else: key.init_from_random_data() passphrase = Passphrase.new(allow_empty=True) @@ -737,12 +709,6 @@ class FlexiKey: raise TypeError("Unsupported borg key storage type") -# legacy imports placed after FlexiKey/AESKeyBase/KeyBase so those names are already -# in the partial module when legacy/crypto/key.py imports them back during circular load -from ..legacy.crypto.key import KeyfileKey, RepoKey, Blake2KeyfileKey, Blake2RepoKey # noqa: E402 -from ..legacy.crypto.key import LEGACY_KEY_TYPES # noqa: E402 - - class AuthenticatedKeyBase(AESKeyBase, FlexiKey): STORAGE = KeyBlobStorage.REPO @@ -782,6 +748,14 @@ class AuthenticatedKeyBase(AESKeyBase, FlexiKey): return memoryview(data)[1:] +# legacy imports placed after FlexiKey/AESKeyBase/KeyBase/AuthenticatedKeyBase so those names are already +# in the partial module when legacy/crypto/key.py imports them back during circular load +from ..legacy.crypto.key import KeyfileKey, RepoKey +from ..legacy.crypto.key import Blake2KeyfileKey, Blake2RepoKey, Blake2AuthenticatedKey # noqa: F401 +from ..legacy.crypto.key import LEGACY_KEY_TYPES # noqa: E402 +from ..legacy.crypto.key import ID_BLAKE2b_256 # noqa: F401 + + class AuthenticatedKey(ID_HMAC_SHA_256, AuthenticatedKeyBase): TYPE = KeyType.AUTHENTICATED TYPES_ACCEPTABLE = {TYPE} @@ -789,16 +763,27 @@ class AuthenticatedKey(ID_HMAC_SHA_256, AuthenticatedKeyBase): ARG_NAME = "authenticated" -class Blake2AuthenticatedKey(ID_BLAKE2b_256, AuthenticatedKeyBase): - TYPE = KeyType.BLAKE2AUTHENTICATED - TYPES_ACCEPTABLE = {TYPE} - NAME = "authenticated BLAKE2b" - ARG_NAME = "authenticated-blake2" - - # ------------ new crypto ------------ +class ID_BLAKE3_256: + """ + Key mix-in class for using BLAKE3 for the id key. + + The id_key length must be 32 bytes. + """ + + def id_hash(self, data): + return blake3(data, key=self.id_key).digest(length=32) + + +class Blake3AuthenticatedKey(ID_BLAKE3_256, AuthenticatedKeyBase): + TYPE = KeyType.BLAKE3AUTHENTICATED + TYPES_ACCEPTABLE = {TYPE} + NAME = "authenticated BLAKE3" + ARG_NAME = "authenticated-blake3" + + class AEADKeyBase(KeyBase): """ Chunks are encrypted and authenticated using some AEAD ciphersuite @@ -944,38 +929,38 @@ class CHPORepoKey(ID_HMAC_SHA_256, AEADKeyBase, FlexiKey): 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" +class Blake3AESOCBKeyfileKey(ID_BLAKE3_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.BLAKE3AESOCBKEYFILE, KeyType.BLAKE3AESOCBREPO} + TYPE = KeyType.BLAKE3AESOCBKEYFILE + NAME = "key file BLAKE3 AES-OCB" + ARG_NAME = "keyfile-blake3-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" +class Blake3AESOCBRepoKey(ID_BLAKE3_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.BLAKE3AESOCBKEYFILE, KeyType.BLAKE3AESOCBREPO} + TYPE = KeyType.BLAKE3AESOCBREPO + NAME = "repokey BLAKE3 AES-OCB" + ARG_NAME = "repokey-blake3-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" +class Blake3CHPOKeyfileKey(ID_BLAKE3_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.BLAKE3CHPOKEYFILE, KeyType.BLAKE3CHPOREPO} + TYPE = KeyType.BLAKE3CHPOKEYFILE + NAME = "key file BLAKE3 ChaCha20-Poly1305" + ARG_NAME = "keyfile-blake3-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" +class Blake3CHPORepoKey(ID_BLAKE3_256, AEADKeyBase, FlexiKey): + TYPES_ACCEPTABLE = {KeyType.BLAKE3CHPOKEYFILE, KeyType.BLAKE3CHPOREPO} + TYPE = KeyType.BLAKE3CHPOREPO + NAME = "repokey BLAKE3 ChaCha20-Poly1305" + ARG_NAME = "repokey-blake3-chacha20-poly1305" STORAGE = KeyBlobStorage.REPO CIPHERSUITE = CHACHA20_POLY1305 @@ -985,14 +970,14 @@ AVAILABLE_KEY_TYPES = ( # not encrypted modes PlaintextKey, AuthenticatedKey, - Blake2AuthenticatedKey, # new crypto + Blake3AuthenticatedKey, AESOCBKeyfileKey, AESOCBRepoKey, CHPOKeyfileKey, CHPORepoKey, - Blake2AESOCBKeyfileKey, - Blake2AESOCBRepoKey, - Blake2CHPOKeyfileKey, - Blake2CHPORepoKey, + Blake3AESOCBKeyfileKey, + Blake3AESOCBRepoKey, + Blake3CHPOKeyfileKey, + Blake3CHPORepoKey, ) diff --git a/src/borg/legacy/crypto/key.py b/src/borg/legacy/crypto/key.py index 27e4c7141..a6c624fde 100644 --- a/src/borg/legacy/crypto/key.py +++ b/src/borg/legacy/crypto/key.py @@ -3,8 +3,8 @@ import os from hashlib import pbkdf2_hmac from ...constants import * # NOQA -from ...crypto.low_level import AES256_CTR_HMAC_SHA256, AES256_CTR_BLAKE2b, hmac_sha256 -from ...crypto.key import ID_HMAC_SHA_256, ID_BLAKE2b_256, AESKeyBase, FlexiKey, UnsupportedKeyFormatError +from ...crypto.low_level import AES256_CTR_HMAC_SHA256, AES256_CTR_BLAKE2b, hmac_sha256, blake2b_256 +from ...crypto.key import ID_HMAC_SHA_256, AESKeyBase, FlexiKey, AuthenticatedKeyBase, UnsupportedKeyFormatError from ...helpers import get_limited_unpacker, msgpack from ...item import EncryptedKey from .low_level import AES @@ -53,6 +53,45 @@ class Pbkdf2FileMixin: return msgpack.packb(enc_key.as_dict()) +def random_blake2b_256_key(): + # This might look a bit curious, but is the same construction used in the keyed mode of BLAKE2b. + # Why limit the key to 64 bytes and pad it with 64 nulls nonetheless? The answer is that BLAKE2b + # has a 128 byte block size, but only 64 bytes of internal state (this is also referred to as a + # "local wide pipe" design, because the compression function transforms (block, state) => state, + # and len(block) >= len(state), hence wide.) + # In other words, a key longer than 64 bytes would have simply no advantage, since the function + # has no way of propagating more than 64 bytes of entropy internally. + # It's padded to a full block so that the key is never buffered internally by blake2b_update, ie. + # it remains in a single memory location that can be tracked and could be erased securely, if we + # wanted to. + return os.urandom(64) + bytes(64) + + +class ID_BLAKE2b_256: + """ + Key mix-in class for using BLAKE2b-256 for the id key. + + The id_key length must be 32 bytes. + """ + + def id_hash(self, data): + return blake2b_256(self.id_key, data) + + def init_from_random_data(self): + super().init_from_random_data() + enc_key = os.urandom(32) + enc_hmac_key = random_blake2b_256_key() + self.crypt_key = enc_key + enc_hmac_key + self.id_key = random_blake2b_256_key() + + +class Blake2AuthenticatedKey(ID_BLAKE2b_256, AuthenticatedKeyBase): # type: ignore[misc] + TYPE = KeyType.BLAKE2AUTHENTICATED + TYPES_ACCEPTABLE = {TYPE} + NAME = "authenticated BLAKE2b" + ARG_NAME = "authenticated-blake2" + + class KeyfileKey(Pbkdf2FileMixin, ID_HMAC_SHA_256, AESKeyBase, FlexiKey): # type: ignore[misc] TYPES_ACCEPTABLE = {KeyType.KEYFILE, KeyType.REPO, KeyType.PASSPHRASE} TYPE = KeyType.KEYFILE @@ -89,4 +128,4 @@ class Blake2RepoKey(Pbkdf2FileMixin, ID_BLAKE2b_256, AESKeyBase, FlexiKey): # t CIPHERSUITE = AES256_CTR_BLAKE2b -LEGACY_KEY_TYPES = (KeyfileKey, RepoKey, Blake2KeyfileKey, Blake2RepoKey) +LEGACY_KEY_TYPES = (KeyfileKey, RepoKey, Blake2KeyfileKey, Blake2RepoKey, Blake2AuthenticatedKey) diff --git a/src/borg/testsuite/archiver/key_cmds_test.py b/src/borg/testsuite/archiver/key_cmds_test.py index 9409adbfa..9944fbe0d 100644 --- a/src/borg/testsuite/archiver/key_cmds_test.py +++ b/src/borg/testsuite/archiver/key_cmds_test.py @@ -37,14 +37,14 @@ def test_change_location_to_keyfile(archivers, request): assert "(key file" in log -def test_change_location_to_b2keyfile(archivers, request): +def test_change_location_to_b3keyfile(archivers, request): archiver = request.getfixturevalue(archivers) - cmd(archiver, "repo-create", "--encryption=repokey-blake2-aes-ocb") + cmd(archiver, "repo-create", "--encryption=repokey-blake3-aes-ocb") log = cmd(archiver, "repo-info") - assert "(repokey BLAKE2b" in log + assert "(repokey BLAKE3" in log cmd(archiver, "key", "change-location", "keyfile") log = cmd(archiver, "repo-info") - assert "(key file BLAKE2b" in log + assert "(key file BLAKE3" in log def test_change_location_to_repokey(archivers, request): @@ -57,14 +57,14 @@ def test_change_location_to_repokey(archivers, request): assert "(repokey" in log -def test_change_location_to_b2repokey(archivers, request): +def test_change_location_to_b3repokey(archivers, request): archiver = request.getfixturevalue(archivers) - cmd(archiver, "repo-create", "--encryption=keyfile-blake2-aes-ocb") + cmd(archiver, "repo-create", "--encryption=keyfile-blake3-aes-ocb") log = cmd(archiver, "repo-info") - assert "(key file BLAKE2b" in log + assert "(key file BLAKE3" in log cmd(archiver, "key", "change-location", "repokey") log = cmd(archiver, "repo-info") - assert "(repokey BLAKE2b" in log + assert "(repokey BLAKE3" in log def test_keyfile_name_is_content_sha256(archivers, request): diff --git a/src/borg/testsuite/crypto/key_test.py b/src/borg/testsuite/crypto/key_test.py index 60bfd0bc6..67592d6c2 100644 --- a/src/borg/testsuite/crypto/key_test.py +++ b/src/borg/testsuite/crypto/key_test.py @@ -9,8 +9,9 @@ from ...crypto.key import PlaintextKey, AuthenticatedKey, Blake2AuthenticatedKey from ...crypto.key import RepoKey, KeyfileKey, Blake2RepoKey, Blake2KeyfileKey from ...crypto.key import AEADKeyBase from ...crypto.key import AESOCBRepoKey, AESOCBKeyfileKey, CHPORepoKey, CHPOKeyfileKey -from ...crypto.key import Blake2AESOCBRepoKey, Blake2AESOCBKeyfileKey, Blake2CHPORepoKey, Blake2CHPOKeyfileKey -from ...crypto.key import ID_HMAC_SHA_256, ID_BLAKE2b_256 +from ...crypto.key import Blake3AESOCBRepoKey, Blake3AESOCBKeyfileKey, Blake3CHPORepoKey, Blake3CHPOKeyfileKey +from ...crypto.key import Blake3AuthenticatedKey +from ...crypto.key import ID_HMAC_SHA_256, ID_BLAKE2b_256, ID_BLAKE3_256 from ...crypto.key import UnsupportedManifestError, UnsupportedKeyFormatError from ...crypto.key import identify_key from ...crypto.low_level import IntegrityError as IntegrityErrorBase @@ -77,21 +78,22 @@ class TestKey: # not encrypted PlaintextKey, AuthenticatedKey, - Blake2AuthenticatedKey, + Blake3AuthenticatedKey, # legacy crypto KeyfileKey, Blake2KeyfileKey, RepoKey, Blake2RepoKey, + Blake2AuthenticatedKey, # new crypto AESOCBKeyfileKey, AESOCBRepoKey, - Blake2AESOCBKeyfileKey, - Blake2AESOCBRepoKey, + Blake3AESOCBKeyfileKey, + Blake3AESOCBRepoKey, CHPOKeyfileKey, CHPORepoKey, - Blake2CHPOKeyfileKey, - Blake2CHPORepoKey, + Blake3CHPOKeyfileKey, + Blake3CHPORepoKey, ) ) def key(self, request, monkeypatch): @@ -266,6 +268,17 @@ class TestKey: # 0x06 is the key TYPE. assert authenticated == b"\x06" + plaintext + def test_blake3_authenticated_encrypt(self, monkeypatch): + monkeypatch.setenv("BORG_PASSPHRASE", "test") + key = Blake3AuthenticatedKey.create(self.MockRepository(), self.MockArgs()) + assert Blake3AuthenticatedKey.id_hash is ID_BLAKE3_256.id_hash + assert len(key.id_key) == 32 + plaintext = b"123456789" + id = key.id_hash(plaintext) + authenticated = key.encrypt(id, plaintext) + # 0x50 is the key TYPE. + assert authenticated == b"\x50" + plaintext + class TestTAM: @pytest.fixture