Package tlslite :: Package utils :: Module aesgcm
[hide private]
[frames] | no frames]

Source Code for Module tlslite.utils.aesgcm

  1  # Author: Google 
  2  # See the LICENSE file for legal information regarding use of this file. 
  3   
  4  # GCM derived from Go's implementation in crypto/cipher. 
  5  # 
  6  # https://golang.org/src/crypto/cipher/gcm.go 
  7   
  8  # GCM works over elements of the field GF(2^128), each of which is a 128-bit 
  9  # polynomial. Throughout this implementation, polynomials are represented as 
 10  # Python integers with the low-order terms at the most significant bits. So a 
 11  # 128-bit polynomial is an integer from 0 to 2^128-1 with the most significant 
 12  # bit representing the x^0 term and the least significant bit representing the 
 13  # x^127 term. This bit reversal also applies to polynomials used as indices in a 
 14  # look-up table. 
 15   
 16  from __future__ import division 
 17  from .cryptomath import bytesToNumber, numberToByteArray 
18 19 -class AESGCM(object):
20 """ 21 AES-GCM implementation. Note: this implementation does not attempt 22 to be side-channel resistant. It's also rather slow. 23 """ 24
25 - def __init__(self, key, implementation, rawAesEncrypt):
26 self.isBlockCipher = False 27 self.isAEAD = True 28 self.nonceLength = 12 29 self.tagLength = 16 30 self.implementation = implementation 31 if len(key) == 16: 32 self.name = "aes128gcm" 33 elif len(key) == 32: 34 self.name = "aes256gcm" 35 else: 36 raise AssertionError() 37 38 self._rawAesEncrypt = rawAesEncrypt 39 40 # The GCM key is AES(0). 41 h = bytesToNumber(self._rawAesEncrypt(bytearray(16))) 42 43 # Pre-compute all 4-bit multiples of h. Note that bits are reversed 44 # because our polynomial representation places low-order terms at the 45 # most significant bit. Thus x^0 * h = h is at index 0b1000 = 8 and 46 # x^1 * h is at index 0b0100 = 4. 47 self._productTable = [0] * 16 48 self._productTable[self._reverseBits(1)] = h 49 for i in range(2, 16, 2): 50 self._productTable[self._reverseBits(i)] = \ 51 self._gcmShift(self._productTable[self._reverseBits(i//2)]) 52 self._productTable[self._reverseBits(i+1)] = \ 53 self._gcmAdd(self._productTable[self._reverseBits(i)], h)
54
55 - def _rawAesCtrEncrypt(self, counter, inp):
56 """ 57 Encrypts (or decrypts) plaintext with AES-CTR. counter is modified. 58 """ 59 out = bytearray(len(inp)) 60 rawAesEncrypt = self._rawAesEncrypt 61 for i in range(0, len(out), 16): 62 mask = rawAesEncrypt(counter) 63 for j in range(i, min(len(out), i + 16)): 64 out[j] = inp[j] ^ mask[j-i] 65 self._inc32(counter) 66 return out
67
68 - def _auth(self, ciphertext, ad, tagMask):
69 y = 0 70 y = self._update(y, ad) 71 y = self._update(y, ciphertext) 72 y ^= (len(ad) << (3 + 64)) | (len(ciphertext) << 3) 73 y = self._mul(y) 74 y ^= bytesToNumber(tagMask) 75 return numberToByteArray(y, 16)
76
77 - def _update(self, y, data):
78 for i in range(0, len(data) // 16): 79 y ^= bytesToNumber(data[16*i:16*i+16]) 80 y = self._mul(y) 81 extra = len(data) % 16 82 if extra != 0: 83 block = bytearray(16) 84 block[:extra] = data[-extra:] 85 y ^= bytesToNumber(block) 86 y = self._mul(y) 87 return y
88
89 - def _mul(self, y):
90 """ Returns y*H, where H is the GCM key. """ 91 ret = 0 92 # Multiply H by y 4 bits at a time, starting with the highest power 93 # terms. 94 for i in range(0, 128, 4): 95 # Multiply by x^4. The reduction for the top four terms is 96 # precomputed. 97 retHigh = ret & 0xf 98 ret >>= 4 99 ret ^= (AESGCM._gcmReductionTable[retHigh] << (128-16)) 100 101 # Add in y' * H where y' are the next four terms of y, shifted down 102 # to the x^0..x^4. This is one of the pre-computed multiples of 103 # H. The multiplication by x^4 shifts them back into place. 104 ret ^= self._productTable[y & 0xf] 105 y >>= 4 106 assert y == 0 107 return ret
108
109 - def seal(self, nonce, plaintext, data):
110 """ 111 Encrypts and authenticates plaintext using nonce and data. Returns the 112 ciphertext, consisting of the encrypted plaintext and tag concatenated. 113 """ 114 115 if len(nonce) != 12: 116 raise ValueError("Bad nonce length") 117 118 # The initial counter value is the nonce, followed by a 32-bit counter 119 # that starts at 1. It's used to compute the tag mask. 120 counter = bytearray(16) 121 counter[:12] = nonce 122 counter[-1] = 1 123 tagMask = self._rawAesEncrypt(counter) 124 125 # The counter starts at 2 for the actual encryption. 126 counter[-1] = 2 127 ciphertext = self._rawAesCtrEncrypt(counter, plaintext) 128 129 tag = self._auth(ciphertext, data, tagMask) 130 131 return ciphertext + tag
132
133 - def open(self, nonce, ciphertext, data):
134 """ 135 Decrypts and authenticates ciphertext using nonce and data. If the 136 tag is valid, the plaintext is returned. If the tag is invalid, 137 returns None. 138 """ 139 140 if len(nonce) != 12: 141 raise ValueError("Bad nonce length") 142 if len(ciphertext) < 16: 143 return None 144 145 tag = ciphertext[-16:] 146 ciphertext = ciphertext[:-16] 147 148 # The initial counter value is the nonce, followed by a 32-bit counter 149 # that starts at 1. It's used to compute the tag mask. 150 counter = bytearray(16) 151 counter[:12] = nonce 152 counter[-1] = 1 153 tagMask = self._rawAesEncrypt(counter) 154 155 if tag != self._auth(ciphertext, data, tagMask): 156 return None 157 158 # The counter starts at 2 for the actual decryption. 159 counter[-1] = 2 160 return self._rawAesCtrEncrypt(counter, ciphertext)
161 162 @staticmethod
163 - def _reverseBits(i):
164 assert i < 16 165 i = ((i << 2) & 0xc) | ((i >> 2) & 0x3) 166 i = ((i << 1) & 0xa) | ((i >> 1) & 0x5) 167 return i
168 169 @staticmethod
170 - def _gcmAdd(x, y):
171 return x ^ y
172 173 @staticmethod
174 - def _gcmShift(x):
175 # Multiplying by x is a right shift, due to bit order. 176 highTermSet = x & 1 177 x >>= 1 178 if highTermSet: 179 # The x^127 term was shifted up to x^128, so subtract a 1+x+x^2+x^7 180 # term. This is 0b11100001 or 0xe1 when represented as an 8-bit 181 # polynomial. 182 x ^= 0xe1 << (128-8) 183 return x
184 185 @staticmethod
186 - def _inc32(counter):
187 for i in range(len(counter)-1, len(counter)-5, -1): 188 counter[i] = (counter[i] + 1) % 256 189 if counter[i] != 0: 190 break 191 return counter
192 193 # _gcmReductionTable[i] is i * (1+x+x^2+x^7) for all 4-bit polynomials i. The 194 # result is stored as a 16-bit polynomial. This is used in the reduction step to 195 # multiply elements of GF(2^128) by x^4. 196 _gcmReductionTable = [ 197 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0, 198 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0, 199 ]
200