Allow AES-CTR to work even with lost messages

Make the counter public. This should no affect the security properties,
as long as you are careful nevert to re-use the (secret key, counter)
pair.
This commit is contained in:
Kovid Goyal 2016-03-22 08:46:10 +05:30
parent 4bee4a6e83
commit cae0e38a97

View File

@ -310,14 +310,13 @@ class CBC(ModeOfOperation):
class Counter: class Counter:
def __init__(self, initial_value): def __init__(self, initial_value):
self.bytes = Uint8Array(16)
if not initial_value: if not initial_value:
self.bytes = Uint8Array(16)
self.set_value(1) self.set_value(1)
elif type(initial_value) is 'number': elif type(initial_value) is 'number':
self.bytes = Uint8Array(16)
self.set_value(initial_value) self.set_value(initial_value)
else: else:
self.bytes = Uint8Array(initial_value) self.bytes.set(initial_value)
def set_value(self, value): def set_value(self, value):
c = self.bytes c = self.bytes
@ -326,7 +325,7 @@ class Counter:
value >>= 8 value >>= 8
def set_bytes(self, bytes): def set_bytes(self, bytes):
self.bytes = Uint8Array(bytes) self.bytes.set(bytes)
def increment(self): def increment(self):
c = self.bytes c = self.bytes
@ -342,6 +341,11 @@ class Counter:
class CTR(ModeOfOperation): class CTR(ModeOfOperation):
# Note that this mode of operation requires the pair of (counterbytes,
# secret key) to always be unique, for every block. Therefore, if you are
# using it for bi-directional messaging it is best to use a different
# secret key for each direction
def __init__(self, key, counter): def __init__(self, key, counter):
ModeOfOperation.__init__(self, key) ModeOfOperation.__init__(self, key)
self.cval = counter self.cval = counter
@ -357,9 +361,12 @@ class CTR(ModeOfOperation):
self.counter.increment() self.counter.increment()
bytes[i] ^= self.wmem[self.counter_index] bytes[i] ^= self.wmem[self.counter_index]
self.counter_index += 1 self.counter_index += 1
self.counter_index = 16
self.counter.increment()
def encrypt(self, plaintext, tag): def encrypt(self, plaintext, tag):
outbytes = string_to_bytes(plaintext) outbytes = string_to_bytes(plaintext)
counterbytes = Uint8Array(self.counter.bytes)
if tag: if tag:
tag_bytes = self.tag_as_bytes(tag) tag_bytes = self.tag_as_bytes(tag)
t = Uint8Array(outbytes.length + tag_bytes.length) t = Uint8Array(outbytes.length + tag_bytes.length)
@ -367,12 +374,22 @@ class CTR(ModeOfOperation):
t.set(outbytes, tag_bytes.length) t.set(outbytes, tag_bytes.length)
outbytes = t outbytes = t
self._crypt(outbytes) self._crypt(outbytes)
return outbytes return {'cipherbytes':outbytes, 'counterbytes':counterbytes}
def __enter__(self):
self.before_index = self.counter_index
self.before_bytes = Uint8Array(self.counter.bytes)
def __exit__(self):
self.counter_index = self.before_index
self.counter.set_bytes(self.before_bytes)
def decrypt(self, output_from_encrypt, tag): def decrypt(self, output_from_encrypt, tag):
b = Uint8Array(output_from_encrypt.length) b = Uint8Array(output_from_encrypt.cipherbytes)
b.set(output_from_encrypt) with self:
self._crypt(b) self.counter.set_bytes(output_from_encrypt.counterbytes)
self.counter_index = 16
self._crypt(b)
offset = 0 offset = 0
if tag: if tag:
tag_bytes = self.tag_as_bytes(tag) tag_bytes = self.tag_as_bytes(tag)
@ -394,12 +411,11 @@ if __name__ == '__main__':
decrypted = cbc.decrypt(crypted, secret_tag) decrypted = cbc.decrypt(crypted, secret_tag)
print('CBC Roundtrip with tag:', 'OK' if text is decrypted else 'FAILED') print('CBC Roundtrip with tag:', 'OK' if text is decrypted else 'FAILED')
ctre = CTR() ctr = CTR()
ctrd = CTR(ctre.key) crypted = ctr.encrypt(text)
crypted = ctre.encrypt(text) decrypted = ctr.decrypt(crypted)
decrypted = ctrd.decrypt(crypted)
print('CTR Roundtrip:', 'OK' if text is decrypted else 'FAILED') print('CTR Roundtrip:', 'OK' if text is decrypted else 'FAILED')
crypted = ctre.encrypt(text, secret_tag) crypted = ctr.encrypt(text, secret_tag)
decrypted = ctrd.decrypt(crypted, secret_tag) decrypted = ctr.decrypt(crypted, secret_tag)
print('CTR Roundtrip with tag:', 'OK' if text is decrypted else 'FAILED') print('CTR Roundtrip with tag:', 'OK' if text is decrypted else 'FAILED')