Skip to content

Commit

Permalink
Merge 2c0d12c into 9396ca0
Browse files Browse the repository at this point in the history
  • Loading branch information
inikolcev committed Jan 22, 2020
2 parents 9396ca0 + 2c0d12c commit 204abc3
Show file tree
Hide file tree
Showing 7 changed files with 521 additions and 68 deletions.
10 changes: 7 additions & 3 deletions tlslite/utils/aes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ class AES(object):
def __init__(self, key, mode, IV, implementation):
if len(key) not in (16, 24, 32):
raise AssertionError()
if mode != 2:
raise AssertionError()
if len(IV) != 16:
if mode not in [2, 6]:
raise AssertionError()
if mode == 2:
if len(IV) != 16:
raise AssertionError()
if mode == 6:
if len(IV) not in [8, 12]:
raise AssertionError()
self.isBlockCipher = True
self.isAEAD = False
self.block_size = 16
Expand Down
65 changes: 16 additions & 49 deletions tlslite/utils/aesccm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@
#

from __future__ import division
from tlslite.utils.cryptomath import numberToByteArray, divceil
from tlslite.utils.python_aes import Python_AES
import sys
import array
from tlslite.utils.cryptomath import numberToByteArray
from tlslite.utils import python_aes


class AESCCM(object):
Expand All @@ -30,7 +28,8 @@ def __init__(self, key, implementation, rawAesEncrypt, tag_length=16):
self._rawAesEncrypt = rawAesEncrypt
self.implementation = implementation
self.nonceLength = 12
self._cbc = Python_AES(self.key, 2, bytearray(b'\x00' * 16))
self._cbc = python_aes.new(self.key, 2, bytearray(b'\x00' * 16))
self._ctr = python_aes.new(self.key, 6, bytearray(b'\x00' * 12))

def _cbcmac_calc(self, nonce, aad, msg):
L = 15 - len(nonce)
Expand Down Expand Up @@ -89,25 +88,23 @@ def seal(self, nonce, msg, aad):
raise ValueError("Bad nonce length")

L = 15 - len(nonce)
auth_value = bytearray(self.tagLength)

# We construct the key stream blocks.
# S_0 is not used for encrypting the message, it is only used
# to compute the authentication value.
# S_1..S_n are used to encrypt the message.

flags = L - 1
s_0 = self._rawAesEncrypt(bytearray([flags]) +
nonce + numberToByteArray(0, L))
s_0 = bytearray([flags]) + nonce + numberToByteArray(0, L)
s_n = bytearray([flags]) + nonce + numberToByteArray(1, L)

s_n = self._construct_s_n(msg, flags, nonce, L)

enc_msg = self._xor(msg, s_n)
self._ctr.counter = s_n
enc_msg = self._ctr.encrypt(msg)

mac = self._cbcmac_calc(nonce, aad, msg)

for i in range(self.tagLength):
auth_value[i] = mac[i] ^ s_0[i]
self._ctr.counter = s_0
auth_value = self._ctr.encrypt(mac)

ciphertext = enc_msg + auth_value
return ciphertext
Expand All @@ -122,61 +119,31 @@ def open(self, nonce, ciphertext, aad):
return None

L = 15 - len(nonce)
received_mac = bytearray(self.tagLength)
flags = L - 1

# Same construction as in seal function

s_0 = self._rawAesEncrypt(bytearray([flags]) +
nonce + numberToByteArray(0, L))

s_n = self._construct_s_n(ciphertext, flags, nonce, L)
s_0 = bytearray([flags]) + nonce + numberToByteArray(0, L)
s_n = bytearray([flags]) + nonce + numberToByteArray(1, L)

msg = self._xor(ciphertext, s_n)
self._ctr.counter = s_n
msg = self._ctr.decrypt(ciphertext)
msg = msg[:-self.tagLength]

auth_value = ciphertext[-self.tagLength:]
computed_mac = self._cbcmac_calc(nonce, aad, msg)

# We decrypt the auth value
for i in range(self.tagLength):
received_mac[i] = auth_value[i] ^ s_0[i]
self._ctr.counter = s_0
received_mac = self._ctr.decrypt(auth_value)

# Compare the mac vlaue is the same as the one we computed
if received_mac != computed_mac:
return None
return msg

def _construct_s_n(self, ciphertext, flags, nonce, L):
s_n = bytearray()
counter_lmt = divceil(len(ciphertext), 16)
for i in range(1, int(counter_lmt) + 1):
s_n += self._rawAesEncrypt(bytearray([flags]) +
nonce + numberToByteArray(i, L))
return s_n

if sys.version_info[0] >= 3:
def _xor(self, inp, s_n):
inp_added = -((8 - (len(inp) % 8)) % 8) or None
self._pad_with_zeroes(inp, 8)
msg = self._use_memoryview(inp, s_n)[:inp_added]
inp[:] = inp[:inp_added]
return msg
else:
def _xor(self, inp, s_n):
msg = bytearray(i ^ j for i, j in zip(inp, s_n))
return msg

@staticmethod
def _pad_with_zeroes(data, size):
if len(data) % size != 0:
zeroes_to_add = size - (len(data) % size)
data += b'\x00' * zeroes_to_add

@staticmethod
def _use_memoryview(msg, s_n):
msg_mv = memoryview(msg).cast('Q')
s_n_mv = memoryview(s_n).cast('Q')
enc_arr = array.array('Q', (i ^ j for i, j in zip(msg_mv, s_n_mv)))
enc_msg = bytearray(enc_arr.tobytes())
return enc_msg
21 changes: 7 additions & 14 deletions tlslite/utils/aesgcm.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# look-up table.

from __future__ import division
from tlslite.utils import python_aes
from .constanttime import ct_compare_digest
from .cryptomath import bytesToNumber, numberToByteArray

Expand All @@ -35,6 +36,8 @@ def __init__(self, key, implementation, rawAesEncrypt):
self.name = "aes256gcm"
else:
raise AssertionError()
self.key = key
self._ctr = python_aes.new(self.key, 6, bytearray(b'\x00' * 12))

self._rawAesEncrypt = rawAesEncrypt

Expand All @@ -53,18 +56,6 @@ def __init__(self, key, implementation, rawAesEncrypt):
self._productTable[self._reverseBits(i+1)] = \
self._gcmAdd(self._productTable[self._reverseBits(i)], h)

def _rawAesCtrEncrypt(self, counter, inp):
"""
Encrypts (or decrypts) plaintext with AES-CTR. counter is modified.
"""
out = bytearray(len(inp))
rawAesEncrypt = self._rawAesEncrypt
for i in range(0, len(out), 16):
mask = rawAesEncrypt(counter)
for j in range(i, min(len(out), i + 16)):
out[j] = inp[j] ^ mask[j-i]
self._inc32(counter)
return out

def _auth(self, ciphertext, ad, tagMask):
y = 0
Expand Down Expand Up @@ -125,7 +116,8 @@ def seal(self, nonce, plaintext, data):

# The counter starts at 2 for the actual encryption.
counter[-1] = 2
ciphertext = self._rawAesCtrEncrypt(counter, plaintext)
self._ctr.counter = counter
ciphertext = self._ctr.encrypt(plaintext)

tag = self._auth(ciphertext, data, tagMask)

Expand Down Expand Up @@ -158,7 +150,8 @@ def open(self, nonce, ciphertext, data):

# The counter starts at 2 for the actual decryption.
counter[-1] = 2
return self._rawAesCtrEncrypt(counter, ciphertext)
self._ctr.counter = counter
return self._ctr.decrypt(ciphertext)

@staticmethod
def _reverseBits(i):
Expand Down
22 changes: 21 additions & 1 deletion tlslite/utils/cipherfactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,27 @@ def createAES(key, IV, implList=None):
elif impl == "pycrypto" and cryptomath.pycryptoLoaded:
return pycrypto_aes.new(key, 2, IV)
elif impl == "python":
return python_aes.new(key, 2, IV)
return python_aes.new(key, 2, IV)
raise NotImplementedError()

def createAESCTR(key, IV, implList=None):
"""Create a new AESCTR object.
:type key: str
:param key: A 16, 24, or 32 byte string.
:type IV: str
:param IV: A 8 or 12 byte string
:rtype: tlslite.utils.AES
:returns: An AES object.
"""
if implList is None:
implList = ["python"]

for impl in implList:
if impl == "python":
return python_aes.new(key, 6, IV)
raise NotImplementedError()

def createAESGCM(key, implList=None):
Expand Down
39 changes: 38 additions & 1 deletion tlslite/utils/python_aes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
def new(key, mode, IV):
# IV argument name is a part of the interface
# pylint: disable=invalid-name
return Python_AES(key, mode, IV)
if mode == 2:
return Python_AES(key, mode, IV)
elif mode == 6:
return Python_AES_CTR(key, mode, IV)


class Python_AES(AES):
Expand Down Expand Up @@ -75,3 +78,37 @@ def decrypt(self, ciphertext):

self.IV = chainBytes[:]
return ciphertextBytes


class Python_AES_CTR(AES):
def __init__(self, key, mode, IV):
super(Python_AES_CTR, self).__init__(key, mode, IV, "python")
self.rijndael = Rijndael(key, 16)
self.IV = IV
if len(self.IV) not in [8, 12]:
raise ValueError("IV needs to be 8 or 12 bytes")
self.counter_bytes = 16 - len(self.IV)
self.counter = self.IV + bytearray(b'\x00' * self.counter_bytes)

def _counter(self, counter):
for i in range(len(counter)-1,
len(counter)-(self.counter_bytes + 1), -1):
counter[i] = (counter[i] + 1) % 256
if counter[i] != 0:
if counter[-self.counter_bytes:] == \
bytearray(b'\x00' * self.counter_bytes):
raise OverflowError("CTR counter overflowed")
break
return counter

def encrypt(self, plaintext):

mask = bytearray()
while len(mask) < len(plaintext):
mask += self.rijndael.encrypt(self.counter)
self._counter(self.counter)
inp_bytes = bytearray(i ^ j for i, j in zip(plaintext, mask))
return inp_bytes

def decrypt(self, ciphertext):
return self.encrypt(ciphertext)

0 comments on commit 204abc3

Please sign in to comment.