1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 from __future__ import division
17 from .cryptomath import bytesToNumber, numberToByteArray
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
41 h = bytesToNumber(self._rawAesEncrypt(bytearray(16)))
42
43
44
45
46
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
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):
76
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
90 """ Returns y*H, where H is the GCM key. """
91 ret = 0
92
93
94 for i in range(0, 128, 4):
95
96
97 retHigh = ret & 0xf
98 ret >>= 4
99 ret ^= (AESGCM._gcmReductionTable[retHigh] << (128-16))
100
101
102
103
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
119
120 counter = bytearray(16)
121 counter[:12] = nonce
122 counter[-1] = 1
123 tagMask = self._rawAesEncrypt(counter)
124
125
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
149
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
159 counter[-1] = 2
160 return self._rawAesCtrEncrypt(counter, ciphertext)
161
162 @staticmethod
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
172
173 @staticmethod
175
176 highTermSet = x & 1
177 x >>= 1
178 if highTermSet:
179
180
181
182 x ^= 0xe1 << (128-8)
183 return x
184
185 @staticmethod
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
194
195
196 _gcmReductionTable = [
197 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
198 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
199 ]
200