| Index: third_party/tlslite/patches/aes_gcm.patch
|
| diff --git a/third_party/tlslite/patches/aes_gcm.patch b/third_party/tlslite/patches/aes_gcm.patch
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..657238635ae20955d1b94882f39836bf66ae9f2d
|
| --- /dev/null
|
| +++ b/third_party/tlslite/patches/aes_gcm.patch
|
| @@ -0,0 +1,759 @@
|
| +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..545ff63 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 *
|
| +@@ -594,8 +595,26 @@ class TLSRecordLayer(object):
|
| +
|
| + #Encrypt for Block or Stream Cipher
|
| + if self._writeState.encContext:
|
| ++ #Seal (for AEAD)
|
| ++ if self._writeState.encContext.isAEAD:
|
| ++ #Assemble the AD.
|
| ++ seqnumBytes = self._writeState.getSeqNumBytes()
|
| ++ ad = 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, ad)
|
| ++
|
| ++ #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 +986,36 @@ 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:]
|
| ++
|
| ++ #Assemble the AD.
|
| ++ seqnumBytes = self._readState.getSeqNumBytes()
|
| ++ plaintextLen = len(b) - self._readState.encContext.tagLength
|
| ++ ad = seqnumBytes + bytearray([recordType, self.version[0],
|
| ++ self.version[1],
|
| ++ plaintextLen//256,
|
| ++ plaintextLen%256])
|
| ++
|
| ++ b = self._readState.encContext.open(nonce, b, ad)
|
| ++ 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 +1113,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 +1136,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 +1148,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 +1188,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..4cb7720
|
| +--- /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 with 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 with 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..dc30cd2
|
| +--- /dev/null
|
| ++++ b/third_party/tlslite/tlslite/utils/python_aesgcm.py
|
| +@@ -0,0 +1,11 @@
|
| ++# 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"
|
|
|