1 #!/usr/local/bin/python2
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
33 from __future__ import print_function
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', 'ccr0', 'ccp0' ]
54 desmodules = [ 'cryptosoft0', ]
55 shamodules = [ 'cryptosoft0', 'aesni0', 'ccr0', 'ccp0' ]
57 def GenTestCase(cname):
59 crid = cryptodev.Crypto.findcrid(cname)
63 class GendCryptoTestCase(unittest.TestCase):
67 @unittest.skipIf(cname not in aesmodules, 'skipping AES-XTS on %s' % (cname))
69 for i in katg('XTSTestVectors/format tweak value input - data unit seq no', '*.rsp'):
70 self.runXTS(i, cryptodev.CRYPTO_AES_XTS)
72 @unittest.skipIf(cname not in aesmodules, 'skipping AES-CBC on %s' % (cname))
74 for i in katg('KAT_AES', 'CBC[GKV]*.rsp'):
77 @unittest.skipIf(cname not in aesmodules, 'skipping AES-CCM on %s' % (cname))
79 for i in katg('ccmtestvectors', 'V*.rsp'):
82 for i in katg('ccmtestvectors', 'D*.rsp'):
85 @unittest.skipIf(cname not in aesmodules, 'skipping AES-GCM on %s' % (cname))
87 for i in katg('gcmtestvectors', 'gcmEncrypt*'):
88 self.runGCM(i, 'ENCRYPT')
90 for i in katg('gcmtestvectors', 'gcmDecrypt*'):
91 self.runGCM(i, 'DECRYPT')
93 _gmacsizes = { 32: cryptodev.CRYPTO_AES_256_NIST_GMAC,
94 24: cryptodev.CRYPTO_AES_192_NIST_GMAC,
95 16: cryptodev.CRYPTO_AES_128_NIST_GMAC,
97 def runGCM(self, fname, mode):
101 curfun = Crypto.encrypt
102 elif mode == 'DECRYPT':
104 curfun = Crypto.decrypt
106 raise RuntimeError('unknown mode: %r' % repr(mode))
108 columns = [ 'Count', 'Key', 'IV', 'CT', 'AAD', 'Tag', 'PT', ]
109 with cryptodev.KATParser(fname, columns) as parser:
110 self.runGCMWithParser(parser, mode)
112 def runGCMWithParser(self, parser, mode):
113 for _, lines in next(parser):
115 curcnt = int(data['Count'])
116 cipherkey = binascii.unhexlify(data['Key'])
117 iv = binascii.unhexlify(data['IV'])
118 aad = binascii.unhexlify(data['AAD'])
119 tag = binascii.unhexlify(data['Tag'])
120 if 'FAIL' not in data:
121 pt = binascii.unhexlify(data['PT'])
122 ct = binascii.unhexlify(data['CT'])
125 # XXX - isn't supported
129 c = Crypto(cryptodev.CRYPTO_AES_NIST_GCM_16,
131 mac=self._gmacsizes[len(cipherkey)],
132 mackey=cipherkey, crid=crid,
134 except EnvironmentError as e:
135 # Can't test algorithms the driver does not support.
136 if e.errno != errno.EOPNOTSUPP:
140 if mode == 'ENCRYPT':
142 rct, rtag = c.encrypt(pt, iv, aad)
143 except EnvironmentError as e:
144 # Can't test inputs the driver does not support.
145 if e.errno != errno.EINVAL:
148 rtag = rtag[:len(tag)]
149 data['rct'] = binascii.hexlify(rct)
150 data['rtag'] = binascii.hexlify(rtag)
151 self.assertEqual(rct, ct, repr(data))
152 self.assertEqual(rtag, tag, repr(data))
156 args = (ct, iv, aad, tag)
158 self.assertRaises(IOError,
162 rpt, rtag = c.decrypt(*args)
163 except EnvironmentError as e:
164 # Can't test inputs the driver does not support.
165 if e.errno != errno.EINVAL:
168 data['rpt'] = binascii.hexlify(rpt)
169 data['rtag'] = binascii.hexlify(rtag)
170 self.assertEqual(rpt, pt,
173 def runCBC(self, fname):
174 columns = [ 'COUNT', 'KEY', 'IV', 'PLAINTEXT', 'CIPHERTEXT', ]
175 with cryptodev.KATParser(fname, columns) as parser:
176 self.runCBCWithParser(parser)
178 def runCBCWithParser(self, parser):
180 for mode, lines in next(parser):
181 if mode == 'ENCRYPT':
183 curfun = Crypto.encrypt
184 elif mode == 'DECRYPT':
186 curfun = Crypto.decrypt
188 raise RuntimeError('unknown mode: %r' % repr(mode))
191 curcnt = int(data['COUNT'])
192 cipherkey = binascii.unhexlify(data['KEY'])
193 iv = binascii.unhexlify(data['IV'])
194 pt = binascii.unhexlify(data['PLAINTEXT'])
195 ct = binascii.unhexlify(data['CIPHERTEXT'])
200 c = Crypto(cryptodev.CRYPTO_AES_CBC, cipherkey, crid=crid)
201 r = curfun(c, pt, iv)
202 self.assertEqual(r, ct)
204 def runXTS(self, fname, meth):
205 columns = [ 'COUNT', 'DataUnitLen', 'Key', 'DataUnitSeqNumber', 'PT',
207 with cryptodev.KATParser(fname, columns) as parser:
208 self.runXTSWithParser(parser, meth)
210 def runXTSWithParser(self, parser, meth):
212 for mode, lines in next(parser):
213 if mode == 'ENCRYPT':
215 curfun = Crypto.encrypt
216 elif mode == 'DECRYPT':
218 curfun = Crypto.decrypt
220 raise RuntimeError('unknown mode: %r' % repr(mode))
223 curcnt = int(data['COUNT'])
224 nbits = int(data['DataUnitLen'])
225 cipherkey = binascii.unhexlify(data['Key'])
226 iv = struct.pack('QQ', int(data['DataUnitSeqNumber']), 0)
227 pt = binascii.unhexlify(data['PT'])
228 ct = binascii.unhexlify(data['CT'])
231 # XXX - mark as skipped
237 c = Crypto(meth, cipherkey, crid=crid)
238 r = curfun(c, pt, iv)
239 except EnvironmentError as e:
240 # Can't test hashes the driver does not support.
241 if e.errno != errno.EOPNOTSUPP:
244 self.assertEqual(r, ct)
246 def runCCMEncrypt(self, fname):
247 with cryptodev.KATCCMParser(fname) as parser:
248 self.runCCMEncryptWithParser(parser)
250 def runCCMEncryptWithParser(self, parser):
251 for data in next(parser):
252 Nlen = int(data['Nlen'])
254 # OCF only supports 12 byte IVs
256 key = binascii.unhexlify(data['Key'])
257 nonce = binascii.unhexlify(data['Nonce'])
258 Alen = int(data['Alen'])
260 aad = binascii.unhexlify(data['Adata'])
263 payload = binascii.unhexlify(data['Payload'])
264 ct = binascii.unhexlify(data['CT'])
267 c = Crypto(crid=crid,
268 cipher=cryptodev.CRYPTO_AES_CCM_16,
270 mac=cryptodev.CRYPTO_AES_CCM_CBC_MAC,
271 mackey=key, maclen=16)
272 r, tag = Crypto.encrypt(c, payload,
274 except EnvironmentError as e:
275 if e.errno != errno.EOPNOTSUPP:
280 self.assertEqual(out, ct,
281 "Count " + data['Count'] + " Actual: " + \
282 repr(binascii.hexlify(out)) + " Expected: " + \
283 repr(data) + " on " + cname)
285 def runCCMDecrypt(self, fname):
286 with cryptodev.KATCCMParser(fname) as parser:
287 self.runCCMDecryptWithParser(parser)
289 def runCCMDecryptWithParser(self, parser):
290 # XXX: Note that all of the current CCM
291 # decryption test vectors use IV and tag sizes
292 # that aren't supported by OCF none of the
293 # tests are actually ran.
294 for data in next(parser):
295 Nlen = int(data['Nlen'])
297 # OCF only supports 12 byte IVs
299 Tlen = int(data['Tlen'])
301 # OCF only supports 16 byte tags
303 key = binascii.unhexlify(data['Key'])
304 nonce = binascii.unhexlify(data['Nonce'])
305 Alen = int(data['Alen'])
307 aad = binascii.unhexlify(data['Adata'])
310 ct = binascii.unhexlify(data['CT'])
315 c = Crypto(crid=crid,
316 cipher=cryptodev.CRYPTO_AES_CCM_16,
318 mac=cryptodev.CRYPTO_AES_CCM_CBC_MAC,
319 mackey=key, maclen=16)
320 except EnvironmentError as e:
321 if e.errno != errno.EOPNOTSUPP:
325 if data['Result'] == 'Fail':
326 self.assertRaises(IOError,
327 c.decrypt, payload, nonce, aad, tag)
329 r = Crypto.decrypt(c, payload, nonce,
332 payload = binascii.unhexlify(data['Payload'])
333 plen = int(data('Plen'))
334 payload = payload[:plen]
335 self.assertEqual(r, payload,
336 "Count " + data['Count'] + \
337 " Actual: " + repr(binascii.hexlify(r)) + \
338 " Expected: " + repr(data) + \
344 @unittest.skipIf(cname not in desmodules, 'skipping DES on %s' % (cname))
346 for i in katg('KAT_TDES', 'TCBC[a-z]*.rsp'):
349 def runTDES(self, fname):
350 columns = [ 'COUNT', 'KEYs', 'IV', 'PLAINTEXT', 'CIPHERTEXT', ]
351 with cryptodev.KATParser(fname, columns) as parser:
352 self.runTDESWithParser(parser)
354 def runTDESWithParser(self, parser):
356 for mode, lines in next(parser):
357 if mode == 'ENCRYPT':
359 curfun = Crypto.encrypt
360 elif mode == 'DECRYPT':
362 curfun = Crypto.decrypt
364 raise RuntimeError('unknown mode: %r' % repr(mode))
367 curcnt = int(data['COUNT'])
368 key = data['KEYs'] * 3
369 cipherkey = binascii.unhexlify(key)
370 iv = binascii.unhexlify(data['IV'])
371 pt = binascii.unhexlify(data['PLAINTEXT'])
372 ct = binascii.unhexlify(data['CIPHERTEXT'])
377 c = Crypto(cryptodev.CRYPTO_3DES_CBC, cipherkey, crid=crid)
378 r = curfun(c, pt, iv)
379 self.assertEqual(r, ct)
384 @unittest.skipIf(cname not in shamodules, 'skipping SHA on %s' % str(cname))
386 for i in katg('shabytetestvectors', 'SHA*Msg.rsp'):
389 def runSHA(self, fname):
390 # Skip SHA512_(224|256) tests
391 if fname.find('SHA512_') != -1:
393 columns = [ 'Len', 'Msg', 'MD' ]
394 with cryptodev.KATParser(fname, columns) as parser:
395 self.runSHAWithParser(parser)
397 def runSHAWithParser(self, parser):
398 for hashlength, lines in next(parser):
399 # E.g., hashlength will be "L=20" (bytes)
400 hashlen = int(hashlength.split("=")[1])
403 alg = cryptodev.CRYPTO_SHA1
405 alg = cryptodev.CRYPTO_SHA2_224
407 alg = cryptodev.CRYPTO_SHA2_256
409 alg = cryptodev.CRYPTO_SHA2_384
411 alg = cryptodev.CRYPTO_SHA2_512
413 # Skip unsupported hashes
414 # Slurp remaining input in section
420 msg = binascii.unhexlify(data['Msg'])
421 msg = msg[:int(data['Len'])]
422 md = binascii.unhexlify(data['MD'])
425 c = Crypto(mac=alg, crid=crid,
427 except EnvironmentError as e:
428 # Can't test hashes the driver does not support.
429 if e.errno != errno.EOPNOTSUPP:
433 _, r = c.encrypt(msg, iv="")
435 self.assertEqual(r, md, "Actual: " + \
436 repr(binascii.hexlify(r)) + " Expected: " + repr(data) + " on " + cname)
438 @unittest.skipIf(cname not in shamodules, 'skipping SHA-HMAC on %s' % str(cname))
439 def test_sha1hmac(self):
440 for i in katg('hmactestvectors', 'HMAC.rsp'):
443 def runSHA1HMAC(self, fname):
444 columns = [ 'Count', 'Klen', 'Tlen', 'Key', 'Msg', 'Mac' ]
445 with cryptodev.KATParser(fname, columns) as parser:
446 self.runSHA1HMACWithParser(parser)
448 def runSHA1HMACWithParser(self, parser):
449 for hashlength, lines in next(parser):
450 # E.g., hashlength will be "L=20" (bytes)
451 hashlen = int(hashlength.split("=")[1])
455 alg = cryptodev.CRYPTO_SHA1_HMAC
458 alg = cryptodev.CRYPTO_SHA2_224_HMAC
461 alg = cryptodev.CRYPTO_SHA2_256_HMAC
464 alg = cryptodev.CRYPTO_SHA2_384_HMAC
467 alg = cryptodev.CRYPTO_SHA2_512_HMAC
470 # Skip unsupported hashes
471 # Slurp remaining input in section
477 key = binascii.unhexlify(data['Key'])
478 msg = binascii.unhexlify(data['Msg'])
479 mac = binascii.unhexlify(data['Mac'])
480 tlen = int(data['Tlen'])
482 if len(key) > blocksize:
486 c = Crypto(mac=alg, mackey=key,
487 crid=crid, maclen=hashlen)
488 except EnvironmentError as e:
489 # Can't test hashes the driver does not support.
490 if e.errno != errno.EOPNOTSUPP:
494 _, r = c.encrypt(msg, iv="")
496 self.assertEqual(r[:tlen], mac, "Actual: " + \
497 repr(binascii.hexlify(r)) + " Expected: " + repr(data))
499 return GendCryptoTestCase
501 cryptosoft = GenTestCase('cryptosoft0')
502 aesni = GenTestCase('aesni0')
503 ccr = GenTestCase('ccr0')
504 ccp = GenTestCase('ccp0')
506 if __name__ == '__main__':