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' ] 49desmodules = [ 'cryptosoft0', ] 50shamodules = [ 'cryptosoft0', 'aesni0', 'ccr0' ] 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 c = Crypto(cryptodev.CRYPTO_AES_NIST_GCM_16, 112 cipherkey, 113 mac=self._gmacsizes[len(cipherkey)], 114 mackey=cipherkey, crid=crid) 115 116 if mode == 'ENCRYPT': 117 rct, rtag = c.encrypt(pt, iv, aad) 118 rtag = rtag[:len(tag)] 119 data['rct'] = rct.encode('hex') 120 data['rtag'] = rtag.encode('hex') 121 self.assertEqual(rct, ct, repr(data)) 122 self.assertEqual(rtag, tag, repr(data)) 123 else: 124 if len(tag) != 16: 125 continue 126 args = (ct, iv, aad, tag) 127 if 'FAIL' in data: 128 self.assertRaises(IOError, 129 c.decrypt, *args) 130 else: 131 rpt, rtag = c.decrypt(*args) 132 data['rpt'] = rpt.encode('hex') 133 data['rtag'] = rtag.encode('hex') 134 self.assertEqual(rpt, pt, 135 repr(data)) 136 137 def runCBC(self, fname): 138 curfun = None 139 for mode, lines in cryptodev.KATParser(fname, 140 [ 'COUNT', 'KEY', 'IV', 'PLAINTEXT', 'CIPHERTEXT', ]): 141 if mode == 'ENCRYPT': 142 swapptct = False 143 curfun = Crypto.encrypt 144 elif mode == 'DECRYPT': 145 swapptct = True 146 curfun = Crypto.decrypt 147 else: 148 raise RuntimeError('unknown mode: %r' % repr(mode)) 149 150 for data in lines: 151 curcnt = int(data['COUNT']) 152 cipherkey = data['KEY'].decode('hex') 153 iv = data['IV'].decode('hex') 154 pt = data['PLAINTEXT'].decode('hex') 155 ct = data['CIPHERTEXT'].decode('hex') 156 157 if swapptct: 158 pt, ct = ct, pt 159 # run the fun 160 c = Crypto(cryptodev.CRYPTO_AES_CBC, cipherkey, crid=crid) 161 r = curfun(c, pt, iv) 162 self.assertEqual(r, ct) 163 164 def runXTS(self, fname, meth): 165 curfun = None 166 for mode, lines in cryptodev.KATParser(fname, 167 [ 'COUNT', 'DataUnitLen', 'Key', 'DataUnitSeqNumber', 'PT', 168 'CT' ]): 169 if mode == 'ENCRYPT': 170 swapptct = False 171 curfun = Crypto.encrypt 172 elif mode == 'DECRYPT': 173 swapptct = True 174 curfun = Crypto.decrypt 175 else: 176 raise RuntimeError('unknown mode: %r' % repr(mode)) 177 178 for data in lines: 179 curcnt = int(data['COUNT']) 180 nbits = int(data['DataUnitLen']) 181 cipherkey = data['Key'].decode('hex') 182 iv = struct.pack('QQ', int(data['DataUnitSeqNumber']), 0) 183 pt = data['PT'].decode('hex') 184 ct = data['CT'].decode('hex') 185 186 if nbits % 128 != 0: 187 # XXX - mark as skipped 188 continue 189 if swapptct: 190 pt, ct = ct, pt 191 # run the fun 192 c = Crypto(meth, cipherkey, crid=crid) 193 r = curfun(c, pt, iv) 194 self.assertEqual(r, ct) 195 196 ############### 197 ##### DES ##### 198 ############### 199 @unittest.skipIf(cname not in desmodules, 'skipping DES on %s' % (cname)) 200 def test_tdes(self): 201 for i in katg('KAT_TDES', 'TCBC[a-z]*.rsp'): 202 self.runTDES(i) 203 204 def runTDES(self, fname): 205 curfun = None 206 for mode, lines in cryptodev.KATParser(fname, 207 [ 'COUNT', 'KEYs', 'IV', 'PLAINTEXT', 'CIPHERTEXT', ]): 208 if mode == 'ENCRYPT': 209 swapptct = False 210 curfun = Crypto.encrypt 211 elif mode == 'DECRYPT': 212 swapptct = True 213 curfun = Crypto.decrypt 214 else: 215 raise RuntimeError('unknown mode: %r' % repr(mode)) 216 217 for data in lines: 218 curcnt = int(data['COUNT']) 219 key = data['KEYs'] * 3 220 cipherkey = key.decode('hex') 221 iv = data['IV'].decode('hex') 222 pt = data['PLAINTEXT'].decode('hex') 223 ct = data['CIPHERTEXT'].decode('hex') 224 225 if swapptct: 226 pt, ct = ct, pt 227 # run the fun 228 c = Crypto(cryptodev.CRYPTO_3DES_CBC, cipherkey, crid=crid) 229 r = curfun(c, pt, iv) 230 self.assertEqual(r, ct) 231 232 ############### 233 ##### SHA ##### 234 ############### 235 @unittest.skipIf(cname not in shamodules, 'skipping SHA on %s' % str(cname)) 236 def test_sha(self): 237 # SHA not available in software 238 pass 239 #for i in iglob('SHA1*'): 240 # self.runSHA(i) 241 242 @unittest.skipIf(cname not in shamodules, 'skipping SHA on %s' % str(cname)) 243 def test_sha1hmac(self): 244 for i in katg('hmactestvectors', 'HMAC.rsp'): 245 self.runSHA1HMAC(i) 246 247 def runSHA1HMAC(self, fname): 248 for hashlength, lines in cryptodev.KATParser(fname, 249 [ 'Count', 'Klen', 'Tlen', 'Key', 'Msg', 'Mac' ]): 250 # E.g., hashlength will be "L=20" (bytes) 251 hashlen = int(hashlength.split("=")[1]) 252 253 blocksize = None 254 if hashlen == 20: 255 alg = cryptodev.CRYPTO_SHA1_HMAC 256 blocksize = 64 257 elif hashlen == 28: 258 # Cryptodev doesn't support SHA-224 259 # Slurp remaining input in section 260 for data in lines: 261 continue 262 continue 263 elif hashlen == 32: 264 alg = cryptodev.CRYPTO_SHA2_256_HMAC 265 blocksize = 64 266 elif hashlen == 48: 267 alg = cryptodev.CRYPTO_SHA2_384_HMAC 268 blocksize = 128 269 elif hashlen == 64: 270 alg = cryptodev.CRYPTO_SHA2_512_HMAC 271 blocksize = 128 272 else: 273 # Skip unsupported hashes 274 # Slurp remaining input in section 275 for data in lines: 276 continue 277 continue 278 279 for data in lines: 280 key = data['Key'].decode('hex') 281 msg = data['Msg'].decode('hex') 282 mac = data['Mac'].decode('hex') 283 tlen = int(data['Tlen']) 284 285 if len(key) > blocksize: 286 continue 287 288 try: 289 c = Crypto(mac=alg, mackey=key, 290 crid=crid) 291 except EnvironmentError, e: 292 # Can't test hashes the driver does not support. 293 if e.errno != errno.EOPNOTSUPP: 294 raise 295 continue 296 297 _, r = c.encrypt(msg, iv="") 298 299 # A limitation in cryptodev.py means we 300 # can only store MACs up to 16 bytes. 301 # That's good enough to validate the 302 # correct behavior, more or less. 303 maclen = min(tlen, 16) 304 self.assertEqual(r[:maclen], mac[:maclen], "Actual: " + \ 305 repr(r[:maclen].encode("hex")) + " Expected: " + repr(data)) 306 307 return GendCryptoTestCase 308 309cryptosoft = GenTestCase('cryptosoft0') 310aesni = GenTestCase('aesni0') 311ccr = GenTestCase('ccr0') 312 313if __name__ == '__main__': 314 unittest.main() 315