blob: 9d4768dd9919a7f642c97e59af9ef22063966851 [file] [log] [blame]
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"