Skip to content

Commit

Permalink
crypto: fix and simplify _get_session_key, fixes borgbackup#7953
Browse files Browse the repository at this point in the history
also:
- convert sessionid memoryview to bytes before calling _get_cipher,
to avoid TypeError in (session + flavour) operation.
- add docstring and comments
  • Loading branch information
ThomasWaldmann committed Dec 1, 2023
1 parent 443245a commit ec33b0c
Showing 1 changed file with 17 additions and 10 deletions.
27 changes: 17 additions & 10 deletions src/borg/crypto/key.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from ..repoobj import RepoObj


from .low_level import AES, bytes_to_int, num_cipher_blocks, hmac_sha256, blake2b_256, hkdf_hmac_sha512
from .low_level import AES, bytes_to_int, num_cipher_blocks, hmac_sha256, blake2b_256
from .low_level import AES256_CTR_HMAC_SHA256, AES256_CTR_BLAKE2b, AES256_OCB, CHACHA20_POLY1305
from . import low_level

Expand Down Expand Up @@ -833,7 +833,7 @@ def decrypt(self, id, data):
# to decrypt existing data, we need to get a cipher configured for the sessionid and iv from header
self.assert_type(data[0], id)
iv_48bit = data[2:8]
sessionid = data[8:32]
sessionid = bytes(data[8:32])
iv = int.from_bytes(iv_48bit, "big")
cipher = self._get_cipher(sessionid, iv)
try:
Expand All @@ -857,15 +857,22 @@ def init_from_random_data(self):
chunk_seed = chunk_seed - 0xFFFFFFFF - 1
self.init_from_given_data(crypt_key=data[0:64], id_key=data[64:96], chunk_seed=chunk_seed)

def _get_session_key(self, sessionid):
def _get_session_key(self, sessionid, domain=None):
"""
Derive a session key from the secret long-term static crypt_key (which is a fully random PRK)
and the session id (which is fully random also).
Optionally, a domain can be given for domain separation (defaults to a different binary string
per cipher suite).
"""
# Performance note:
# While this is only invoked once per session to generate a new key for encrypting new data, it is invoked
# frequently (per encrypted repo object) to compute the corresponding key for decrypting existing data.
assert len(sessionid) == 24 # 192bit
key = hkdf_hmac_sha512(
ikm=self.crypt_key,
salt=sessionid,
info=b"borg-session-key-" + self.CIPHERSUITE.__name__.encode(),
output_length=32,
)
return key
if domain is None:
domain = b"borg-session-key-" + self.CIPHERSUITE.__name__.encode()
# Because crypt_key is already a PRK, we do not need KDF security here, PRF security is good enough.
# See https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Cr2.pdf section 4 "one-step KDF".
return sha256(self.crypt_key + sessionid + domain).digest()

def _get_cipher(self, sessionid, iv):
assert isinstance(iv, int)
Expand Down

0 comments on commit ec33b0c

Please sign in to comment.