mirror of
https://github.com/borgbackup/borg.git
synced 2026-06-10 09:21:44 -04:00
128bit increment_iv implementation/pack/unpack
remove strange "lower 64bits of IV" stuff, 64bit pack/unpack. while the 64bit counter 295EB "limit" was maybe high enough, always dealing with dissecting and reassembling the IV was a pain.
This commit is contained in:
parent
0c183acb55
commit
5ae3fa2927
5 changed files with 54 additions and 30 deletions
|
|
@ -55,11 +55,21 @@ cdef extern from "openssl/evp.h":
|
|||
import struct
|
||||
|
||||
_int = struct.Struct('>I')
|
||||
_long = struct.Struct('>Q')
|
||||
_2long = struct.Struct('>QQ')
|
||||
|
||||
bytes_to_int = lambda x, offset=0: _int.unpack_from(x, offset)[0]
|
||||
bytes_to_long = lambda x, offset=0: _long.unpack_from(x, offset)[0]
|
||||
long_to_bytes = lambda x: _long.pack(x)
|
||||
|
||||
|
||||
def bytes16_to_int(b, offset=0):
|
||||
h, l = _2long.unpack_from(b, offset)
|
||||
return (h << 64) + l
|
||||
|
||||
|
||||
def int_to_bytes16(i):
|
||||
max_uint64 = 0xffffffffffffffff
|
||||
l = i & max_uint64
|
||||
h = (i >> 64) & max_uint64
|
||||
return _2long.pack(h, l)
|
||||
|
||||
|
||||
def num_aes_blocks(length):
|
||||
|
|
@ -69,6 +79,22 @@ def num_aes_blocks(length):
|
|||
return (length + 15) // 16
|
||||
|
||||
|
||||
def increment_iv(iv, amount):
|
||||
"""
|
||||
increment the given IV considering that <amount> bytes of data was
|
||||
encrypted based on it. In CTR / GCM mode, the IV is just a counter and
|
||||
must never repeat.
|
||||
|
||||
:param iv: current IV, 16 bytes (128 bit)
|
||||
:param amount: amount of data (in bytes) that was encrypted
|
||||
:return: new IV, 16 bytes (128 bit)
|
||||
"""
|
||||
iv = bytes16_to_int(iv)
|
||||
iv += num_aes_blocks(amount)
|
||||
iv = int_to_bytes16(iv)
|
||||
return iv
|
||||
|
||||
|
||||
def pbkdf2_sha256(password, salt, iterations, size):
|
||||
"""Password based key derivation function 2 (RFC2898)
|
||||
"""
|
||||
|
|
|
|||
21
attic/key.py
21
attic/key.py
|
|
@ -17,7 +17,7 @@ except ImportError:
|
|||
lzma = None
|
||||
|
||||
from attic.crypto import pbkdf2_sha256, get_random_bytes, AES, AES_CTR_MODE, AES_GCM_MODE, \
|
||||
bytes_to_long, long_to_bytes, bytes_to_int, num_aes_blocks
|
||||
bytes_to_int, increment_iv
|
||||
from attic.helpers import IntegrityError, get_keys_dir, Error
|
||||
|
||||
# we do not store the full IV on disk, as the upper 8 bytes are expected to be
|
||||
|
|
@ -216,25 +216,6 @@ class PLAIN:
|
|||
return data
|
||||
|
||||
|
||||
def increment_iv(iv, amount):
|
||||
"""
|
||||
increment the given IV considering that <amount> bytes of data was
|
||||
encrypted based on it. In CTR / GCM mode, the IV is just a counter and
|
||||
must never repeat.
|
||||
|
||||
:param iv: current IV, 16 bytes (128 bit)
|
||||
:param amount: amount of data (in bytes) that was encrypted
|
||||
:return: new IV, 16 bytes (128 bit)
|
||||
"""
|
||||
# TODO: code assumes that the last 8 bytes are enough, the upper 8 always zero
|
||||
iv_last8 = iv[8:]
|
||||
current_iv = bytes_to_long(iv_last8)
|
||||
new_iv = current_iv + num_aes_blocks(amount)
|
||||
iv_last8 = long_to_bytes(new_iv)
|
||||
iv = PREFIX + iv_last8
|
||||
return iv
|
||||
|
||||
|
||||
def get_aad(meta):
|
||||
"""get additional authenticated data for AEAD ciphers"""
|
||||
if meta.legacy:
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ from hashlib import sha256
|
|||
from attic import xattr
|
||||
from attic.archive import Archive, ChunkBuffer
|
||||
from attic.archiver import Archiver
|
||||
from attic.crypto import bytes_to_long, num_aes_blocks
|
||||
from attic.crypto import bytes16_to_int, num_aes_blocks
|
||||
from attic.helpers import Manifest
|
||||
from attic.key import parser
|
||||
from attic.remote import RemoteRepository, PathNotAllowed
|
||||
|
|
@ -385,7 +385,7 @@ class ArchiverTestCase(ArchiverTestCaseBase):
|
|||
seen.add(hash)
|
||||
mac, meta, data = parser(data)
|
||||
num_blocks = num_aes_blocks(len(data))
|
||||
nonce = bytes_to_long(meta.iv, 8)
|
||||
nonce = bytes16_to_int(meta.iv)
|
||||
for counter in range(nonce, nonce + num_blocks):
|
||||
self.assert_not_in(counter, used)
|
||||
used.add(counter)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from binascii import hexlify
|
||||
from attic.testsuite import AtticTestCase
|
||||
from attic.crypto import pbkdf2_sha256, get_random_bytes, AES, AES_GCM_MODE, AES_CTR_MODE, \
|
||||
bytes_to_long, bytes_to_int, long_to_bytes
|
||||
bytes_to_int, bytes16_to_int, int_to_bytes16, increment_iv
|
||||
|
||||
|
||||
class CryptoTestCase(AtticTestCase):
|
||||
|
|
@ -9,9 +9,27 @@ class CryptoTestCase(AtticTestCase):
|
|||
def test_bytes_to_int(self):
|
||||
self.assert_equal(bytes_to_int(b'\0\0\0\1'), 1)
|
||||
|
||||
def test_bytes_to_long(self):
|
||||
self.assert_equal(bytes_to_long(b'\0\0\0\0\0\0\0\1'), 1)
|
||||
self.assert_equal(long_to_bytes(1), b'\0\0\0\0\0\0\0\1')
|
||||
def test_bytes16_to_int(self):
|
||||
i, b = 1, b'\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1'
|
||||
self.assert_equal(bytes16_to_int(b), i)
|
||||
self.assert_equal(int_to_bytes16(i), b)
|
||||
i, b = (1 << 64) + 2, b'\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0\2'
|
||||
self.assert_equal(bytes16_to_int(b), i)
|
||||
self.assert_equal(int_to_bytes16(i), b)
|
||||
|
||||
def test_increment_iv(self):
|
||||
tests = [
|
||||
# iv, amount, iv_expected
|
||||
(0, 0, 0),
|
||||
(0, 15, 1),
|
||||
(0, 16, 1),
|
||||
(0, 17, 2),
|
||||
(0xffffffffffffffff, 32, 0x10000000000000001),
|
||||
]
|
||||
for iv, amount, iv_expected in tests:
|
||||
iv = int_to_bytes16(iv)
|
||||
iv_expected = int_to_bytes16(iv_expected)
|
||||
self.assert_equal(increment_iv(iv, amount), iv_expected)
|
||||
|
||||
def test_pbkdf2_sha256(self):
|
||||
self.assert_equal(hexlify(pbkdf2_sha256(b'password', b'salt', 1, 32)),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ import re
|
|||
import shutil
|
||||
import tempfile
|
||||
from binascii import hexlify
|
||||
from attic.crypto import bytes_to_long
|
||||
from attic.testsuite import AtticTestCase
|
||||
from attic.key import PlaintextKey, PassphraseKey, KeyfileKey, COMPR_DEFAULT, increment_iv
|
||||
from attic.helpers import Location, unhexlify
|
||||
|
|
|
|||
Loading…
Reference in a new issue