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 for i in range(0, len(out), 16):
61 mask = self._rawAesEncrypt(counter)
62 for j in range(i, min(len(out), i + 16)):
63 out[j] = inp[j] ^ mask[j-i]
64 self._inc32(counter)
65 return out
66
67 - def _auth(self, ciphertext, ad, tagMask):
75
77 for i in range(0, len(data) // 16):
78 y ^= bytesToNumber(data[16*i:16*i+16])
79 y = self._mul(y)
80 extra = len(data) % 16
81 if extra != 0:
82 block = bytearray(16)
83 block[:extra] = data[-extra:]
84 y ^= bytesToNumber(block)
85 y = self._mul(y)
86 return y
87
89 """ Returns y*H, where H is the GCM key. """
90 ret = 0
91
92
93 for i in range(0, 128, 4):
94
95
96 retHigh = ret & 0xf
97 ret >>= 4
98 ret ^= (AESGCM._gcmReductionTable[retHigh] << (128-16))
99
100
101
102
103 ret ^= self._productTable[y & 0xf]
104 y >>= 4
105 assert y == 0
106 return ret
107
108 - def seal(self, nonce, plaintext, data):
109 """
110 Encrypts and authenticates plaintext using nonce and data. Returns the
111 ciphertext, consisting of the encrypted plaintext and tag concatenated.
112 """
113
114 if len(nonce) != 12:
115 raise ValueError("Bad nonce length")
116
117
118
119 counter = bytearray(16)
120 counter[:12] = nonce
121 counter[-1] = 1
122 tagMask = self._rawAesEncrypt(counter)
123
124
125 counter[-1] = 2
126 ciphertext = self._rawAesCtrEncrypt(counter, plaintext)
127
128 tag = self._auth(ciphertext, data, tagMask)
129
130 return ciphertext + tag
131
132 - def open(self, nonce, ciphertext, data):
133 """
134 Decrypts and authenticates ciphertext using nonce and data. If the
135 tag is valid, the plaintext is returned. If the tag is invalid,
136 returns None.
137 """
138
139 if len(nonce) != 12:
140 raise ValueError("Bad nonce length")
141 if len(ciphertext) < 16:
142 return None
143
144 tag = ciphertext[-16:]
145 ciphertext = ciphertext[:-16]
146
147
148
149 counter = bytearray(16)
150 counter[:12] = nonce
151 counter[-1] = 1
152 tagMask = self._rawAesEncrypt(counter)
153
154 if tag != self._auth(ciphertext, data, tagMask):
155 return None
156
157
158 counter[-1] = 2
159 return self._rawAesCtrEncrypt(counter, ciphertext)
160
161 @staticmethod
163 assert i < 16
164 i = ((i << 2) & 0xc) | ((i >> 2) & 0x3)
165 i = ((i << 1) & 0xa) | ((i >> 1) & 0x5)
166 return i
167
168 @staticmethod
171
172 @staticmethod
174
175 highTermSet = x & 1
176 x >>= 1
177 if highTermSet:
178
179
180
181 x ^= 0xe1 << (128-8)
182 return x
183
184 @staticmethod
186 for i in range(len(counter)-1, len(counter)-5, -1):
187 counter[i] = (counter[i] + 1) % 256
188 if counter[i] != 0:
189 break
190 return counter
191
192
193
194
195 _gcmReductionTable = [
196 0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
197 0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
198 ]
199