xref: /freebsd/tests/sys/opencrypto/cryptotest.py (revision a812392203d7c4c3f0db9d8a0f3391374c49c71f)
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
32import cryptodev
33import itertools
34import os
35import struct
36import unittest
37from cryptodev import *
38from glob import iglob
39
40katdir = '/usr/local/share/nist-kat'
41
42def katg(base, glob):
43	return iglob(os.path.join(katdir, base, glob))
44
45aesmodules = [ 'cryptosoft0', 'aesni0', ]
46desmodules = [ 'cryptosoft0', ]
47shamodules = [ 'cryptosoft0', ]
48
49def GenTestCase(cname):
50	try:
51		crid = cryptodev.Crypto.findcrid(cname)
52	except IOError:
53		return None
54
55	class GendCryptoTestCase(unittest.TestCase):
56		###############
57		##### AES #####
58		###############
59		@unittest.skipIf(cname not in aesmodules, 'skipping AES on %s' % `cname`)
60		def test_xts(self):
61			for i in katg('XTSTestVectors/format tweak value input - data unit seq no', '*.rsp'):
62				self.runXTS(i, cryptodev.CRYPTO_AES_XTS)
63
64		def test_cbc(self):
65			for i in katg('KAT_AES', 'CBC[GKV]*.rsp'):
66				self.runCBC(i)
67
68		def test_gcm(self):
69			for i in katg('gcmtestvectors', 'gcmEncrypt*'):
70				self.runGCM(i, 'ENCRYPT')
71
72			for i in katg('gcmtestvectors', 'gcmDecrypt*'):
73				self.runGCM(i, 'DECRYPT')
74
75		_gmacsizes = { 32: cryptodev.CRYPTO_AES_256_NIST_GMAC,
76			24: cryptodev.CRYPTO_AES_192_NIST_GMAC,
77			16: cryptodev.CRYPTO_AES_128_NIST_GMAC,
78		}
79		def runGCM(self, fname, mode):
80			curfun = None
81			if mode == 'ENCRYPT':
82				swapptct = False
83				curfun = Crypto.encrypt
84			elif mode == 'DECRYPT':
85				swapptct = True
86				curfun = Crypto.decrypt
87			else:
88				raise RuntimeError('unknown mode: %s' % `mode`)
89
90			for bogusmode, lines in cryptodev.KATParser(fname,
91			    [ 'Count', 'Key', 'IV', 'CT', 'AAD', 'Tag', 'PT', ]):
92				for data in lines:
93					curcnt = int(data['Count'])
94					cipherkey = data['Key'].decode('hex')
95					iv = data['IV'].decode('hex')
96					aad = data['AAD'].decode('hex')
97					tag = data['Tag'].decode('hex')
98					if 'FAIL' not in data:
99						pt = data['PT'].decode('hex')
100					ct = data['CT'].decode('hex')
101
102					if len(iv) != 12:
103						# XXX - isn't supported
104						continue
105
106					c = Crypto(cryptodev.CRYPTO_AES_NIST_GCM_16,
107					    cipherkey,
108					    mac=self._gmacsizes[len(cipherkey)],
109					    mackey=cipherkey, crid=crid)
110
111					if mode == 'ENCRYPT':
112						rct, rtag = c.encrypt(pt, iv, aad)
113						rtag = rtag[:len(tag)]
114						data['rct'] = rct.encode('hex')
115						data['rtag'] = rtag.encode('hex')
116						self.assertEqual(rct, ct, `data`)
117						self.assertEqual(rtag, tag, `data`)
118					else:
119						if len(tag) != 16:
120							continue
121						args = (ct, iv, aad, tag)
122						if 'FAIL' in data:
123							self.assertRaises(IOError,
124								c.decrypt, *args)
125						else:
126							rpt, rtag = c.decrypt(*args)
127							data['rpt'] = rpt.encode('hex')
128							data['rtag'] = rtag.encode('hex')
129							self.assertEqual(rpt, pt,
130							    `data`)
131
132		def runCBC(self, fname):
133			curfun = None
134			for mode, lines in cryptodev.KATParser(fname,
135			    [ 'COUNT', 'KEY', 'IV', 'PLAINTEXT', 'CIPHERTEXT', ]):
136				if mode == 'ENCRYPT':
137					swapptct = False
138					curfun = Crypto.encrypt
139				elif mode == 'DECRYPT':
140					swapptct = True
141					curfun = Crypto.decrypt
142				else:
143					raise RuntimeError('unknown mode: %s' % `mode`)
144
145				for data in lines:
146					curcnt = int(data['COUNT'])
147					cipherkey = data['KEY'].decode('hex')
148					iv = data['IV'].decode('hex')
149					pt = data['PLAINTEXT'].decode('hex')
150					ct = data['CIPHERTEXT'].decode('hex')
151
152					if swapptct:
153						pt, ct = ct, pt
154					# run the fun
155					c = Crypto(cryptodev.CRYPTO_AES_CBC, cipherkey, crid=crid)
156					r = curfun(c, pt, iv)
157					self.assertEqual(r, ct)
158
159		def runXTS(self, fname, meth):
160			curfun = None
161			for mode, lines in cryptodev.KATParser(fname,
162			    [ 'COUNT', 'DataUnitLen', 'Key', 'DataUnitSeqNumber', 'PT',
163			    'CT' ]):
164				if mode == 'ENCRYPT':
165					swapptct = False
166					curfun = Crypto.encrypt
167				elif mode == 'DECRYPT':
168					swapptct = True
169					curfun = Crypto.decrypt
170				else:
171					raise RuntimeError('unknown mode: %s' % `mode`)
172
173				for data in lines:
174					curcnt = int(data['COUNT'])
175					nbits = int(data['DataUnitLen'])
176					cipherkey = data['Key'].decode('hex')
177					iv = struct.pack('QQ', int(data['DataUnitSeqNumber']), 0)
178					pt = data['PT'].decode('hex')
179					ct = data['CT'].decode('hex')
180
181					if nbits % 128 != 0:
182						# XXX - mark as skipped
183						continue
184					if swapptct:
185						pt, ct = ct, pt
186					# run the fun
187					c = Crypto(meth, cipherkey, crid=crid)
188					r = curfun(c, pt, iv)
189					self.assertEqual(r, ct)
190
191		###############
192		##### DES #####
193		###############
194		@unittest.skipIf(cname not in desmodules, 'skipping DES on %s' % `cname`)
195		def test_tdes(self):
196			for i in katg('KAT_TDES', 'TCBC[a-z]*.rsp'):
197				self.runTDES(i)
198
199		def runTDES(self, fname):
200			curfun = None
201			for mode, lines in cryptodev.KATParser(fname,
202			    [ 'COUNT', 'KEYs', 'IV', 'PLAINTEXT', 'CIPHERTEXT', ]):
203				if mode == 'ENCRYPT':
204					swapptct = False
205					curfun = Crypto.encrypt
206				elif mode == 'DECRYPT':
207					swapptct = True
208					curfun = Crypto.decrypt
209				else:
210					raise RuntimeError('unknown mode: %s' % `mode`)
211
212				for data in lines:
213					curcnt = int(data['COUNT'])
214					key = data['KEYs'] * 3
215					cipherkey = key.decode('hex')
216					iv = data['IV'].decode('hex')
217					pt = data['PLAINTEXT'].decode('hex')
218					ct = data['CIPHERTEXT'].decode('hex')
219
220					if swapptct:
221						pt, ct = ct, pt
222					# run the fun
223					c = Crypto(cryptodev.CRYPTO_3DES_CBC, cipherkey, crid=crid)
224					r = curfun(c, pt, iv)
225					self.assertEqual(r, ct)
226
227		###############
228		##### SHA #####
229		###############
230		@unittest.skipIf(cname not in shamodules, 'skipping SHA on %s' % `cname`)
231		def test_sha(self):
232			# SHA not available in software
233			pass
234			#for i in iglob('SHA1*'):
235			#	self.runSHA(i)
236
237		def test_sha1hmac(self):
238			for i in katg('hmactestvectors', 'HMAC.rsp'):
239				self.runSHA1HMAC(i)
240
241		def runSHA1HMAC(self, fname):
242			for bogusmode, lines in cryptodev.KATParser(fname,
243			    [ 'Count', 'Klen', 'Tlen', 'Key', 'Msg', 'Mac' ]):
244				for data in lines:
245					key = data['Key'].decode('hex')
246					msg = data['Msg'].decode('hex')
247					mac = data['Mac'].decode('hex')
248
249					if len(key) != 20:
250						# XXX - implementation bug
251						continue
252
253					c = Crypto(mac=cryptodev.CRYPTO_SHA1_HMAC,
254					    mackey=key, crid=crid)
255
256					r = c.encrypt(msg)
257					self.assertEqual(r, mac, `data`)
258
259	return GendCryptoTestCase
260
261cryptosoft = GenTestCase('cryptosoft0')
262aesni = GenTestCase('aesni0')
263
264if __name__ == '__main__':
265	unittest.main()
266