diff --git a/docs/internals/security.rst b/docs/internals/security.rst index 3f79e66bd7..70f3badab6 100644 --- a/docs/internals/security.rst +++ b/docs/internals/security.rst @@ -124,12 +124,93 @@ 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 (see section "Encryption" below). +- 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. Every operation (encryption, MAC / authentication, chunk ID derivation) -uses independent, random keys generated by `os.urandom`_ [#]_. +uses independent, random keys generated by `os.urandom`_. Borg does not support unauthenticated encryption -- only authenticated encryption schemes are supported. No unauthenticated encryption schemes will be added @@ -137,19 +218,22 @@ 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. - The authentication primitive is either HMAC-SHA-256 or BLAKE2b-256 - in a keyed mode. HMAC-SHA-256 uses 256 bit keys, while BLAKE2b-256 - uses 512 bit keys. - - The latter is secure not only because BLAKE2b itself is not - susceptible to `length extension`_, but also since it truncates the - hash output from 512 bits to 256 bits, which would make the - construction safe even if BLAKE2b were broken regarding length - extension or similar attacks. + in a keyed mode. + + Both HMAC-SHA-256 and BLAKE2b have undergone extensive cryptanalysis + and have proven secure against known attacks. The known vulnerability + of SHA-256 against length extension attacks does not apply to HMAC-SHA-256. + + The authentication primitive should be chosen based upon SHA hardware support: + all AMD Ryzen, Intel 10th+ generation mobile and Intel 11th+ generation + desktop processors, Apple M1+ and most current ARM64 architectures support + SHA extensions and are likely to perform best with HMAC-SHA-256. + 64-bit CPUs without SHA extensions are likely to perform best with BLAKE2b. - The primitive used for authentication is always the same primitive that is used for deriving the chunk ID, but they are always @@ -208,13 +292,6 @@ untrusted, but a trusted synchronization channel exists between clients, the security database could be synchronized between them over said trusted channel. This is not part of Borg's functionality. -.. [#] Using the :ref:`borg key migrate-to-repokey ` - command a user can convert repositories created using Attic in "passphrase" - mode to "repokey" mode. In this case the keys were directly derived from - the user's passphrase at some point using PBKDF2. - - Borg does not support "passphrase" mode otherwise any more. - .. _key_encryption: Offline key security @@ -260,7 +337,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 @@ -275,7 +352,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`_.