mirror of
https://github.com/borgbackup/borg.git
synced 2024-12-26 01:37:20 +00:00
refactor to cipher.extract_iv
position and length of iv depends on cipher
This commit is contained in:
parent
2d79f19263
commit
e9bbf9307d
3 changed files with 36 additions and 23 deletions
|
@ -373,13 +373,6 @@ def decrypt(self, id, data, decompress=True):
|
|||
self.assert_id(id, data)
|
||||
return data
|
||||
|
||||
def extract_nonce(self, payload):
|
||||
if not (payload[0] == self.TYPE or
|
||||
payload[0] == PassphraseKey.TYPE and isinstance(self, RepoKey)):
|
||||
raise IntegrityError('Manifest: Invalid encryption envelope')
|
||||
nonce = bytes_to_long(payload[33:41])
|
||||
return nonce
|
||||
|
||||
def init_from_random_data(self, data=None):
|
||||
if data is None:
|
||||
data = os.urandom(100)
|
||||
|
@ -391,10 +384,21 @@ def init_from_random_data(self, data=None):
|
|||
if self.chunk_seed & 0x80000000:
|
||||
self.chunk_seed = self.chunk_seed - 0xffffffff - 1
|
||||
|
||||
def init_ciphers(self, manifest_nonce=0):
|
||||
self.cipher = CIPHERSUITE(mac_key=self.enc_hmac_key, enc_key=self.enc_key,
|
||||
iv=manifest_nonce.to_bytes(16, byteorder='big'))
|
||||
self.nonce_manager = NonceManager(self.repository, self.cipher, manifest_nonce)
|
||||
def init_ciphers(self, manifest_data=None):
|
||||
self.cipher = CIPHERSUITE(mac_key=self.enc_hmac_key, enc_key=self.enc_key)
|
||||
if manifest_data is None:
|
||||
nonce = 0
|
||||
else:
|
||||
if not (manifest_data[0] == self.TYPE or
|
||||
manifest_data[0] == PassphraseKey.TYPE and isinstance(self, RepoKey)):
|
||||
raise IntegrityError('Invalid encryption envelope')
|
||||
# manifest_blocks is a safe upper bound on the amount of cipher blocks needed
|
||||
# to encrypt the manifest. depending on the ciphersuite and overhead, it might
|
||||
# be a bit too high, but that does not matter.
|
||||
manifest_blocks = num_cipher_blocks(len(manifest_data))
|
||||
nonce = self.cipher.extract_iv(manifest_data) + manifest_blocks
|
||||
self.cipher.set_iv(nonce.to_bytes(16, byteorder='big'))
|
||||
self.nonce_manager = NonceManager(self.repository, self.cipher, nonce)
|
||||
|
||||
|
||||
class Passphrase(str):
|
||||
|
@ -514,8 +518,7 @@ def detect(cls, repository, manifest_data):
|
|||
key.init(repository, passphrase)
|
||||
try:
|
||||
key.decrypt(None, manifest_data)
|
||||
num_blocks = num_cipher_blocks(len(manifest_data) - 41)
|
||||
key.init_ciphers(key.extract_nonce(manifest_data) + num_blocks)
|
||||
key.init_ciphers(manifest_data)
|
||||
key._passphrase = passphrase
|
||||
return key
|
||||
except IntegrityError:
|
||||
|
@ -554,8 +557,7 @@ def detect(cls, repository, manifest_data):
|
|||
else:
|
||||
if not key.load(target, passphrase):
|
||||
raise PassphraseWrong
|
||||
num_blocks = num_cipher_blocks(len(manifest_data) - 41)
|
||||
key.init_ciphers(key.extract_nonce(manifest_data) + num_blocks)
|
||||
key.init_ciphers(manifest_data)
|
||||
key._passphrase = passphrase
|
||||
return key
|
||||
|
||||
|
|
|
@ -233,6 +233,9 @@ class UNENCRYPTED:
|
|||
def next_iv(self):
|
||||
return self.iv
|
||||
|
||||
def extract_iv(self, envelope):
|
||||
return 0
|
||||
|
||||
|
||||
cdef class AES256_CTR_HMAC_SHA256:
|
||||
# Layout: HEADER + HMAC 32 + IV 8 + CT (same as attic / borg < 1.2 IF HEADER = TYPE_BYTE, no AAD)
|
||||
|
@ -395,6 +398,10 @@ cdef class AES256_CTR_HMAC_SHA256:
|
|||
for i in range(self.iv_len_short):
|
||||
iv_out[i] = iv[(self.iv_len-self.iv_len_short)+i]
|
||||
|
||||
def extract_iv(self, envelope):
|
||||
offset = 1 + self.mac_len
|
||||
return bytes_to_long(envelope[offset:offset+self.iv_len_short])
|
||||
|
||||
|
||||
ctypedef const EVP_CIPHER * (* CIPHER)()
|
||||
|
||||
|
@ -565,6 +572,10 @@ cdef class _AEAD_BASE:
|
|||
for i in range(self.iv_len):
|
||||
iv_out[i] = iv[i]
|
||||
|
||||
def extract_iv(self, envelope):
|
||||
offset = 1 + self.mac_len # XXX 1 -> self.header_len
|
||||
return bytes_to_long(envelope[offset:offset+self.iv_len])
|
||||
|
||||
|
||||
cdef class _AES_BASE(_AEAD_BASE):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
@ -119,12 +119,12 @@ def test_keyfile(self, monkeypatch, keys_dir):
|
|||
key = KeyfileKey.create(self.MockRepository(), self.MockArgs())
|
||||
assert bytes_to_long(key.cipher.next_iv(), 8) == 0
|
||||
manifest = key.encrypt(b'ABC')
|
||||
assert key.extract_nonce(manifest) == 0
|
||||
assert key.cipher.extract_iv(manifest) == 0
|
||||
manifest2 = key.encrypt(b'ABC')
|
||||
assert manifest != manifest2
|
||||
assert key.decrypt(None, manifest) == key.decrypt(None, manifest2)
|
||||
assert key.extract_nonce(manifest2) == 1
|
||||
iv = key.extract_nonce(manifest)
|
||||
assert key.cipher.extract_iv(manifest2) == 1
|
||||
iv = key.cipher.extract_iv(manifest)
|
||||
key2 = KeyfileKey.detect(self.MockRepository(), manifest)
|
||||
assert bytes_to_long(key2.cipher.next_iv(), 8) >= iv + key2.cipher.block_count(len(manifest) - KeyfileKey.PAYLOAD_OVERHEAD)
|
||||
# Key data sanity check
|
||||
|
@ -140,7 +140,7 @@ def test_keyfile_nonce_rollback_protection(self, monkeypatch, keys_dir):
|
|||
fd.write("0000000000002000")
|
||||
key = KeyfileKey.create(repository, self.MockArgs())
|
||||
data = key.encrypt(b'ABC')
|
||||
assert key.extract_nonce(data) == 0x2000
|
||||
assert key.cipher.extract_iv(data) == 0x2000
|
||||
assert key.decrypt(None, data) == b'ABC'
|
||||
|
||||
def test_keyfile_kfenv(self, tmpdir, monkeypatch):
|
||||
|
@ -192,14 +192,14 @@ def test_passphrase(self, keys_dir, monkeypatch):
|
|||
assert hexlify(key.enc_key) == b'2ff3654c6daf7381dbbe718d2b20b4f1ea1e34caa6cc65f6bb3ac376b93fed2a'
|
||||
assert key.chunk_seed == -775740477
|
||||
manifest = key.encrypt(b'ABC')
|
||||
assert key.extract_nonce(manifest) == 0
|
||||
assert key.cipher.extract_iv(manifest) == 0
|
||||
manifest2 = key.encrypt(b'ABC')
|
||||
assert manifest != manifest2
|
||||
assert key.decrypt(None, manifest) == key.decrypt(None, manifest2)
|
||||
assert key.extract_nonce(manifest2) == 1
|
||||
iv = key.extract_nonce(manifest)
|
||||
assert key.cipher.extract_iv(manifest2) == 1
|
||||
iv = key.cipher.extract_iv(manifest)
|
||||
key2 = PassphraseKey.detect(self.MockRepository(), manifest)
|
||||
assert bytes_to_long(key2.cipher.next_iv(), 8) == iv + key2.cipher.block_count(len(manifest) - PassphraseKey.PAYLOAD_OVERHEAD)
|
||||
assert bytes_to_long(key2.cipher.next_iv(), 8) == iv + key2.cipher.block_count(len(manifest))
|
||||
assert key.id_key == key2.id_key
|
||||
assert key.enc_hmac_key == key2.enc_hmac_key
|
||||
assert key.enc_key == key2.enc_key
|
||||
|
|
Loading…
Reference in a new issue