1
0
Fork 0
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:
Thomas Waldmann 2016-09-02 23:43:15 +02:00
parent 2d79f19263
commit e9bbf9307d
3 changed files with 36 additions and 23 deletions

View file

@ -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

View file

@ -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):

View file

@ -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