1 #!/usr/local/bin/python3
3 # Copyright (c) 2014 The FreeBSD Foundation
5 # Copyright 2019 Enji Cooper
7 # This software was developed by John-Mark Gurney under
8 # the sponsorship from the FreeBSD Foundation.
9 # Redistribution and use in source and binary forms, with or without
10 # modification, are permitted provided that the following conditions
12 # 1. Redistributions of source code must retain the above copyright
13 # notice, this list of conditions and the following disclaimer.
14 # 2. Redistributions in binary form must reproduce the above copyright
15 # notice, this list of conditions and the following disclaimer in the
16 # documentation and/or other materials provided with the distribution.
18 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 from cryptodev import *
43 from glob import iglob
45 katdir = '/usr/local/share/nist-kat'
48 assert os.path.exists(katdir), "Please 'pkg install nist-kat'"
49 if not os.path.exists(os.path.join(katdir, base)):
50 raise unittest.SkipTest("Missing %s test vectors" % (base))
51 return iglob(os.path.join(katdir, base, glob))
53 aesmodules = [ 'cryptosoft0', 'aesni0', 'armv8crypto0', 'ccr0', 'ccp0', 'safexcel0' ]
54 shamodules = [ 'cryptosoft0', 'aesni0', 'armv8crypto0', 'ccr0', 'ccp0', 'ossl0', 'safexcel0' ]
56 def GenTestCase(cname):
58 crid = cryptodev.Crypto.findcrid(cname)
62 class GendCryptoTestCase(unittest.TestCase):
66 @unittest.skipIf(cname not in aesmodules, 'skipping AES-XTS on %s' % (cname))
68 for i in katg('XTSTestVectors/format tweak value input - data unit seq no', '*.rsp'):
69 self.runXTS(i, cryptodev.CRYPTO_AES_XTS)
71 @unittest.skipIf(cname not in aesmodules, 'skipping AES-CBC on %s' % (cname))
73 for i in katg('KAT_AES', 'CBC[GKV]*.rsp'):
76 @unittest.skipIf(cname not in aesmodules, 'skipping AES-CCM on %s' % (cname))
78 for i in katg('ccmtestvectors', 'V*.rsp'):
81 for i in katg('ccmtestvectors', 'D*.rsp'):
84 @unittest.skipIf(cname not in aesmodules, 'skipping AES-GCM on %s' % (cname))
86 for i in katg('gcmtestvectors', 'gcmEncrypt*'):
87 self.runGCM(i, 'ENCRYPT')
89 for i in katg('gcmtestvectors', 'gcmDecrypt*'):
90 self.runGCM(i, 'DECRYPT')
92 def runGCM(self, fname, mode):
96 curfun = Crypto.encrypt
97 elif mode == 'DECRYPT':
99 curfun = Crypto.decrypt
101 raise RuntimeError('unknown mode: %r' % repr(mode))
103 columns = [ 'Count', 'Key', 'IV', 'CT', 'AAD', 'Tag', 'PT', ]
104 with cryptodev.KATParser(fname, columns) as parser:
105 self.runGCMWithParser(parser, mode)
107 def runGCMWithParser(self, parser, mode):
108 for _, lines in next(parser):
110 curcnt = int(data['Count'])
111 cipherkey = binascii.unhexlify(data['Key'])
112 iv = binascii.unhexlify(data['IV'])
113 aad = binascii.unhexlify(data['AAD'])
114 tag = binascii.unhexlify(data['Tag'])
115 if 'FAIL' not in data:
116 pt = binascii.unhexlify(data['PT'])
117 ct = binascii.unhexlify(data['CT'])
120 # XXX - isn't supported
124 c = Crypto(cryptodev.CRYPTO_AES_NIST_GCM_16,
125 cipherkey, crid=crid,
127 except EnvironmentError as e:
128 # Can't test algorithms the driver does not support.
129 if e.errno != errno.EOPNOTSUPP:
133 if mode == 'ENCRYPT':
135 rct, rtag = c.encrypt(pt, iv, aad)
136 except EnvironmentError as e:
137 # Can't test inputs the driver does not support.
138 if e.errno != errno.EINVAL:
141 rtag = rtag[:len(tag)]
142 data['rct'] = binascii.hexlify(rct)
143 data['rtag'] = binascii.hexlify(rtag)
144 self.assertEqual(rct, ct, repr(data))
145 self.assertEqual(rtag, tag, repr(data))
149 args = (ct, iv, aad, tag)
151 self.assertRaises(IOError,
155 rpt, rtag = c.decrypt(*args)
156 except EnvironmentError as e:
157 # Can't test inputs the driver does not support.
158 if e.errno != errno.EINVAL:
161 data['rpt'] = binascii.hexlify(rpt)
162 data['rtag'] = binascii.hexlify(rtag)
163 self.assertEqual(rpt, pt,
166 def runCBC(self, fname):
167 columns = [ 'COUNT', 'KEY', 'IV', 'PLAINTEXT', 'CIPHERTEXT', ]
168 with cryptodev.KATParser(fname, columns) as parser:
169 self.runCBCWithParser(parser)
171 def runCBCWithParser(self, parser):
173 for mode, lines in next(parser):
174 if mode == 'ENCRYPT':
176 curfun = Crypto.encrypt
177 elif mode == 'DECRYPT':
179 curfun = Crypto.decrypt
181 raise RuntimeError('unknown mode: %r' % repr(mode))
184 curcnt = int(data['COUNT'])
185 cipherkey = binascii.unhexlify(data['KEY'])
186 iv = binascii.unhexlify(data['IV'])
187 pt = binascii.unhexlify(data['PLAINTEXT'])
188 ct = binascii.unhexlify(data['CIPHERTEXT'])
193 c = Crypto(cryptodev.CRYPTO_AES_CBC, cipherkey, crid=crid)
194 r = curfun(c, pt, iv)
195 self.assertEqual(r, ct)
197 def runXTS(self, fname, meth):
198 columns = [ 'COUNT', 'DataUnitLen', 'Key', 'DataUnitSeqNumber', 'PT',
200 with cryptodev.KATParser(fname, columns) as parser:
201 self.runXTSWithParser(parser, meth)
203 def runXTSWithParser(self, parser, meth):
205 for mode, lines in next(parser):
206 if mode == 'ENCRYPT':
208 curfun = Crypto.encrypt
209 elif mode == 'DECRYPT':
211 curfun = Crypto.decrypt
213 raise RuntimeError('unknown mode: %r' % repr(mode))
216 curcnt = int(data['COUNT'])
217 nbits = int(data['DataUnitLen'])
218 cipherkey = binascii.unhexlify(data['Key'])
219 iv = struct.pack('QQ', int(data['DataUnitSeqNumber']), 0)
220 pt = binascii.unhexlify(data['PT'])
221 ct = binascii.unhexlify(data['CT'])
224 # XXX - mark as skipped
230 c = Crypto(meth, cipherkey, crid=crid)
231 r = curfun(c, pt, iv)
232 except EnvironmentError as e:
233 # Can't test hashes the driver does not support.
234 if e.errno != errno.EOPNOTSUPP:
237 self.assertEqual(r, ct)
239 def runCCMEncrypt(self, fname):
240 with cryptodev.KATCCMParser(fname) as parser:
241 self.runCCMEncryptWithParser(parser)
243 def runCCMEncryptWithParser(self, parser):
244 for data in next(parser):
245 Nlen = int(data['Nlen'])
247 # OCF only supports 12 byte IVs
249 key = binascii.unhexlify(data['Key'])
250 nonce = binascii.unhexlify(data['Nonce'])
251 Alen = int(data['Alen'])
253 aad = binascii.unhexlify(data['Adata'])
256 payload = binascii.unhexlify(data['Payload'])
257 ct = binascii.unhexlify(data['CT'])
260 c = Crypto(crid=crid,
261 cipher=cryptodev.CRYPTO_AES_CCM_16,
263 mac=cryptodev.CRYPTO_AES_CCM_CBC_MAC,
264 mackey=key, maclen=16)
265 r, tag = Crypto.encrypt(c, payload,
267 except EnvironmentError as e:
268 if e.errno != errno.EOPNOTSUPP:
273 self.assertEqual(out, ct,
274 "Count " + data['Count'] + " Actual: " + \
275 repr(binascii.hexlify(out)) + " Expected: " + \
276 repr(data) + " on " + cname)
278 def runCCMDecrypt(self, fname):
279 with cryptodev.KATCCMParser(fname) as parser:
280 self.runCCMDecryptWithParser(parser)
282 def runCCMDecryptWithParser(self, parser):
283 # XXX: Note that all of the current CCM
284 # decryption test vectors use IV and tag sizes
285 # that aren't supported by OCF none of the
286 # tests are actually ran.
287 for data in next(parser):
288 Nlen = int(data['Nlen'])
290 # OCF only supports 12 byte IVs
292 Tlen = int(data['Tlen'])
294 # OCF only supports 16 byte tags
296 key = binascii.unhexlify(data['Key'])
297 nonce = binascii.unhexlify(data['Nonce'])
298 Alen = int(data['Alen'])
300 aad = binascii.unhexlify(data['Adata'])
303 ct = binascii.unhexlify(data['CT'])
308 c = Crypto(crid=crid,
309 cipher=cryptodev.CRYPTO_AES_CCM_16,
311 mac=cryptodev.CRYPTO_AES_CCM_CBC_MAC,
312 mackey=key, maclen=16)
313 except EnvironmentError as e:
314 if e.errno != errno.EOPNOTSUPP:
318 if data['Result'] == 'Fail':
319 self.assertRaises(IOError,
320 c.decrypt, payload, nonce, aad, tag)
322 r = Crypto.decrypt(c, payload, nonce,
325 payload = binascii.unhexlify(data['Payload'])
326 plen = int(data('Plen'))
327 payload = payload[:plen]
328 self.assertEqual(r, payload,
329 "Count " + data['Count'] + \
330 " Actual: " + repr(binascii.hexlify(r)) + \
331 " Expected: " + repr(data) + \
337 @unittest.skipIf(cname not in shamodules, 'skipping SHA on %s' % str(cname))
339 for i in katg('shabytetestvectors', 'SHA*Msg.rsp'):
342 def runSHA(self, fname):
343 # Skip SHA512_(224|256) tests
344 if fname.find('SHA512_') != -1:
346 columns = [ 'Len', 'Msg', 'MD' ]
347 with cryptodev.KATParser(fname, columns) as parser:
348 self.runSHAWithParser(parser)
350 def runSHAWithParser(self, parser):
351 for hashlength, lines in next(parser):
352 # E.g., hashlength will be "L=20" (bytes)
353 hashlen = int(hashlength.split("=")[1])
356 alg = cryptodev.CRYPTO_SHA1
358 alg = cryptodev.CRYPTO_SHA2_224
360 alg = cryptodev.CRYPTO_SHA2_256
362 alg = cryptodev.CRYPTO_SHA2_384
364 alg = cryptodev.CRYPTO_SHA2_512
366 # Skip unsupported hashes
367 # Slurp remaining input in section
373 msg = binascii.unhexlify(data['Msg'])
374 msg = msg[:int(data['Len'])]
375 md = binascii.unhexlify(data['MD'])
378 c = Crypto(mac=alg, crid=crid,
380 except EnvironmentError as e:
381 # Can't test hashes the driver does not support.
382 if e.errno != errno.EOPNOTSUPP:
386 _, r = c.encrypt(msg, iv="")
388 self.assertEqual(r, md, "Actual: " + \
389 repr(binascii.hexlify(r)) + " Expected: " + repr(data) + " on " + cname)
391 @unittest.skipIf(cname not in shamodules, 'skipping SHA-HMAC on %s' % str(cname))
392 def test_sha1hmac(self):
393 for i in katg('hmactestvectors', 'HMAC.rsp'):
396 def runSHA1HMAC(self, fname):
397 columns = [ 'Count', 'Klen', 'Tlen', 'Key', 'Msg', 'Mac' ]
398 with cryptodev.KATParser(fname, columns) as parser:
399 self.runSHA1HMACWithParser(parser)
401 def runSHA1HMACWithParser(self, parser):
402 for hashlength, lines in next(parser):
403 # E.g., hashlength will be "L=20" (bytes)
404 hashlen = int(hashlength.split("=")[1])
408 alg = cryptodev.CRYPTO_SHA1_HMAC
411 alg = cryptodev.CRYPTO_SHA2_224_HMAC
414 alg = cryptodev.CRYPTO_SHA2_256_HMAC
417 alg = cryptodev.CRYPTO_SHA2_384_HMAC
420 alg = cryptodev.CRYPTO_SHA2_512_HMAC
423 # Skip unsupported hashes
424 # Slurp remaining input in section
430 key = binascii.unhexlify(data['Key'])
431 msg = binascii.unhexlify(data['Msg'])
432 mac = binascii.unhexlify(data['Mac'])
433 tlen = int(data['Tlen'])
435 if len(key) > blocksize:
439 c = Crypto(mac=alg, mackey=key,
440 crid=crid, maclen=hashlen)
441 except EnvironmentError as e:
442 # Can't test hashes the driver does not support.
443 if e.errno != errno.EOPNOTSUPP:
447 _, r = c.encrypt(msg, iv="")
449 self.assertEqual(r[:tlen], mac, "Actual: " + \
450 repr(binascii.hexlify(r)) + " Expected: " + repr(data))
452 return GendCryptoTestCase
454 cryptosoft = GenTestCase('cryptosoft0')
455 aesni = GenTestCase('aesni0')
456 armv8crypto = GenTestCase('armv8crypto0')
457 ccr = GenTestCase('ccr0')
458 ccp = GenTestCase('ccp0')
459 ossl = GenTestCase('ossl0')
460 safexcel = GenTestCase('safexcel0')
462 if __name__ == '__main__':