Skip to content

Commit

Permalink
Merge 6fee274 into 27157e0
Browse files Browse the repository at this point in the history
  • Loading branch information
inikolcev committed Mar 5, 2020
2 parents 27157e0 + 6fee274 commit 2db7c22
Show file tree
Hide file tree
Showing 13 changed files with 898 additions and 87 deletions.
19 changes: 12 additions & 7 deletions tests/tlstest.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,10 +921,13 @@ def connect():
continue
if cipher in ("aes128gcm", "aes256gcm") and \
implementation not in ("pycrypto",
"python"):
"python", "openssl"):
continue
if cipher in ("chacha20-poly1305_draft00", "chacha20-poly1305",
"aes128ccm", "aes128ccm_8", "aes256ccm", "aes256ccm_8") \
if cipher in ("aes128ccm", "aes128ccm_8",
"aes256ccm", "aes256ccm_8") and \
implementation not in ("python", "openssl"):
continue
if cipher in ("chacha20-poly1305_draft00", "chacha20-poly1305") \
and implementation not in ("python", ):
continue

Expand Down Expand Up @@ -2211,11 +2214,13 @@ def server_bind(self):
continue
if cipher in ("aes128gcm", "aes256gcm") and \
implementation not in ("pycrypto",
"python"):
"python", "openssl"):
continue
if cipher in ("aes128ccm", "aes128ccm_8",
"aes256ccm", "aes256ccm_8") and \
implementation not in ("python", "openssl"):
continue
if cipher in ("chacha20-poly1305_draft00", "chacha20-poly1305",
"aes128ccm", "aes128ccm_8",
"aes256ccm", "aes256ccm_8") \
if cipher in ("chacha20-poly1305_draft00", "chacha20-poly1305") \
and implementation not in ("python", ):
continue

Expand Down
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) > 16:
raise AssertionError()
self.isBlockCipher = True
self.isAEAD = False
self.block_size = 16
Expand Down
81 changes: 25 additions & 56 deletions tlslite/utils/aesccm.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
#

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 import cryptomath
from tlslite.utils.cryptomath import numberToByteArray
from tlslite.utils import python_aes, openssl_aes


class AESCCM(object):
Expand All @@ -18,6 +17,10 @@ def __init__(self, key, implementation, rawAesEncrypt, tag_length=16):
self.isAEAD = True
self.key = key
self.tagLength = tag_length
self._rawAesEncrypt = rawAesEncrypt
self.nonceLength = 12
self.implementation = implementation

if len(self.key) == 16 and self.tagLength == 8:
self.name = "aes128ccm_8"
elif len(self.key) == 16 and self.tagLength == 16:
Expand All @@ -27,10 +30,10 @@ def __init__(self, key, implementation, rawAesEncrypt, tag_length=16):
else:
assert len(self.key) == 32 and self.tagLength == 16
self.name = "aes256ccm"
self._rawAesEncrypt = rawAesEncrypt
self.implementation = implementation
self.nonceLength = 12
self._cbc = Python_AES(self.key, 2, bytearray(b'\x00' * 16))

self._ctr = python_aes.new(self.key, 6, bytearray(b'\x00' * 16))
self._cbc = python_aes.new(self.key, 2, bytearray(b'\x00' * 16))


def _cbcmac_calc(self, nonce, aad, msg):
L = 15 - len(nonce)
Expand Down Expand Up @@ -89,25 +92,21 @@ 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_n = self._construct_s_n(msg, flags, nonce, L)

enc_msg = self._xor(msg, s_n)
s_0 = bytearray([flags]) + nonce + numberToByteArray(0, L)
s_n = bytearray([flags]) + nonce + numberToByteArray(1, L)

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)
self._ctr.counter = s_n
enc_msg = self._ctr.encrypt(msg)

ciphertext = enc_msg + auth_value
return ciphertext
Expand All @@ -122,61 +121,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_0 = bytearray([flags]) + nonce + numberToByteArray(0, L)
s_n = bytearray([flags]) + nonce + numberToByteArray(1, L)

s_n = self._construct_s_n(ciphertext, flags, nonce, L)
auth_value = ciphertext[-self.tagLength:]

msg = self._xor(ciphertext, s_n)
# We decrypt the auth value
self._ctr.counter = s_0
received_mac = self._ctr.decrypt(auth_value)
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]

# 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,8 +36,10 @@ def __init__(self, key, implementation, rawAesEncrypt):
self.name = "aes256gcm"
else:
raise AssertionError()
self.key = key

self._rawAesEncrypt = rawAesEncrypt
self._ctr = python_aes.new(self.key, 6, bytearray(b'\x00' * 16))

# The GCM key is AES(0).
h = bytesToNumber(self._rawAesEncrypt(bytearray(16)))
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
36 changes: 32 additions & 4 deletions tlslite/utils/cipherfactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from tlslite.utils import python_chacha20_poly1305
from tlslite.utils import python_rc4
from tlslite.utils import python_tripledes
from tlslite.utils import openssl_aesccm
from tlslite.utils import openssl_aesgcm

from tlslite.utils import cryptomath

Expand Down Expand Up @@ -53,7 +55,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 All @@ -66,13 +88,15 @@ def createAESGCM(key, implList=None):
:returns: An AESGCM object.
"""
if implList is None:
implList = ["pycrypto", "python"]
implList = ["pycrypto", "python", "openssl"]

for impl in implList:
if impl == "pycrypto" and cryptomath.pycryptoLoaded:
return pycrypto_aesgcm.new(key)
if impl == "python":
return python_aesgcm.new(key)
if impl == "openssl" and cryptomath.m2cryptoLoaded:
return openssl_aesgcm.new(key)
raise NotImplementedError()

def createAESCCM(key, implList=None):
Expand All @@ -86,11 +110,13 @@ def createAESCCM(key, implList=None):
"""

if implList is None:
implList = ["python"]
implList = ["python", "openssl"]

for impl in implList:
if impl == "python":
return python_aesccm.new(key)
if impl == "openssl":
return openssl_aesccm.new(key)

raise NotImplementedError()

Expand All @@ -105,11 +131,13 @@ def createAESCCM_8(key, implList=None):
"""

if implList is None:
implList = ["python"]
implList = ["python", "openssl"]

for impl in implList:
if impl == "python":
return python_aesccm.new(key, 8)
if impl == "openssl":
return openssl_aesccm.new(key)

raise NotImplementedError()

Expand Down

0 comments on commit 2db7c22

Please sign in to comment.