Path: blob/main/tests/sys/opencrypto/cryptotest.py
39507 views
#!/usr/local/bin/python31#2# Copyright (c) 2014 The FreeBSD Foundation3# All rights reserved.4# Copyright 2019 Enji Cooper5#6# This software was developed by John-Mark Gurney under7# the sponsorship from the FreeBSD Foundation.8# Redistribution and use in source and binary forms, with or without9# modification, are permitted provided that the following conditions10# are met:11# 1. Redistributions of source code must retain the above copyright12# notice, this list of conditions and the following disclaimer.13# 2. Redistributions in binary form must reproduce the above copyright14# notice, this list of conditions and the following disclaimer in the15# documentation and/or other materials provided with the distribution.16#17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE20# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF27# SUCH DAMAGE.28#29#30313233import binascii34import errno35import cryptodev36import itertools37import os38import struct39import unittest40from cryptodev import *41from glob import iglob4243katdir = '/usr/local/share/nist-kat'4445def katg(base, glob):46assert os.path.exists(katdir), "Please 'pkg install nist-kat'"47if not os.path.exists(os.path.join(katdir, base)):48raise unittest.SkipTest("Missing %s test vectors" % (base))49return iglob(os.path.join(katdir, base, glob))5051aesmodules = [ 'cryptosoft0', 'aesni0', 'armv8crypto0', 'ccr0', 'ccp0', 'ossl0', 'safexcel0', 'qat0' ]52shamodules = [ 'cryptosoft0', 'aesni0', 'armv8crypto0', 'ccr0', 'ccp0', 'ossl0', 'safexcel0', 'qat0' ]5354def GenTestCase(cname):55try:56crid = cryptodev.Crypto.findcrid(cname)57except IOError:58return None5960class GendCryptoTestCase(unittest.TestCase):61###############62##### AES #####63###############64@unittest.skipIf(cname not in aesmodules, 'skipping AES-XTS on %s' % (cname))65def test_xts(self):66for i in katg('XTSTestVectors/format tweak value input - data unit seq no', '*.rsp'):67self.runXTS(i, cryptodev.CRYPTO_AES_XTS)6869@unittest.skipIf(cname not in aesmodules, 'skipping AES-CBC on %s' % (cname))70def test_cbc(self):71for i in katg('KAT_AES', 'CBC[GKV]*.rsp'):72self.runCBC(i)7374@unittest.skipIf(cname not in aesmodules, 'skipping AES-CCM on %s' % (cname))75def test_ccm(self):76for i in katg('ccmtestvectors', 'V*.rsp'):77self.runCCMEncrypt(i)7879for i in katg('ccmtestvectors', 'D*.rsp'):80self.runCCMDecrypt(i)8182@unittest.skipIf(cname not in aesmodules, 'skipping AES-GCM on %s' % (cname))83def test_gcm(self):84for i in katg('gcmtestvectors', 'gcmEncrypt*'):85self.runGCM(i, 'ENCRYPT')8687for i in katg('gcmtestvectors', 'gcmDecrypt*'):88self.runGCM(i, 'DECRYPT')8990def runGCM(self, fname, mode):91curfun = None92if mode == 'ENCRYPT':93swapptct = False94curfun = Crypto.encrypt95elif mode == 'DECRYPT':96swapptct = True97curfun = Crypto.decrypt98else:99raise RuntimeError('unknown mode: %r' % repr(mode))100101columns = [ 'Count', 'Key', 'IV', 'CT', 'AAD', 'Tag', 'PT', ]102with cryptodev.KATParser(fname, columns) as parser:103self.runGCMWithParser(parser, mode)104105def runGCMWithParser(self, parser, mode):106for _, lines in next(parser):107for data in lines:108curcnt = int(data['Count'])109cipherkey = binascii.unhexlify(data['Key'])110iv = binascii.unhexlify(data['IV'])111aad = binascii.unhexlify(data['AAD'])112tag = binascii.unhexlify(data['Tag'])113if 'FAIL' not in data:114pt = binascii.unhexlify(data['PT'])115ct = binascii.unhexlify(data['CT'])116117if len(iv) != 12:118# XXX - isn't supported119continue120121try:122c = Crypto(cryptodev.CRYPTO_AES_NIST_GCM_16,123cipherkey, crid=crid,124maclen=16)125except EnvironmentError as e:126# Can't test algorithms the driver does not support.127if e.errno != errno.EOPNOTSUPP:128raise129continue130131if mode == 'ENCRYPT':132try:133rct, rtag = c.encrypt(pt, iv, aad)134except EnvironmentError as e:135# Can't test inputs the driver does not support.136if e.errno != errno.EINVAL:137raise138continue139rtag = rtag[:len(tag)]140data['rct'] = binascii.hexlify(rct)141data['rtag'] = binascii.hexlify(rtag)142self.assertEqual(rct, ct, repr(data))143self.assertEqual(rtag, tag, repr(data))144else:145if len(tag) != 16:146continue147args = (ct, iv, aad, tag)148if 'FAIL' in data:149self.assertRaises(IOError,150c.decrypt, *args)151else:152try:153rpt, rtag = c.decrypt(*args)154except EnvironmentError as e:155# Can't test inputs the driver does not support.156if e.errno != errno.EINVAL:157raise158continue159data['rpt'] = binascii.hexlify(rpt)160data['rtag'] = binascii.hexlify(rtag)161self.assertEqual(rpt, pt,162repr(data))163164def runCBC(self, fname):165columns = [ 'COUNT', 'KEY', 'IV', 'PLAINTEXT', 'CIPHERTEXT', ]166with cryptodev.KATParser(fname, columns) as parser:167self.runCBCWithParser(parser)168169def runCBCWithParser(self, parser):170curfun = None171for mode, lines in next(parser):172if mode == 'ENCRYPT':173swapptct = False174curfun = Crypto.encrypt175elif mode == 'DECRYPT':176swapptct = True177curfun = Crypto.decrypt178else:179raise RuntimeError('unknown mode: %r' % repr(mode))180181for data in lines:182curcnt = int(data['COUNT'])183cipherkey = binascii.unhexlify(data['KEY'])184iv = binascii.unhexlify(data['IV'])185pt = binascii.unhexlify(data['PLAINTEXT'])186ct = binascii.unhexlify(data['CIPHERTEXT'])187188if swapptct:189pt, ct = ct, pt190# run the fun191c = Crypto(cryptodev.CRYPTO_AES_CBC, cipherkey, crid=crid)192r = curfun(c, pt, iv)193self.assertEqual(r, ct)194195def runXTS(self, fname, meth):196columns = [ 'COUNT', 'DataUnitLen', 'Key', 'DataUnitSeqNumber', 'PT',197'CT']198with cryptodev.KATParser(fname, columns) as parser:199self.runXTSWithParser(parser, meth)200201def runXTSWithParser(self, parser, meth):202curfun = None203for mode, lines in next(parser):204if mode == 'ENCRYPT':205swapptct = False206curfun = Crypto.encrypt207elif mode == 'DECRYPT':208swapptct = True209curfun = Crypto.decrypt210else:211raise RuntimeError('unknown mode: %r' % repr(mode))212213for data in lines:214curcnt = int(data['COUNT'])215nbits = int(data['DataUnitLen'])216cipherkey = binascii.unhexlify(data['Key'])217iv = struct.pack('QQ', int(data['DataUnitSeqNumber']), 0)218pt = binascii.unhexlify(data['PT'])219ct = binascii.unhexlify(data['CT'])220221if nbits % 128 != 0:222# XXX - mark as skipped223continue224if swapptct:225pt, ct = ct, pt226# run the fun227try:228c = Crypto(meth, cipherkey, crid=crid)229r = curfun(c, pt, iv)230except EnvironmentError as e:231# Can't test hashes the driver does not support.232if e.errno != errno.EOPNOTSUPP:233raise234continue235self.assertEqual(r, ct)236237def runCCMEncrypt(self, fname):238with cryptodev.KATCCMParser(fname) as parser:239self.runCCMEncryptWithParser(parser)240241def runCCMEncryptWithParser(self, parser):242for data in next(parser):243Nlen = int(data['Nlen'])244Tlen = int(data['Tlen'])245key = binascii.unhexlify(data['Key'])246nonce = binascii.unhexlify(data['Nonce'])247Alen = int(data['Alen'])248Plen = int(data['Plen'])249if Alen != 0:250aad = binascii.unhexlify(data['Adata'])251else:252aad = None253if Plen != 0:254payload = binascii.unhexlify(data['Payload'])255else:256payload = None257ct = binascii.unhexlify(data['CT'])258259try:260c = Crypto(crid=crid,261cipher=cryptodev.CRYPTO_AES_CCM_16,262key=key,263mackey=key, maclen=Tlen, ivlen=Nlen)264r, tag = Crypto.encrypt(c, payload,265nonce, aad)266except EnvironmentError as e:267if e.errno != errno.EOPNOTSUPP:268raise269continue270271out = r + tag272self.assertEqual(out, ct,273"Count " + data['Count'] + " Actual: " + \274repr(binascii.hexlify(out)) + " Expected: " + \275repr(data) + " on " + cname)276277def runCCMDecrypt(self, fname):278with cryptodev.KATCCMParser(fname) as parser:279self.runCCMDecryptWithParser(parser)280281def runCCMDecryptWithParser(self, parser):282for data in next(parser):283Nlen = int(data['Nlen'])284Tlen = int(data['Tlen'])285key = binascii.unhexlify(data['Key'])286nonce = binascii.unhexlify(data['Nonce'])287Alen = int(data['Alen'])288Plen = int(data['Plen'])289if Alen != 0:290aad = binascii.unhexlify(data['Adata'])291else:292aad = None293ct = binascii.unhexlify(data['CT'])294tag = ct[-Tlen:]295if Plen != 0:296payload = ct[:-Tlen]297else:298payload = None299300try:301c = Crypto(crid=crid,302cipher=cryptodev.CRYPTO_AES_CCM_16,303key=key,304mackey=key, maclen=Tlen, ivlen=Nlen)305except EnvironmentError as e:306if e.errno != errno.EOPNOTSUPP:307raise308continue309310if data['Result'] == 'Fail':311self.assertRaises(IOError,312c.decrypt, payload, nonce, aad, tag)313else:314r, tag = Crypto.decrypt(c, payload, nonce,315aad, tag)316317payload = binascii.unhexlify(data['Payload'])318payload = payload[:Plen]319self.assertEqual(r, payload,320"Count " + data['Count'] + \321" Actual: " + repr(binascii.hexlify(r)) + \322" Expected: " + repr(data) + \323" on " + cname)324325###############326##### SHA #####327###############328@unittest.skipIf(cname not in shamodules, 'skipping SHA on %s' % str(cname))329def test_sha(self):330for i in katg('shabytetestvectors', 'SHA*Msg.rsp'):331self.runSHA(i)332333def runSHA(self, fname):334# Skip SHA512_(224|256) tests335if fname.find('SHA512_') != -1:336return337columns = [ 'Len', 'Msg', 'MD' ]338with cryptodev.KATParser(fname, columns) as parser:339self.runSHAWithParser(parser)340341def runSHAWithParser(self, parser):342for hashlength, lines in next(parser):343# E.g., hashlength will be "L=20" (bytes)344hashlen = int(hashlength.split("=")[1])345346if hashlen == 20:347alg = cryptodev.CRYPTO_SHA1348elif hashlen == 28:349alg = cryptodev.CRYPTO_SHA2_224350elif hashlen == 32:351alg = cryptodev.CRYPTO_SHA2_256352elif hashlen == 48:353alg = cryptodev.CRYPTO_SHA2_384354elif hashlen == 64:355alg = cryptodev.CRYPTO_SHA2_512356else:357# Skip unsupported hashes358# Slurp remaining input in section359for data in lines:360continue361continue362363for data in lines:364msg = binascii.unhexlify(data['Msg'])365msg = msg[:int(data['Len'])]366md = binascii.unhexlify(data['MD'])367368try:369c = Crypto(mac=alg, crid=crid,370maclen=hashlen)371except EnvironmentError as e:372# Can't test hashes the driver does not support.373if e.errno != errno.EOPNOTSUPP:374raise375continue376377_, r = c.encrypt(msg, iv="")378379self.assertEqual(r, md, "Actual: " + \380repr(binascii.hexlify(r)) + " Expected: " + repr(data) + " on " + cname)381382@unittest.skipIf(cname not in shamodules, 'skipping SHA-HMAC on %s' % str(cname))383def test_sha1hmac(self):384for i in katg('hmactestvectors', 'HMAC.rsp'):385self.runSHA1HMAC(i)386387def runSHA1HMAC(self, fname):388columns = [ 'Count', 'Klen', 'Tlen', 'Key', 'Msg', 'Mac' ]389with cryptodev.KATParser(fname, columns) as parser:390self.runSHA1HMACWithParser(parser)391392def runSHA1HMACWithParser(self, parser):393for hashlength, lines in next(parser):394# E.g., hashlength will be "L=20" (bytes)395hashlen = int(hashlength.split("=")[1])396397blocksize = None398if hashlen == 20:399alg = cryptodev.CRYPTO_SHA1_HMAC400blocksize = 64401elif hashlen == 28:402alg = cryptodev.CRYPTO_SHA2_224_HMAC403blocksize = 64404elif hashlen == 32:405alg = cryptodev.CRYPTO_SHA2_256_HMAC406blocksize = 64407elif hashlen == 48:408alg = cryptodev.CRYPTO_SHA2_384_HMAC409blocksize = 128410elif hashlen == 64:411alg = cryptodev.CRYPTO_SHA2_512_HMAC412blocksize = 128413else:414# Skip unsupported hashes415# Slurp remaining input in section416for data in lines:417continue418continue419420for data in lines:421key = binascii.unhexlify(data['Key'])422msg = binascii.unhexlify(data['Msg'])423mac = binascii.unhexlify(data['Mac'])424tlen = int(data['Tlen'])425426if len(key) > blocksize:427continue428429try:430c = Crypto(mac=alg, mackey=key,431crid=crid, maclen=hashlen)432except EnvironmentError as e:433# Can't test hashes the driver does not support.434if e.errno != errno.EOPNOTSUPP:435raise436continue437438_, r = c.encrypt(msg, iv="")439440self.assertEqual(r[:tlen], mac, "Actual: " + \441repr(binascii.hexlify(r)) + " Expected: " + repr(data))442443return GendCryptoTestCase444445cryptosoft = GenTestCase('cryptosoft0')446aesni = GenTestCase('aesni0')447armv8crypto = GenTestCase('armv8crypto0')448ccr = GenTestCase('ccr0')449ccp = GenTestCase('ccp0')450ossl = GenTestCase('ossl0')451safexcel = GenTestCase('safexcel0')452qat = GenTestCase('qat0')453454if __name__ == '__main__':455unittest.main()456457458