mirror of
https://github.com/borgbackup/borg.git
synced 2026-06-04 06:07:48 -04:00
legacy: move pbkdf2 key file encryption to borg.legacy.crypto.key, refs #9556
This commit is contained in:
parent
f58732290a
commit
b4821255f4
3 changed files with 56 additions and 31 deletions
|
|
@ -28,7 +28,7 @@ from ..platform import SaveFile
|
|||
from ..repoobj import RepoObj
|
||||
|
||||
|
||||
from .low_level import AES, bytes_to_int, num_cipher_blocks, hmac_sha256, blake2b_256
|
||||
from .low_level import bytes_to_int, num_cipher_blocks, hmac_sha256, blake2b_256
|
||||
from .low_level import AES256_OCB, CHACHA20_POLY1305
|
||||
from . import low_level
|
||||
|
||||
|
|
@ -438,9 +438,7 @@ class FlexiKey:
|
|||
raise UnsupportedKeyFormatError()
|
||||
else:
|
||||
self._encrypted_key_algorithm = encrypted_key.algorithm
|
||||
if encrypted_key.algorithm == "sha256":
|
||||
return self.decrypt_key_file_pbkdf2(encrypted_key, passphrase)
|
||||
elif encrypted_key.algorithm == "argon2 chacha20-poly1305":
|
||||
if encrypted_key.algorithm == "argon2 chacha20-poly1305":
|
||||
return self.decrypt_key_file_argon2(encrypted_key, passphrase)
|
||||
else:
|
||||
raise UnsupportedKeyFormatError()
|
||||
|
|
@ -478,13 +476,6 @@ class FlexiKey:
|
|||
)
|
||||
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)
|
||||
if hmac.compare_digest(hmac_sha256(key, data), encrypted_key.hash):
|
||||
return data
|
||||
return None
|
||||
|
||||
def decrypt_key_file_argon2(self, encrypted_key, passphrase):
|
||||
key = self.argon2(
|
||||
passphrase,
|
||||
|
|
@ -502,22 +493,11 @@ class FlexiKey:
|
|||
return None
|
||||
|
||||
def encrypt_key_file(self, data, passphrase, algorithm):
|
||||
if algorithm == "sha256":
|
||||
return self.encrypt_key_file_pbkdf2(data, passphrase)
|
||||
elif algorithm == "argon2 chacha20-poly1305":
|
||||
if algorithm == "argon2 chacha20-poly1305":
|
||||
return self.encrypt_key_file_argon2(data, passphrase)
|
||||
else:
|
||||
raise ValueError(f"Unexpected algorithm: {algorithm}")
|
||||
|
||||
def encrypt_key_file_pbkdf2(self, data, passphrase):
|
||||
salt = os.urandom(32)
|
||||
iterations = PBKDF2_ITERATIONS
|
||||
key = self.pbkdf2(passphrase, salt, iterations, 32)
|
||||
hash = hmac_sha256(key, data)
|
||||
cdata = AES(key, b"\0" * 16).encrypt(data)
|
||||
enc_key = EncryptedKey(version=1, salt=salt, iterations=iterations, algorithm="sha256", hash=hash, data=cdata)
|
||||
return msgpack.packb(enc_key.as_dict())
|
||||
|
||||
def encrypt_key_file_argon2(self, data, passphrase):
|
||||
salt = os.urandom(ARGON2_SALT_BYTES)
|
||||
key = self.argon2(passphrase, output_len_in_bytes=32, salt=salt, **ARGON2_ARGS)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,52 @@
|
|||
import hmac
|
||||
import os
|
||||
|
||||
from ...constants import * # NOQA
|
||||
from ...crypto.low_level import AES256_CTR_HMAC_SHA256, AES256_CTR_BLAKE2b
|
||||
from ...crypto.key import ID_HMAC_SHA_256, ID_BLAKE2b_256, AESKeyBase, FlexiKey
|
||||
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 ...helpers import get_limited_unpacker, msgpack
|
||||
from ...item import EncryptedKey
|
||||
from .low_level import AES
|
||||
|
||||
|
||||
class KeyfileKey(ID_HMAC_SHA_256, AESKeyBase, FlexiKey): # type: ignore[misc]
|
||||
class Pbkdf2FileMixin:
|
||||
"""Mixin for borg 1.x key files encrypted with PBKDF2 + AES-CTR."""
|
||||
|
||||
def decrypt_key_file(self, data, passphrase):
|
||||
unpacker = get_limited_unpacker("key")
|
||||
unpacker.feed(data)
|
||||
unpacked = unpacker.unpack()
|
||||
encrypted_key = EncryptedKey(internal_dict=unpacked)
|
||||
if encrypted_key.version != 1:
|
||||
raise UnsupportedKeyFormatError()
|
||||
self._encrypted_key_algorithm = encrypted_key.algorithm
|
||||
if encrypted_key.algorithm == "sha256":
|
||||
return self.decrypt_key_file_pbkdf2(encrypted_key, passphrase)
|
||||
return super().decrypt_key_file(data, passphrase)
|
||||
|
||||
def encrypt_key_file(self, data, passphrase, algorithm):
|
||||
if algorithm == "sha256":
|
||||
return self.encrypt_key_file_pbkdf2(data, passphrase)
|
||||
return super().encrypt_key_file(data, passphrase, algorithm)
|
||||
|
||||
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)
|
||||
if hmac.compare_digest(hmac_sha256(key, data), encrypted_key.hash):
|
||||
return data
|
||||
return None
|
||||
|
||||
def encrypt_key_file_pbkdf2(self, data, passphrase):
|
||||
salt = os.urandom(32)
|
||||
iterations = PBKDF2_ITERATIONS
|
||||
key = self.pbkdf2(passphrase, salt, iterations, 32)
|
||||
hash = hmac_sha256(key, data)
|
||||
cdata = AES(key, b"\0" * 16).encrypt(data)
|
||||
enc_key = EncryptedKey(version=1, salt=salt, iterations=iterations, algorithm="sha256", hash=hash, data=cdata)
|
||||
return msgpack.packb(enc_key.as_dict())
|
||||
|
||||
|
||||
class KeyfileKey(Pbkdf2FileMixin, ID_HMAC_SHA_256, AESKeyBase, FlexiKey): # type: ignore[misc]
|
||||
TYPES_ACCEPTABLE = {KeyType.KEYFILE, KeyType.REPO, KeyType.PASSPHRASE}
|
||||
TYPE = KeyType.KEYFILE
|
||||
NAME = "key file"
|
||||
|
|
@ -12,7 +55,7 @@ class KeyfileKey(ID_HMAC_SHA_256, AESKeyBase, FlexiKey): # type: ignore[misc]
|
|||
CIPHERSUITE = AES256_CTR_HMAC_SHA256
|
||||
|
||||
|
||||
class RepoKey(ID_HMAC_SHA_256, AESKeyBase, FlexiKey): # type: ignore[misc]
|
||||
class RepoKey(Pbkdf2FileMixin, ID_HMAC_SHA_256, AESKeyBase, FlexiKey): # type: ignore[misc]
|
||||
TYPES_ACCEPTABLE = {KeyType.KEYFILE, KeyType.REPO, KeyType.PASSPHRASE}
|
||||
TYPE = KeyType.REPO
|
||||
NAME = "repokey"
|
||||
|
|
@ -21,7 +64,7 @@ class RepoKey(ID_HMAC_SHA_256, AESKeyBase, FlexiKey): # type: ignore[misc]
|
|||
CIPHERSUITE = AES256_CTR_HMAC_SHA256
|
||||
|
||||
|
||||
class Blake2KeyfileKey(ID_BLAKE2b_256, AESKeyBase, FlexiKey): # type: ignore[misc]
|
||||
class Blake2KeyfileKey(Pbkdf2FileMixin, ID_BLAKE2b_256, AESKeyBase, FlexiKey): # type: ignore[misc]
|
||||
TYPES_ACCEPTABLE = {KeyType.BLAKE2KEYFILE, KeyType.BLAKE2REPO}
|
||||
TYPE = KeyType.BLAKE2KEYFILE
|
||||
NAME = "key file BLAKE2b"
|
||||
|
|
@ -30,7 +73,7 @@ class Blake2KeyfileKey(ID_BLAKE2b_256, AESKeyBase, FlexiKey): # type: ignore[mi
|
|||
CIPHERSUITE = AES256_CTR_BLAKE2b
|
||||
|
||||
|
||||
class Blake2RepoKey(ID_BLAKE2b_256, AESKeyBase, FlexiKey): # type: ignore[misc]
|
||||
class Blake2RepoKey(Pbkdf2FileMixin, ID_BLAKE2b_256, AESKeyBase, FlexiKey): # type: ignore[misc]
|
||||
TYPES_ACCEPTABLE = {KeyType.BLAKE2KEYFILE, KeyType.BLAKE2REPO}
|
||||
TYPE = KeyType.BLAKE2REPO
|
||||
NAME = "repokey BLAKE2b"
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ import unittest
|
|||
|
||||
from ...crypto.low_level import AES256_CTR_HMAC_SHA256, AES256_OCB, CHACHA20_POLY1305, UNENCRYPTED, IntegrityError
|
||||
from ...crypto.low_level import bytes_to_long, bytes_to_int, long_to_bytes
|
||||
from ...crypto.low_level import AES, hmac_sha256
|
||||
from ...crypto.low_level import hmac_sha256
|
||||
from ...legacy.crypto.low_level import AES
|
||||
from hashlib import sha256
|
||||
from ...crypto.key import CHPOKeyfileKey, AESOCBRepoKey, FlexiKey, KeyBase, PlaintextKey
|
||||
from ...legacy.crypto.key import KeyfileKey as LegacyKeyfileKey
|
||||
from ...helpers import msgpack, bin_to_hex
|
||||
|
||||
from .. import BaseTestCase
|
||||
|
|
@ -232,7 +234,7 @@ def test_decrypt_key_file_pbkdf2_sha256_aes256_ctr_hmac_sha256():
|
|||
encrypted = msgpack.packb(
|
||||
{"version": 1, "algorithm": "sha256", "iterations": 1, "salt": salt, "data": data, "hash": hash}
|
||||
)
|
||||
key = CHPOKeyfileKey(None)
|
||||
key = LegacyKeyfileKey(None)
|
||||
|
||||
decrypted = key.decrypt_key_file(encrypted, passphrase)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue