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..9d4768dd9919a7f642c97e59af9ef22063966851 |
--- /dev/null |
+++ b/third_party/tlslite/patches/aes_gcm.patch |
@@ -0,0 +1,770 @@ |
+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" |