1#!/usr/bin/env python 2# 3# Copyright (c) 2014 The FreeBSD Foundation 4# All rights reserved. 5# 6# This software was developed by John-Mark Gurney under 7# the sponsorship from the FreeBSD Foundation. 8# Redistribution and use in source and binary forms, with or without 9# modification, are permitted provided that the following conditions 10# are met: 11# 1. Redistributions of source code must retain the above copyright 12# notice, this list of conditions and the following disclaimer. 13# 2. Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# 17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27# SUCH DAMAGE. 28# 29# $FreeBSD$ 30# 31 32from __future__ import print_function 33import errno 34import cryptodev 35import itertools 36import os 37import struct 38import unittest 39from cryptodev import * 40from glob import iglob 41 42katdir = '/usr/local/share/nist-kat' 43 44def katg(base, glob): 45 assert os.path.exists(os.path.join(katdir, base)), "Please 'pkg install nist-kat'" 46 return iglob(os.path.join(katdir, base, glob)) 47 48aesmodules = [ 'cryptosoft0', 'aesni0', 'ccr0', 'ccp0' ] 49desmodules = [ 'cryptosoft0', ] 50shamodules = [ 'cryptosoft0', 'aesni0', 'ccr0', 'ccp0' ] 51 52def GenTestCase(cname): 53 try: 54 crid = cryptodev.Crypto.findcrid(cname) 55 except IOError: 56 return None 57 58 class GendCryptoTestCase(unittest.TestCase): 59 ############### 60 ##### AES ##### 61 ############### 62 @unittest.skipIf(cname not in aesmodules, 'skipping AES on %s' % (cname)) 63 def test_xts(self): 64 for i in katg('XTSTestVectors/format tweak value input - data unit seq no', '*.rsp'): 65 self.runXTS(i, cryptodev.CRYPTO_AES_XTS) 66 67 @unittest.skipIf(cname not in aesmodules, 'skipping AES on %s' % (cname)) 68 def test_cbc(self): 69 for i in katg('KAT_AES', 'CBC[GKV]*.rsp'): 70 self.runCBC(i) 71 72 @unittest.skipIf(cname not in aesmodules, 'skipping AES on %s' % (cname)) 73 def test_gcm(self): 74 for i in katg('gcmtestvectors', 'gcmEncrypt*'): 75 self.runGCM(i, 'ENCRYPT') 76 77 for i in katg('gcmtestvectors', 'gcmDecrypt*'): 78 self.runGCM(i, 'DECRYPT') 79 80 _gmacsizes = { 32: cryptodev.CRYPTO_AES_256_NIST_GMAC, 81 24: cryptodev.CRYPTO_AES_192_NIST_GMAC, 82 16: cryptodev.CRYPTO_AES_128_NIST_GMAC, 83 } 84 def runGCM(self, fname, mode): 85 curfun = None 86 if mode == 'ENCRYPT': 87 swapptct = False 88 curfun = Crypto.encrypt 89 elif mode == 'DECRYPT': 90 swapptct = True 91 curfun = Crypto.decrypt 92 else: 93 raise RuntimeError('unknown mode: %r' % repr(mode)) 94 95 for bogusmode, lines in cryptodev.KATParser(fname, 96 [ 'Count', 'Key', 'IV', 'CT', 'AAD', 'Tag', 'PT', ]): 97 for data in lines: 98 curcnt = int(data['Count']) 99 cipherkey = data['Key'].decode('hex') 100 iv = data['IV'].decode('hex') 101 aad = data['AAD'].decode('hex') 102 tag = data['Tag'].decode('hex') 103 if 'FAIL' not in data: 104 pt = data['PT'].decode('hex') 105 ct = data['CT'].decode('hex') 106 107 if len(iv) != 12: 108 # XXX - isn't supported 109 continue 110 111 try: 112 c = Crypto(cryptodev.CRYPTO_AES_NIST_GCM_16, 113 cipherkey, 114 mac=self._gmacsizes[len(cipherkey)], 115 mackey=cipherkey, crid=crid) 116 except EnvironmentError, e: 117 # Can't test algorithms the driver does not support. 118 if e.errno != errno.EOPNOTSUPP: 119 raise 120 continue 121 122 if mode == 'ENCRYPT': 123 try: 124 rct, rtag = c.encrypt(pt, iv, aad) 125 except EnvironmentError, e: 126 # Can't test inputs the driver does not support. 127 if e.errno != errno.EINVAL: 128 raise 129 continue 130 rtag = rtag[:len(tag)] 131 data['rct'] = rct.encode('hex') 132 data['rtag'] = rtag.encode('hex') 133 self.assertEqual(rct, ct, repr(data)) 134 self.assertEqual(rtag, tag, repr(data)) 135 else: 136 if len(tag) != 16: 137 continue 138 args = (ct, iv, aad, tag) 139 if 'FAIL' in data: 140 self.assertRaises(IOError, 141 c.decrypt, *args) 142 else: 143 try: 144 rpt, rtag = c.decrypt(*args) 145 except EnvironmentError, e: 146 # Can't test inputs the driver does not support. 147 if e.errno != errno.EINVAL: 148 raise 149 continue 150 data['rpt'] = rpt.encode('hex') 151 data['rtag'] = rtag.encode('hex') 152 self.assertEqual(rpt, pt, 153 repr(data)) 154 155 def runCBC(self, fname): 156 curfun = None 157 for mode, lines in cryptodev.KATParser(fname, 158 [ 'COUNT', 'KEY', 'IV', 'PLAINTEXT', 'CIPHERTEXT', ]): 159 if mode == 'ENCRYPT': 160 swapptct = False 161 curfun = Crypto.encrypt 162 elif mode == 'DECRYPT': 163 swapptct = True 164 curfun = Crypto.decrypt 165 else: 166 raise RuntimeError('unknown mode: %r' % repr(mode)) 167 168 for data in lines: 169 curcnt = int(data['COUNT']) 170 cipherkey = data['KEY'].decode('hex') 171 iv = data['IV'].decode('hex') 172 pt = data['PLAINTEXT'].decode('hex') 173 ct = data['CIPHERTEXT'].decode('hex') 174 175 if swapptct: 176 pt, ct = ct, pt 177 # run the fun 178 c = Crypto(cryptodev.CRYPTO_AES_CBC, cipherkey, crid=crid) 179 r = curfun(c, pt, iv) 180 self.assertEqual(r, ct) 181 182 def runXTS(self, fname, meth): 183 curfun = None 184 for mode, lines in cryptodev.KATParser(fname, 185 [ 'COUNT', 'DataUnitLen', 'Key', 'DataUnitSeqNumber', 'PT', 186 'CT' ]): 187 if mode == 'ENCRYPT': 188 swapptct = False 189 curfun = Crypto.encrypt 190 elif mode == 'DECRYPT': 191 swapptct = True 192 curfun = Crypto.decrypt 193 else: 194 raise RuntimeError('unknown mode: %r' % repr(mode)) 195 196 for data in lines: 197 curcnt = int(data['COUNT']) 198 nbits = int(data['DataUnitLen']) 199 cipherkey = data['Key'].decode('hex') 200 iv = struct.pack('QQ', int(data['DataUnitSeqNumber']), 0) 201 pt = data['PT'].decode('hex') 202 ct = data['CT'].decode('hex') 203 204 if nbits % 128 != 0: 205 # XXX - mark as skipped 206 continue 207 if swapptct: 208 pt, ct = ct, pt 209 # run the fun 210 try: 211 c = Crypto(meth, cipherkey, crid=crid) 212 r = curfun(c, pt, iv) 213 except EnvironmentError, e: 214 # Can't test hashes the driver does not support. 215 if e.errno != errno.EOPNOTSUPP: 216 raise 217 continue 218 self.assertEqual(r, ct) 219 220 ############### 221 ##### DES ##### 222 ############### 223 @unittest.skipIf(cname not in desmodules, 'skipping DES on %s' % (cname)) 224 def test_tdes(self): 225 for i in katg('KAT_TDES', 'TCBC[a-z]*.rsp'): 226 self.runTDES(i) 227 228 def runTDES(self, fname): 229 curfun = None 230 for mode, lines in cryptodev.KATParser(fname, 231 [ 'COUNT', 'KEYs', 'IV', 'PLAINTEXT', 'CIPHERTEXT', ]): 232 if mode == 'ENCRYPT': 233 swapptct = False 234 curfun = Crypto.encrypt 235 elif mode == 'DECRYPT': 236 swapptct = True 237 curfun = Crypto.decrypt 238 else: 239 raise RuntimeError('unknown mode: %r' % repr(mode)) 240 241 for data in lines: 242 curcnt = int(data['COUNT']) 243 key = data['KEYs'] * 3 244 cipherkey = key.decode('hex') 245 iv = data['IV'].decode('hex') 246 pt = data['PLAINTEXT'].decode('hex') 247 ct = data['CIPHERTEXT'].decode('hex') 248 249 if swapptct: 250 pt, ct = ct, pt 251 # run the fun 252 c = Crypto(cryptodev.CRYPTO_3DES_CBC, cipherkey, crid=crid) 253 r = curfun(c, pt, iv) 254 self.assertEqual(r, ct) 255 256 ############### 257 ##### SHA ##### 258 ############### 259 @unittest.skipIf(cname not in shamodules, 'skipping SHA on %s' % str(cname)) 260 def test_sha(self): 261 # SHA not available in software 262 pass 263 #for i in iglob('SHA1*'): 264 # self.runSHA(i) 265 266 @unittest.skipIf(cname not in shamodules, 'skipping SHA on %s' % str(cname)) 267 def test_sha1hmac(self): 268 for i in katg('hmactestvectors', 'HMAC.rsp'): 269 self.runSHA1HMAC(i) 270 271 def runSHA1HMAC(self, fname): 272 for hashlength, lines in cryptodev.KATParser(fname, 273 [ 'Count', 'Klen', 'Tlen', 'Key', 'Msg', 'Mac' ]): 274 # E.g., hashlength will be "L=20" (bytes) 275 hashlen = int(hashlength.split("=")[1]) 276 277 blocksize = None 278 if hashlen == 20: 279 alg = cryptodev.CRYPTO_SHA1_HMAC 280 blocksize = 64 281 elif hashlen == 28: 282 # Cryptodev doesn't support SHA-224 283 # Slurp remaining input in section 284 for data in lines: 285 continue 286 continue 287 elif hashlen == 32: 288 alg = cryptodev.CRYPTO_SHA2_256_HMAC 289 blocksize = 64 290 elif hashlen == 48: 291 alg = cryptodev.CRYPTO_SHA2_384_HMAC 292 blocksize = 128 293 elif hashlen == 64: 294 alg = cryptodev.CRYPTO_SHA2_512_HMAC 295 blocksize = 128 296 else: 297 # Skip unsupported hashes 298 # Slurp remaining input in section 299 for data in lines: 300 continue 301 continue 302 303 for data in lines: 304 key = data['Key'].decode('hex') 305 msg = data['Msg'].decode('hex') 306 mac = data['Mac'].decode('hex') 307 tlen = int(data['Tlen']) 308 309 if len(key) > blocksize: 310 continue 311 312 try: 313 c = Crypto(mac=alg, mackey=key, 314 crid=crid) 315 except EnvironmentError, e: 316 # Can't test hashes the driver does not support. 317 if e.errno != errno.EOPNOTSUPP: 318 raise 319 continue 320 321 _, r = c.encrypt(msg, iv="") 322 323 # A limitation in cryptodev.py means we 324 # can only store MACs up to 16 bytes. 325 # That's good enough to validate the 326 # correct behavior, more or less. 327 maclen = min(tlen, 16) 328 self.assertEqual(r[:maclen], mac[:maclen], "Actual: " + \ 329 repr(r[:maclen].encode("hex")) + " Expected: " + repr(data)) 330 331 return GendCryptoTestCase 332 333cryptosoft = GenTestCase('cryptosoft0') 334aesni = GenTestCase('aesni0') 335ccr = GenTestCase('ccr0') 336ccp = GenTestCase('ccp0') 337 338if __name__ == '__main__': 339 unittest.main() 340