diff --git a/third_party/tlslite/tests/tlstest.py b/third_party/tlslite/tests/tlstest.py index fa1b13f..7985d23 100755 --- a/third_party/tlslite/tests/tlstest.py +++ b/third_party/tlslite/tests/tlstest.py @@ -318,9 +318,11 @@ def clientTestCmd(argv): print("Test 23 - throughput test") for implementation in implementations: - for cipher in ["aes128", "aes256", "3des", "rc4"]: + for cipher in ["aes128gcm", "aes128", "aes256", "3des", "rc4"]: if cipher == "3des" and implementation not in ("openssl", "pycrypto"): continue + if cipher == "aes128gcm" and implementation not in ("pycrypto", "python"): + continue print("Test 23:", end=' ') connection = connect() @@ -678,9 +680,11 @@ def serverTestCmd(argv): print("Test 23 - throughput test") for implementation in implementations: - for cipher in ["aes128", "aes256", "3des", "rc4"]: + for cipher in ["aes128gcm", "aes128", "aes256", "3des", "rc4"]: if cipher == "3des" and implementation not in ("openssl", "pycrypto"): continue + if cipher == "aes128gcm" and implementation not in ("pycrypto", "python"): + continue print("Test 23:", end=' ') connection = connect() diff --git a/third_party/tlslite/tlslite/constants.py b/third_party/tlslite/tlslite/constants.py index 7ee70be..e5b88af 100644 --- a/third_party/tlslite/tlslite/constants.py +++ b/third_party/tlslite/tlslite/constants.py @@ -175,6 +175,9 @@ class CipherSuite: TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067 TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B + TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E + tripleDESSuites = [] tripleDESSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) tripleDESSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) @@ -199,6 +202,10 @@ class CipherSuite: aes256Suites.append(TLS_RSA_WITH_AES_256_CBC_SHA256) aes256Suites.append(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) + aes128GcmSuites = [] + aes128GcmSuites.append(TLS_RSA_WITH_AES_128_GCM_SHA256) + aes128GcmSuites.append(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) + rc4Suites = [] rc4Suites.append(TLS_RSA_WITH_RC4_128_SHA) rc4Suites.append(TLS_RSA_WITH_RC4_128_MD5) @@ -225,25 +232,35 @@ class CipherSuite: sha256Suites.append(TLS_RSA_WITH_AES_256_CBC_SHA256) sha256Suites.append(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) sha256Suites.append(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) + sha256Suites.append(TLS_RSA_WITH_AES_128_GCM_SHA256) + sha256Suites.append(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) + + aeadSuites = aes128GcmSuites md5Suites = [] md5Suites.append(TLS_RSA_WITH_RC4_128_MD5) @staticmethod - def _filterSuites(suites, settings): + def _filterSuites(suites, settings, version=None): + if version is None: + version = settings.maxVersion macNames = settings.macNames cipherNames = settings.cipherNames keyExchangeNames = settings.keyExchangeNames macSuites = [] if "sha" in macNames: macSuites += CipherSuite.shaSuites - if "sha256" in macNames: + if "sha256" in macNames and version >= (3,3): macSuites += CipherSuite.sha256Suites if "md5" in macNames: macSuites += CipherSuite.md5Suites + if "aead" in macNames and version >= (3,3): + macSuites += CipherSuite.aeadSuites cipherSuites = [] + if "aes128gcm" in cipherNames and version >= (3,3): + cipherSuites += CipherSuite.aes128GcmSuites if "aes128" in cipherNames: cipherSuites += CipherSuite.aes128Suites if "aes256" in cipherNames: @@ -274,8 +291,8 @@ class CipherSuite: srpSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) @staticmethod - def getSrpSuites(settings): - return CipherSuite._filterSuites(CipherSuite.srpSuites, settings) + def getSrpSuites(settings, version=None): + return CipherSuite._filterSuites(CipherSuite.srpSuites, settings, version) srpCertSuites = [] srpCertSuites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA) @@ -283,16 +300,17 @@ class CipherSuite: srpCertSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA) @staticmethod - def getSrpCertSuites(settings): - return CipherSuite._filterSuites(CipherSuite.srpCertSuites, settings) + def getSrpCertSuites(settings, version=None): + return CipherSuite._filterSuites(CipherSuite.srpCertSuites, settings, version) srpAllSuites = srpSuites + srpCertSuites @staticmethod - def getSrpAllSuites(settings): - return CipherSuite._filterSuites(CipherSuite.srpAllSuites, settings) + def getSrpAllSuites(settings, version=None): + return CipherSuite._filterSuites(CipherSuite.srpAllSuites, settings, version) certSuites = [] + certSuites.append(TLS_RSA_WITH_AES_128_GCM_SHA256) certSuites.append(TLS_RSA_WITH_AES_256_CBC_SHA256) certSuites.append(TLS_RSA_WITH_AES_128_CBC_SHA256) certSuites.append(TLS_RSA_WITH_AES_256_CBC_SHA) @@ -302,10 +320,11 @@ class CipherSuite: certSuites.append(TLS_RSA_WITH_RC4_128_MD5) @staticmethod - def getCertSuites(settings): - return CipherSuite._filterSuites(CipherSuite.certSuites, settings) + def getCertSuites(settings, version=None): + return CipherSuite._filterSuites(CipherSuite.certSuites, settings, version) dheCertSuites = [] + dheCertSuites.append(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256) dheCertSuites.append(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) dheCertSuites.append(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) dheCertSuites.append(TLS_DHE_RSA_WITH_AES_256_CBC_SHA) @@ -313,8 +332,8 @@ class CipherSuite: dheCertSuites.append(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) @staticmethod - def getDheCertSuites(settings): - return CipherSuite._filterSuites(CipherSuite.dheCertSuites, settings) + def getDheCertSuites(settings, version=None): + return CipherSuite._filterSuites(CipherSuite.dheCertSuites, settings, version) certAllSuites = srpCertSuites + certSuites + dheCertSuites @@ -323,8 +342,8 @@ class CipherSuite: anonSuites.append(TLS_DH_ANON_WITH_AES_128_CBC_SHA) @staticmethod - def getAnonSuites(settings): - return CipherSuite._filterSuites(CipherSuite.anonSuites, settings) + def getAnonSuites(settings, version=None): + return CipherSuite._filterSuites(CipherSuite.anonSuites, settings, version) dhAllSuites = dheCertSuites + anonSuites diff --git a/third_party/tlslite/tlslite/handshakesettings.py b/third_party/tlslite/tlslite/handshakesettings.py index 2e9e06d..2f11aaa 100644 --- a/third_party/tlslite/tlslite/handshakesettings.py +++ b/third_party/tlslite/tlslite/handshakesettings.py @@ -11,11 +11,9 @@ from .constants import CertificateType from .utils import cryptomath from .utils import cipherfactory -# RC4 is preferred as faster in Python, works in SSL3, and immune to CBC -# issues such as timing attacks -CIPHER_NAMES = ["rc4", "aes256", "aes128", "3des"] -MAC_NAMES = ["sha", "sha256"] # Don't allow "md5" by default. -ALL_MAC_NAMES = ["sha", "sha256", "md5"] +CIPHER_NAMES = ["aes128gcm", "rc4", "aes256", "aes128", "3des"] +MAC_NAMES = ["sha", "sha256", "aead"] # Don't allow "md5" by default. +ALL_MAC_NAMES = MAC_NAMES + ["md5"] KEY_EXCHANGE_NAMES = ["rsa", "dhe_rsa", "srp_sha", "srp_sha_rsa", "dh_anon"] CIPHER_IMPLEMENTATIONS = ["openssl", "pycrypto", "python"] CERTIFICATE_TYPES = ["x509"] @@ -42,7 +40,7 @@ class HandshakeSettings(object): The default is 8193. @type cipherNames: list - @ivar cipherNames: The allowed ciphers, in order of preference. + @ivar cipherNames: The allowed ciphers. The allowed values in this list are 'aes256', 'aes128', '3des', and 'rc4'. If these settings are used with a client handshake, they @@ -68,8 +66,7 @@ class HandshakeSettings(object): @type certificateTypes: list - @ivar certificateTypes: The allowed certificate types, in order of - preference. + @ivar certificateTypes: The allowed certificate types. The only allowed certificate type is 'x509'. This list is only used with a client handshake. The client will advertise to the server which certificate @@ -197,10 +194,6 @@ class HandshakeSettings(object): if not other.maxVersion in ((3,0), (3,1), (3,2), (3,3)): raise ValueError("maxVersion set incorrectly") - if other.maxVersion < (3,3): - # No sha256 pre TLS 1.2 - other.macNames = [e for e in self.macNames if e != "sha256"] - return other def _getCertificateTypes(self): diff --git a/third_party/tlslite/tlslite/tlsconnection.py b/third_party/tlslite/tlslite/tlsconnection.py index 3d97e97..0e13a78 100644 --- a/third_party/tlslite/tlslite/tlsconnection.py +++ b/third_party/tlslite/tlslite/tlsconnection.py @@ -1385,21 +1385,6 @@ class TLSConnection(TLSRecordLayer): def _serverGetClientHello(self, settings, certChain, verifierDB, sessionCache, anon, fallbackSCSV): - #Initialize acceptable cipher suites - cipherSuites = [] - if verifierDB: - if certChain: - cipherSuites += \ - CipherSuite.getSrpCertSuites(settings) - cipherSuites += CipherSuite.getSrpSuites(settings) - elif certChain: - cipherSuites += CipherSuite.getDheCertSuites(settings) - cipherSuites += CipherSuite.getCertSuites(settings) - elif anon: - cipherSuites += CipherSuite.getAnonSuites(settings) - else: - assert(False) - #Tentatively set version to most-desirable version, so if an error #occurs parsing the ClientHello, this is what we'll use for the #error alert @@ -1451,7 +1436,22 @@ class TLSConnection(TLSRecordLayer): else: #Set the version to the client's version - self.version = clientHello.client_version + self.version = clientHello.client_version + + #Initialize acceptable cipher suites + cipherSuites = [] + if verifierDB: + if certChain: + cipherSuites += \ + CipherSuite.getSrpCertSuites(settings, self.version) + cipherSuites += CipherSuite.getSrpSuites(settings, self.version) + elif certChain: + cipherSuites += CipherSuite.getDheCertSuites(settings, self.version) + cipherSuites += CipherSuite.getCertSuites(settings, self.version) + elif anon: + cipherSuites += CipherSuite.getAnonSuites(settings, self.version) + else: + assert(False) #If resumption was requested and we have a session cache... if clientHello.session_id and sessionCache: diff --git a/third_party/tlslite/tlslite/tlsrecordlayer.py b/third_party/tlslite/tlslite/tlsrecordlayer.py index a09499d..c3bcd8c 100644 --- a/third_party/tlslite/tlslite/tlsrecordlayer.py +++ b/third_party/tlslite/tlslite/tlsrecordlayer.py @@ -11,7 +11,8 @@ from __future__ import generators from .utils.compat import * from .utils.cryptomath import * -from .utils.cipherfactory import createAES, createRC4, createTripleDES +from .utils.cipherfactory import createAESGCM, createAES, createRC4, \ + createTripleDES from .utils.codec import * from .errors import * from .messages import * @@ -592,10 +593,30 @@ class TLSRecordLayer(object): if self.fault == Fault.badMAC: macBytes[0] = (macBytes[0]+1) % 256 - #Encrypt for Block or Stream Cipher + #Encrypt for non-NULL cipher. if self._writeState.encContext: + #Seal (for AEAD) + if self._writeState.encContext.isAEAD: + #Assemble the authenticated data. + seqNumBytes = self._writeState.getSeqNumBytes() + authData = seqNumBytes + bytearray([contentType, + self.version[0], + self.version[1], + len(b)//256, + len(b)%256]) + + #The nonce is always the fixed nonce and the sequence number. + nonce = self._writeState.fixedNonce + seqNumBytes + assert len(nonce) == self._writeState.encContext.nonceLength + + b = self._writeState.encContext.seal(nonce, b, authData) + + #The only AEAD supported, AES-GCM, has an explicit variable + #nonce. + b = seqNumBytes + b + #Add padding and encrypt (for Block Cipher): - if self._writeState.encContext.isBlockCipher: + elif self._writeState.encContext.isBlockCipher: #Add TLS 1.1 fixed block if self.version >= (3,2): @@ -967,6 +988,43 @@ class TLSRecordLayer(object): def _decryptRecord(self, recordType, b): if self._readState.encContext: + #Open if it's an AEAD. + if self._readState.encContext.isAEAD: + #The only AEAD supported, AES-GCM, has an explicit variable + #nonce. + explicitNonceLength = 8 + if explicitNonceLength > len(b): + #Publicly invalid. + for result in self._sendError( + AlertDescription.bad_record_mac, + "MAC failure (or padding failure)"): + yield result + nonce = self._readState.fixedNonce + b[:explicitNonceLength] + b = b[8:] + + if self._readState.encContext.tagLength > len(b): + #Publicly invalid. + for result in self._sendError( + AlertDescription.bad_record_mac, + "MAC failure (or padding failure)"): + yield result + + #Assemble the authenticated data. + seqnumBytes = self._readState.getSeqNumBytes() + plaintextLen = len(b) - self._readState.encContext.tagLength + authData = seqnumBytes + bytearray([recordType, self.version[0], + self.version[1], + plaintextLen//256, + plaintextLen%256]) + + b = self._readState.encContext.open(nonce, b, authData) + if b is None: + for result in self._sendError( + AlertDescription.bad_record_mac, + "MAC failure (or padding failure)"): + yield result + yield b + return #Decrypt if it's a block cipher if self._readState.encContext.isBlockCipher: @@ -1064,7 +1122,11 @@ class TLSRecordLayer(object): def _calcPendingStates(self, cipherSuite, masterSecret, clientRandom, serverRandom, implementations): - if cipherSuite in CipherSuite.aes128Suites: + if cipherSuite in CipherSuite.aes128GcmSuites: + keyLength = 16 + ivLength = 4 + createCipherFunc = createAESGCM + elif cipherSuite in CipherSuite.aes128Suites: keyLength = 16 ivLength = 16 createCipherFunc = createAES @@ -1083,7 +1145,10 @@ class TLSRecordLayer(object): else: raise AssertionError() - if cipherSuite in CipherSuite.shaSuites: + if cipherSuite in CipherSuite.aeadSuites: + macLength = 0 + digestmod = None + elif cipherSuite in CipherSuite.shaSuites: macLength = 20 digestmod = hashlib.sha1 elif cipherSuite in CipherSuite.sha256Suites: @@ -1092,8 +1157,12 @@ class TLSRecordLayer(object): elif cipherSuite in CipherSuite.md5Suites: macLength = 16 digestmod = hashlib.md5 + else: + raise AssertionError() - if self.version == (3,0): + if not digestmod: + createMACFunc = None + elif self.version == (3,0): createMACFunc = createMAC_SSL elif self.version in ((3,1), (3,2), (3,3)): createMACFunc = createHMAC @@ -1128,16 +1197,28 @@ class TLSRecordLayer(object): serverKeyBlock = p.getFixBytes(keyLength) clientIVBlock = p.getFixBytes(ivLength) serverIVBlock = p.getFixBytes(ivLength) - clientPendingState.macContext = createMACFunc( - compatHMAC(clientMACBlock), digestmod=digestmod) - serverPendingState.macContext = createMACFunc( - compatHMAC(serverMACBlock), digestmod=digestmod) - clientPendingState.encContext = createCipherFunc(clientKeyBlock, - clientIVBlock, - implementations) - serverPendingState.encContext = createCipherFunc(serverKeyBlock, - serverIVBlock, - implementations) + if digestmod: + # Legacy cipher. + clientPendingState.macContext = createMACFunc( + compatHMAC(clientMACBlock), digestmod=digestmod) + serverPendingState.macContext = createMACFunc( + compatHMAC(serverMACBlock), digestmod=digestmod) + clientPendingState.encContext = createCipherFunc(clientKeyBlock, + clientIVBlock, + implementations) + serverPendingState.encContext = createCipherFunc(serverKeyBlock, + serverIVBlock, + implementations) + else: + # AEAD. + clientPendingState.macContext = None + serverPendingState.macContext = None + clientPendingState.encContext = createCipherFunc(clientKeyBlock, + implementations) + serverPendingState.encContext = createCipherFunc(serverKeyBlock, + implementations) + clientPendingState.fixedNonce = clientIVBlock + serverPendingState.fixedNonce = serverIVBlock #Assign new connection states to pending states if self._client: diff --git a/third_party/tlslite/tlslite/utils/aes.py b/third_party/tlslite/tlslite/utils/aes.py index 95afaa3..5a038fb 100644 --- a/third_party/tlslite/tlslite/utils/aes.py +++ b/third_party/tlslite/tlslite/utils/aes.py @@ -12,6 +12,7 @@ class AES(object): if len(IV) != 16: raise AssertionError() self.isBlockCipher = True + self.isAEAD = False self.block_size = 16 self.implementation = implementation if len(key)==16: @@ -31,4 +32,4 @@ class AES(object): #CBC-Mode decryption, returns plaintext #WARNING: *MAY* modify the input as well def decrypt(self, ciphertext): - assert(len(ciphertext) % 16 == 0) \ No newline at end of file + assert(len(ciphertext) % 16 == 0) diff --git a/third_party/tlslite/tlslite/utils/aesgcm.py b/third_party/tlslite/tlslite/utils/aesgcm.py new file mode 100644 index 0000000..7319c26 --- /dev/null +++ b/third_party/tlslite/tlslite/utils/aesgcm.py @@ -0,0 +1,193 @@ +# Author: Google +# See the LICENSE file for legal information regarding use of this file. + +# GCM derived from Go's implementation in crypto/cipher. +# +# https://golang.org/src/crypto/cipher/gcm.go + +# GCM works over elements of the field GF(2^128), each of which is a 128-bit +# polynomial. Throughout this implementation, polynomials are represented as +# Python integers with the low-order terms at the most significant bits. So a +# 128-bit polynomial is an integer from 0 to 2^128-1 with the most significant +# bit representing the x^0 term and the least significant bit representing the +# x^127 term. This bit reversal also applies to polynomials used as indices in a +# look-up table. + +from .cryptomath import bytesToNumber, numberToByteArray + +class AESGCM(object): + """ + AES-GCM implementation. Note: this implementation does not attempt + to be side-channel resistant. It's also rather slow. + """ + + def __init__(self, key, implementation, rawAesEncrypt): + self.isBlockCipher = False + self.isAEAD = True + self.nonceLength = 12 + self.tagLength = 16 + self.implementation = implementation + if len(key) == 16: + self.name = "aes128gcm" + elif len(key) == 32: + self.name = "aes256gcm" + else: + raise AssertionError() + + self._rawAesEncrypt = rawAesEncrypt + + # The GCM key is AES(0). + h = bytesToNumber(self._rawAesEncrypt(bytearray(16))) + + # Pre-compute all 4-bit multiples of h. Note that bits are reversed + # because our polynomial representation places low-order terms at the + # most significant bit. Thus x^0 * h = h is at index 0b1000 = 8 and + # x^1 * h is at index 0b0100 = 4. + self._productTable = [0] * 16 + self._productTable[_reverseBits(1)] = h + for i in range(2, 16, 2): + self._productTable[_reverseBits(i)] = \ + _gcmShift(self._productTable[_reverseBits(i/2)]) + self._productTable[_reverseBits(i+1)] = \ + _gcmAdd(self._productTable[_reverseBits(i)], h) + + def _rawAesCtrEncrypt(self, counter, inp): + """ + Encrypts (or decrypts) plaintext with AES-CTR. counter is modified. + """ + out = bytearray(len(inp)) + for i in range(0, len(out), 16): + mask = self._rawAesEncrypt(counter) + for j in range(i, min(len(out), i + 16)): + out[j] = inp[j] ^ mask[j-i] + _inc32(counter) + return out + + def _auth(self, ciphertext, ad, tagMask): + y = 0 + y = self._update(y, ad) + y = self._update(y, ciphertext) + y ^= (len(ad) << (3 + 64)) | (len(ciphertext) << 3) + y = self._mul(y) + y ^= bytesToNumber(tagMask) + return numberToByteArray(y, 16) + + def _update(self, y, data): + for i in range(0, len(data) // 16): + y ^= bytesToNumber(data[16*i:16*i+16]) + y = self._mul(y) + extra = len(data) % 16 + if extra != 0: + block = bytearray(16) + block[:extra] = data[-extra:] + y ^= bytesToNumber(block) + y = self._mul(y) + return y + + def _mul(self, y): + """ Returns y*H, where H is the GCM key. """ + ret = 0 + # Multiply H by y 4 bits at a time, starting with the highest power + # terms. + for i in range(0, 128, 4): + # Multiply by x^4. The reduction for the top four terms is + # precomputed. + retHigh = ret & 0xf + ret >>= 4 + ret ^= (_gcmReductionTable[retHigh] << (128-16)) + + # Add in y' * H where y' are the next four terms of y, shifted down + # to the x^0..x^4. This is one of the pre-computed multiples of + # H. The multiplication by x^4 shifts them back into place. + ret ^= self._productTable[y & 0xf] + y >>= 4 + assert y == 0 + return ret + + def seal(self, nonce, plaintext, data): + """ + Encrypts and authenticates plaintext using nonce and data. Returns the + ciphertext, consisting of the encrypted plaintext and tag concatenated. + """ + + if len(nonce) != 12: + raise ValueError("Bad nonce length") + + # The initial counter value is the nonce, followed by a 32-bit counter + # that starts at 1. It's used to compute the tag mask. + counter = bytearray(16) + counter[:12] = nonce + counter[-1] = 1 + tagMask = self._rawAesEncrypt(counter) + + # The counter starts at 2 for the actual encryption. + counter[-1] = 2 + ciphertext = self._rawAesCtrEncrypt(counter, plaintext) + + tag = self._auth(ciphertext, data, tagMask) + + return ciphertext + tag + + def open(self, nonce, ciphertext, data): + """ + Decrypts and authenticates ciphertext using nonce and data. If the + tag is valid, the plaintext is returned. If the tag is invalid, + returns None. + """ + + if len(nonce) != 12: + raise ValueError("Bad nonce length") + if len(ciphertext) < 16: + return None + + tag = ciphertext[-16:] + ciphertext = ciphertext[:-16] + + # The initial counter value is the nonce, followed by a 32-bit counter + # that starts at 1. It's used to compute the tag mask. + counter = bytearray(16) + counter[:12] = nonce + counter[-1] = 1 + tagMask = self._rawAesEncrypt(counter) + + if tag != self._auth(ciphertext, data, tagMask): + return None + + # The counter starts at 2 for the actual decryption. + counter[-1] = 2 + return self._rawAesCtrEncrypt(counter, ciphertext) + +def _reverseBits(i): + assert i < 16 + i = ((i << 2) & 0xc) | ((i >> 2) & 0x3) + i = ((i << 1) & 0xa) | ((i >> 1) & 0x5) + return i + +def _gcmAdd(x, y): + return x ^ y + +def _gcmShift(x): + # Multiplying by x is a right shift, due to bit order. + highTermSet = x & 1 + x >>= 1 + if highTermSet: + # The x^127 term was shifted up to x^128, so subtract a 1+x+x^2+x^7 + # term. This is 0b11100001 or 0xe1 when represented as an 8-bit + # polynomial. + x ^= 0xe1 << (128-8) + return x + +def _inc32(counter): + for i in range(len(counter)-1, len(counter)-5, -1): + counter[i] = (counter[i] + 1) % 256 + if counter[i] != 0: + break + return counter + +# _gcmReductionTable[i] is i * (1+x+x^2+x^7) for all 4-bit polynomials i. The +# result is stored as a 16-bit polynomial. This is used in the reduction step to +# multiply elements of GF(2^128) by x^4. +_gcmReductionTable = [ + 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0, + 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0, +] diff --git a/third_party/tlslite/tlslite/utils/cipherfactory.py b/third_party/tlslite/tlslite/utils/cipherfactory.py index 20e20f1..d525644 100644 --- a/third_party/tlslite/tlslite/utils/cipherfactory.py +++ b/third_party/tlslite/tlslite/utils/cipherfactory.py @@ -6,6 +6,7 @@ import os from tlslite.utils import python_aes +from tlslite.utils import python_aesgcm from tlslite.utils import python_rc4 from tlslite.utils import cryptomath @@ -20,6 +21,7 @@ if cryptomath.m2cryptoLoaded: if cryptomath.pycryptoLoaded: from tlslite.utils import pycrypto_aes + from tlslite.utils import pycrypto_aesgcm from tlslite.utils import pycrypto_rc4 from tlslite.utils import pycrypto_tripledes tripleDESPresent = True @@ -52,6 +54,25 @@ def createAES(key, IV, implList=None): return python_aes.new(key, 2, IV) raise NotImplementedError() +def createAESGCM(key, implList=None): + """Create a new AESGCM object. + + @type key: bytearray + @param key: A 16 or 32 byte byte array. + + @rtype: L{tlslite.utils.AESGCM} + @return: An AESGCM object. + """ + if implList == None: + implList = ["pycrypto", "python"] + + for impl in implList: + if impl == "pycrypto" and cryptomath.pycryptoLoaded: + return pycrypto_aesgcm.new(key) + if impl == "python": + return python_aesgcm.new(key) + raise NotImplementedError() + def createRC4(key, IV, implList=None): """Create a new RC4 object. @@ -99,4 +120,4 @@ def createTripleDES(key, IV, implList=None): return openssl_tripledes.new(key, 2, IV) elif impl == "pycrypto" and cryptomath.pycryptoLoaded: return pycrypto_tripledes.new(key, 2, IV) - raise NotImplementedError() \ No newline at end of file + raise NotImplementedError() diff --git a/third_party/tlslite/tlslite/utils/pycrypto_aesgcm.py b/third_party/tlslite/tlslite/utils/pycrypto_aesgcm.py new file mode 100644 index 0000000..ee187ee --- /dev/null +++ b/third_party/tlslite/tlslite/utils/pycrypto_aesgcm.py @@ -0,0 +1,16 @@ +# Author: Google +# See the LICENSE file for legal information regarding use of this file. + +"""PyCrypto AES-GCM implementation.""" + +from .cryptomath import * +from .aesgcm import AESGCM + +if pycryptoLoaded: + import Crypto.Cipher.AES + + def new(key): + cipher = Crypto.Cipher.AES.new(bytes(key)) + def encrypt(plaintext): + return bytearray(cipher.encrypt(bytes(plaintext))) + return AESGCM(key, "pycrypto", encrypt) diff --git a/third_party/tlslite/tlslite/utils/python_aesgcm.py b/third_party/tlslite/tlslite/utils/python_aesgcm.py new file mode 100644 index 0000000..80a5fd5 --- /dev/null +++ b/third_party/tlslite/tlslite/utils/python_aesgcm.py @@ -0,0 +1,10 @@ +# Author: Google +# See the LICENSE file for legal information regarding use of this file. + +"""Pure-Python AES-GCM implementation.""" + +from .aesgcm import AESGCM +from .rijndael import rijndael + +def new(key): + return AESGCM(key, "python", rijndael(key, 16).encrypt) diff --git a/third_party/tlslite/tlslite/utils/rc4.py b/third_party/tlslite/tlslite/utils/rc4.py index 809026a..3853f5b 100644 --- a/third_party/tlslite/tlslite/utils/rc4.py +++ b/third_party/tlslite/tlslite/utils/rc4.py @@ -9,6 +9,7 @@ class RC4(object): if len(keyBytes) < 16 or len(keyBytes) > 256: raise ValueError() self.isBlockCipher = False + self.isAEAD = False self.name = "rc4" self.implementation = implementation @@ -16,4 +17,4 @@ class RC4(object): raise NotImplementedError() def decrypt(self, ciphertext): - raise NotImplementedError() \ No newline at end of file + raise NotImplementedError() diff --git a/third_party/tlslite/tlslite/utils/tripledes.py b/third_party/tlslite/tlslite/utils/tripledes.py index 0b4d075..ddcdcad 100644 --- a/third_party/tlslite/tlslite/utils/tripledes.py +++ b/third_party/tlslite/tlslite/utils/tripledes.py @@ -12,6 +12,7 @@ class TripleDES(object): if len(IV) != 8: raise ValueError() self.isBlockCipher = True + self.isAEAD = False self.block_size = 8 self.implementation = implementation self.name = "3des"