Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion docs/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,22 @@ above.

New features:

- repo-create: split ``--encryption`` into orthogonal options. ``--encryption`` now
selects only the cipher / AE algorithm (``none``, ``authenticated``, ``aes256-ocb``
or ``chacha20-poly1305``), the new ``--id-hash`` selects the id hash function
(``sha256`` (default) or ``blake3``), and ``--key-location`` (already present) selects
the key storage. The old combined names were removed: select a BLAKE3 suite via
``--encryption ... --id-hash blake3`` instead of ``blake3-*``, and note that
``aes-ocb`` was renamed to ``aes256-ocb``. The JSON output (``--json``) reflects this
too: the ``encryption.mode`` field was replaced by separate ``encryption.encryption``
(cipher / AE algorithm) and ``encryption.id_hash`` fields. #9168
- key: unify keyfile/repokey key classes and locate the key independently of the
manifest key-type byte. Borg now tries keyfiles first and repokeys afterwards until
a passphrase unlocks a key, so where a key is stored (keyfile vs repokey) is a
per-key property rather than a separate key class. The key-type byte still selects
the crypto suite (id hash, MAC, cipher). ``borg repo-create --encryption`` now takes
only the crypto suite (e.g. ``aes-ocb``, ``chacha20-poly1305``, ``blake3-aes-ocb``);
only the crypto suite (e.g. ``aes256-ocb``, ``chacha20-poly1305``; see #9168 above for
the further split into ``--encryption`` and ``--id-hash``);
choose the storage location with the new ``--key-location=repokey|keyfile`` option
(default: ``repokey``). The old combined modes (``repokey-aes-ocb`` etc.) were
removed. ``borg key import`` also gained ``--key-location``. #9743
Expand Down
17 changes: 11 additions & 6 deletions docs/internals/frontends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,12 @@ last_modified

The *encryption* key, if present, contains:

mode
Textual encryption mode name (same as :ref:`borg_repo-create` ``--encryption`` names)
encryption
Textual cipher / AE algorithm name (same as :ref:`borg_repo-create` ``--encryption`` names)
id_hash
Textual id hash function name (same as :ref:`borg_repo-create` ``--id-hash`` names)
keyfile
Path to the local key file used for access. Depending on *mode* this key may be absent.
Path to the local key file used for access. Depending on the key location this may be absent.

The *cache* key, if present, contains:

Expand Down Expand Up @@ -334,7 +336,8 @@ Example *borg info* output::
}
},
"encryption": {
"mode": "repokey"
"encryption": "aes256-ocb",
"id_hash": "sha256"
},
"repository": {
"id": "0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23",
Expand Down Expand Up @@ -410,7 +413,8 @@ Example of a simple archive listing (``borg list --last 1 --json``)::
}
],
"encryption": {
"mode": "repokey"
"encryption": "aes256-ocb",
"id_hash": "sha256"
},
"repository": {
"id": "0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23",
Expand Down Expand Up @@ -463,7 +467,8 @@ The same archive with more information (``borg info --last 1 --json``)::
}
},
"encryption": {
"mode": "repokey"
"encryption": "aes256-ocb",
"id_hash": "sha256"
},
"repository": {
"id": "0cbe6166b46627fd26b97f8831e2ca97584280a46714ef84d2b668daf8271a23",
Expand Down
110 changes: 52 additions & 58 deletions docs/usage/repo-create.rst.inc

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/usage/repo-info.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Examples
$ borg repo-info
Repository ID: 0e85a7811022326c067acb2a7181d5b526b7d2f61b34470fb8670c440a67f1a9
Location: /Users/tw/w/borg/path/to/repo
Encrypted: Yes (repokey AES-OCB)
Encrypted: Yes (repokey, aes256-ocb, sha256)
Cache: /Users/tw/.cache/borg/0e85a7811022326c067acb2a7181d5b526b7d2f61b34470fb8670c440a67f1a9
Security dir: /Users/tw/.config/borg/security/0e85a7811022326c067acb2a7181d5b526b7d2f61b34470fb8670c440a67f1a9
Original size: 152.14 MB
Expand Down
10 changes: 5 additions & 5 deletions docs/usage/transfer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ locations and passphrases first:

# 1. Create a new "related" repository:
# Here, the existing Borg 1.x repository used repokey (and AES-CTR mode),
# thus we use aes-ocb for the new Borg 2.0 repository.
# thus we use aes256-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 chacha20-poly1305.
$ borg repo-create -e aes-ocb
$ borg repo-create -e aes256-ocb

# 2. Check what and how much it would transfer:
$ borg transfer --from-borg1 --dry-run
Expand All @@ -46,15 +46,15 @@ locations and passphrases first:

# 1. Create a new "related" repository:
# Here, the existing Borg 1.x repository used repokey-blake2 (and AES-CTR mode),
# thus we use blake3-aes-ocb for the new Borg 2.0 repository.
# thus we use aes256-ocb with --id-hash blake3 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 blake3-chacha20-poly1305.
$ borg repo-create -e blake3-aes-ocb
# re-authenticated anyway); you could also choose -e chacha20-poly1305 -i blake3.
$ borg repo-create -e aes256-ocb -i blake3
$ export CHUNKER_PARAMS="buzhash64,19,23,21,4095"

# 2. Check what and how much it would transfer:
Expand Down
87 changes: 45 additions & 42 deletions src/borg/archiver/repo_create_cmd.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from ._common import with_repository, with_other_repository, Highlander
from ..cache import Cache
from ..constants import * # NOQA
from ..crypto.key import key_creator, key_argument_names
from ..crypto.key import key_creator, encryption_argument_names, id_hash_argument_names
from ..helpers import CancelledByUser
from ..helpers import location_validator, Location
from ..helpers.argparsing import ArgumentParser
Expand Down Expand Up @@ -32,7 +32,7 @@ def do_repo_create(self, args, repository, *, other_repository=None, other_manif
manifest.write()
with Cache(repository, manifest, warn_if_unencrypted=False):
pass
if key.NAME != "plaintext":
if key.ENC_NAME != "none": # any key-bearing suite (everything except plaintext "none")
logger.warning(
"\n"
"IMPORTANT: you will need both KEY AND PASSPHRASE to access this repository!\n"
Expand Down Expand Up @@ -77,7 +77,7 @@ def build_parser_repo_create(self, subparsers, common_parser, mid_common_parser)

::

borg repo-create --encryption aes-ocb --key-location repokey
borg repo-create --encryption aes256-ocb --key-location repokey

Borg will:

Expand Down Expand Up @@ -119,58 +119,49 @@ def build_parser_repo_create(self, subparsers, common_parser, mid_common_parser)
You can change your passphrase for existing repositories at any time; it will not affect
the encryption/decryption key or other secrets.

Choosing an encryption mode
+++++++++++++++++++++++++++
Choosing a crypto suite
+++++++++++++++++++++++

Depending on your hardware, hashing and crypto performance may vary widely.
The easiest way to find out what is fastest is to run ``borg benchmark cpu``.

The encryption mode (``--encryption``) only selects the crypto suite (id hash, encryption
and authentication). Where the key is stored is chosen separately with ``--key-location``:
A crypto suite is selected by three orthogonal options:

``--encryption`` (**required**) selects the cipher / authenticated-encryption algorithm:

- ``aes256-ocb``: AES256 in OCB mode (encryption + authentication).
- ``chacha20-poly1305``: ChaCha20 + Poly1305 (encryption + authentication).
- ``authenticated``: no encryption, but still authenticates your data (tamper detection).
- ``none``: no encryption and no authentication (see the warning below).

``--id-hash`` selects the id hash function (used for chunk ids and authentication):

- ``sha256`` (default): HMAC-SHA-256 (or plain SHA-256 for the ``none`` encryption).
- ``blake3``: BLAKE3. Often faster on CPUs without SHA hardware acceleration.

The ``none`` encryption has no key, so it only supports the ``sha256`` id hash.

``--key-location`` selects where the key is stored (orthogonal to the crypto suite):

- ``repokey`` (default): the key is stored in the repository (under ``keys/``). Pick this
if you want ease-of-use and "passphrase" security is good enough.
- ``keyfile``: the key is stored in your home directory (in ``~/.config/borg/keys``). Pick
this if you want "passphrase and having-the-key" security.

You can move the key between these locations later with ``borg key change-location``.
This also applies to the ``authenticated*`` modes: they do not encrypt your data, but they
still have a key (used for the id hash and authentication), so ``--key-location`` selects
where that key is stored, just like for the encrypted modes.
``--key-location`` is only ignored for the ``none`` mode, which has no key at all.

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

+-----------------------------------+--------------+----------------+--------------------+
| Encryption mode | ID-Hash | Encryption | Authentication |
+-----------------------------------+--------------+----------------+--------------------+
| blake3-chacha20-poly1305 | BLAKE3 | CHACHA20 | POLY1305 |
+-----------------------------------+--------------+----------------+--------------------+
| chacha20-poly1305 | HMAC-SHA-256 | CHACHA20 | POLY1305 |
+-----------------------------------+--------------+----------------+--------------------+
| blake3-aes-ocb | BLAKE3 | AES256-OCB | AES256-OCB |
+-----------------------------------+--------------+----------------+--------------------+
| aes-ocb | HMAC-SHA-256 | AES256-OCB | AES256-OCB |
+-----------------------------------+--------------+----------------+--------------------+
| authenticated-blake3 | BLAKE3 | none | BLAKE3 |
+-----------------------------------+--------------+----------------+--------------------+
| authenticated | HMAC-SHA-256 | none | HMAC-SHA256 |
+-----------------------------------+--------------+----------------+--------------------+
| none | SHA-256 | none | none |
+-----------------------------------+--------------+----------------+--------------------+

.. nanorst: inline-replace

`none` mode uses no encryption and no authentication. You are advised NOT to use this mode
This also applies to the ``authenticated`` encryption: it does not encrypt your data, but it
still has a key (used for the id hash and authentication), so ``--key-location`` selects
where that key is stored, just like for the encrypted suites.
``--key-location`` is only ignored for the ``none`` encryption, which has no key at all.

`none` encryption uses no encryption and no authentication. You are advised NOT to use this
as it would expose you to a Denial-of-Service risk (due to how the :ref:`internals_hashindex`
works) and other issues (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 is like `repokey` minus encryption.
malicious tampering, use ``--encryption authenticated``. It is like an encrypted suite
minus the data encryption.
To normally work with ``authenticated`` repositories, you will need the passphrase, but
there is an emergency workaround; see ``BORG_WORKAROUNDS=authenticated_no_key`` docs.

Expand Down Expand Up @@ -217,12 +208,24 @@ def build_parser_repo_create(self, subparsers, common_parser, mid_common_parser)
subparser.add_argument(
"-e",
"--encryption",
metavar="MODE",
metavar="ENCRYPTION",
dest="encryption",
required=True,
choices=key_argument_names(),
choices=encryption_argument_names(),
action=Highlander,
help="select cipher / AE algorithm: 'none', 'authenticated', 'aes256-ocb' or "
"'chacha20-poly1305' **(required)**",
)
subparser.add_argument(
"-i",
"--id-hash",
metavar="HASH",
dest="id_hash",
choices=id_hash_argument_names(),
default="sha256",
action=Highlander,
help="select encryption crypto suite **(required)**",
help="select the id hash function: 'sha256' (default) or 'blake3'. "
"The 'none' encryption only supports 'sha256'.",
)
subparser.add_argument(
"--key-location",
Expand Down
15 changes: 9 additions & 6 deletions src/borg/archiver/repo_info_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@ def do_repo_info(self, args, repository, manifest, cache):
json_print(info)
else:
encryption = "Encrypted: "
# storage (keyfile/repokey) is a per-key property now; the crypto suite is key.NAME.
# storage (keyfile/repokey) is a per-key property now; the crypto suite is described by
# the two dimensions: cipher / AE algorithm (ENC_NAME) and id hash function (IDHASH_NAME).
storage = getattr(key, "storage", None)
mode = {KeyBlobStorage.KEYFILE: "keyfile", KeyBlobStorage.REPO: "repokey"}.get(storage)
if key.NAME in ("plaintext", "authenticated", "authenticated BLAKE3"):
# authenticated modes do not encrypt data, but (unlike plaintext) still have a key
# that is stored as a keyfile or repokey, so show that location when there is one.
encryption += "No (%s, %s)" % (mode, key.NAME) if mode else "No"
suite = "%s, %s" % (key.ENC_NAME, key.IDHASH_NAME)
if key.ENC_NAME in ("none", "authenticated"):
# the "none" and "authenticated" encryptions do not encrypt data; "authenticated"
# (unlike "none"/plaintext) still has a key stored as a keyfile or repokey, so show
# that location when there is one.
encryption += "No (%s, %s)" % (mode, suite) if mode else "No"
else:
encryption += "Yes (%s, %s)" % (mode, key.NAME) if mode else "Yes (%s)" % key.NAME
encryption += "Yes (%s, %s)" % (mode, suite) if mode else "Yes (%s)" % suite
if storage == KeyBlobStorage.KEYFILE:
encryption += "\nKey file: %s" % key.find_key()
info["encryption"] = encryption
Expand Down
Loading
Loading