| 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" |