diff --git a/src/borg/archiver.py b/src/borg/archiver.py index 54215799d..d2a745621 100644 --- a/src/borg/archiver.py +++ b/src/borg/archiver.py @@ -622,12 +622,11 @@ class Archiver: for spec, func in tests: print(f"{spec:<24} {size:<10} {timeit(func, number=100):.3f}s") - from borg.helpers.passphrase import Passphrase print("KDFs (slow is GOOD, use argon2!) ===============================") count = 5 for spec, func in [ - ("pbkdf2", lambda: FlexiKey.pbkdf2(Passphrase('mypassphrase'), b'salt'*8, PBKDF2_ITERATIONS, 32)), - ("argon2", lambda: Passphrase('mypassphrase').argon2(64, b'S' * ARGON2_SALT_BYTES, **ARGON2_ARGS)), + ("pbkdf2", lambda: FlexiKey.pbkdf2('mypassphrase', b'salt'*8, PBKDF2_ITERATIONS, 32)), + ("argon2", lambda: FlexiKey.argon2('mypassphrase', 64, b'S' * ARGON2_SALT_BYTES, **ARGON2_ARGS)), ]: print(f"{spec:<24} {count:<10} {timeit(func, number=count):.3f}s") diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index bf5feb449..bfe148561 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -4,11 +4,14 @@ import os import textwrap from binascii import a2b_base64, b2a_base64, hexlify from hashlib import sha256, pbkdf2_hmac +from typing import Literal from ..logger import create_logger logger = create_logger() +import argon2.low_level + from ..constants import * # NOQA from ..compress import Compressor from ..helpers import StableDict @@ -453,6 +456,37 @@ class FlexiKey: iterations = 1 return pbkdf2_hmac('sha256', passphrase.encode('utf-8'), salt, iterations, output_len_in_bytes) + @staticmethod + def argon2( + passphrase, + output_len_in_bytes: int, + salt: bytes, + time_cost, + memory_cost, + parallelism, + type: Literal['i', 'd', 'id'] + ) -> bytes: + if os.environ.get("BORG_TESTONLY_WEAKEN_KDF") == "1": + time_cost = 1 + parallelism = 1 + # 8 is the smallest value that avoids the "Memory cost is too small" exception + memory_cost = 8 + type_map = { + 'i': argon2.low_level.Type.I, + 'd': argon2.low_level.Type.D, + 'id': argon2.low_level.Type.ID, + } + key = argon2.low_level.hash_secret_raw( + secret=passphrase.encode("utf-8"), + hash_len=output_len_in_bytes, + salt=salt, + time_cost=time_cost, + memory_cost=memory_cost, + parallelism=parallelism, + type=type_map[type], + ) + return key + def decrypt_key_file_pbkdf2(self, encrypted_key, passphrase): key = self.pbkdf2(passphrase, encrypted_key.salt, encrypted_key.iterations, 32) data = AES(key, b'\0'*16).decrypt(encrypted_key.data) @@ -461,7 +495,8 @@ class FlexiKey: return None def decrypt_key_file_argon2(self, encrypted_key, passphrase): - key = passphrase.argon2( + key = self.argon2( + passphrase, output_len_in_bytes=64, salt=encrypted_key.salt, time_cost=encrypted_key.argon2_time_cost, @@ -506,7 +541,8 @@ class FlexiKey: def encrypt_key_file_argon2(self, data, passphrase): salt = os.urandom(ARGON2_SALT_BYTES) - key = passphrase.argon2( + key = self.argon2( + passphrase, output_len_in_bytes=64, salt=salt, **ARGON2_ARGS, diff --git a/src/borg/helpers/passphrase.py b/src/borg/helpers/passphrase.py index 704d67cc6..b53e26ad1 100644 --- a/src/borg/helpers/passphrase.py +++ b/src/borg/helpers/passphrase.py @@ -3,7 +3,6 @@ import os import shlex import subprocess import sys -from typing import Literal from . import bin_to_hex from . import Error @@ -12,8 +11,6 @@ from . import prepare_subprocess_env from ..logger import create_logger -import argon2.low_level - logger = create_logger() @@ -138,33 +135,3 @@ class Passphrase(str): def __repr__(self): return '' - - def argon2( - self, - output_len_in_bytes: int, - salt: bytes, - time_cost, - memory_cost, - parallelism, - type: Literal['i', 'd', 'id'] - ) -> bytes: - if os.environ.get("BORG_TESTONLY_WEAKEN_KDF") == "1": - time_cost = 1 - parallelism = 1 - # 8 is the smallest value that avoids the "Memory cost is too small" exception - memory_cost = 8 - type_map = { - 'i': argon2.low_level.Type.I, - 'd': argon2.low_level.Type.D, - 'id': argon2.low_level.Type.ID, - } - key = argon2.low_level.hash_secret_raw( - secret=self.encode("utf-8"), - hash_len=output_len_in_bytes, - salt=salt, - time_cost=time_cost, - memory_cost=memory_cost, - parallelism=parallelism, - type=type_map[type], - ) - return key diff --git a/src/borg/testsuite/crypto.py b/src/borg/testsuite/crypto.py index a907c910c..5dac74f5f 100644 --- a/src/borg/testsuite/crypto.py +++ b/src/borg/testsuite/crypto.py @@ -294,7 +294,7 @@ def test_decrypt_key_file_argon2_aes256_ctr_hmac_sha256(monkeypatch): def test_decrypt_key_file_pbkdf2_sha256_aes256_ctr_hmac_sha256(monkeypatch): plain = b'hello' salt = b'salt'*4 - passphrase = Passphrase("hello, pass phrase") + passphrase = "hello, pass phrase" key = FlexiKey.pbkdf2(passphrase, salt, 1, 32) hash = hmac_sha256(key, plain) data = AES(key, b'\0'*16).encrypt(plain)