aboutsummaryrefslogtreecommitdiff
path: root/unit_tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'unit_tests.py')
-rw-r--r--unit_tests.py235
1 files changed, 169 insertions, 66 deletions
diff --git a/unit_tests.py b/unit_tests.py
index 4b02a35..efda5e9 100644
--- a/unit_tests.py
+++ b/unit_tests.py
@@ -11,10 +11,17 @@ import sys
from py11 import *
from py11.mutex import MutexDB
+try:
+ from Crypto.Util.number import inverse
+ from Crypto.PublicKey import RSA
+ pycrypto_loaded = True
+except ImportError:
+ pycrypto_loaded = False
+
def log(msg):
if not args.quiet:
- sys.stderr.write(msg)
+ sys.stderr.write("{}\n".format(msg))
def main():
from sys import argv
@@ -72,11 +79,11 @@ def setUpModule():
cmd = [args.server]
if geteuid() != 0:
cmd.insert(0, "sudo")
- log("Starting RPC server: {}\n".format(" ".join(cmd)))
+ log("Starting RPC server: {}".format(" ".join(cmd)))
rpc = Popen(cmd, env = dict(environ, CRYPTECH_KEYSTORE = server_keystore))
# Order of PINs here is significant, see p11util for details.
- log("Setting PINs (SLOW!)\n")
+ log("Setting PINs (SLOW!)")
if args.wheel_pin is None:
flags = "-sup"
pins = (args.initial_pin, args.so_pin, args.user_pin)
@@ -85,10 +92,10 @@ def setUpModule():
pins = (args.initial_pin, args.wheel_pin, args.so_pin, args.user_pin)
Popen((args.p11util, flags), stdin = PIPE).communicate("".join(pin + "\n" for pin in pins))
- log("Loading PKCS #11 library {}\n".format(args.libpkcs11))
+ log("Loading PKCS #11 library {}".format(args.libpkcs11))
p11 = PKCS11(args.libpkcs11)
- log("Setup complete\n")
+ log("Setup complete")
def tearDownModule():
@@ -122,13 +129,22 @@ class TimedTestCase(unittest.TestCase):
def setUp(self):
super(TimedTestCase, self).setUp()
+ self.addCleanup(self._print_timing)
self.startTime = datetime.datetime.now()
def tearDown(self):
self.endTime = datetime.datetime.now()
- log("runtime {} seconds ... ".format(self.endTime - self.startTime))
super(TimedTestCase, self).tearDown()
+ def _print_timing(self):
+ try:
+ elapsed = self.endTime - self.startTime
+ except AttributeError:
+ pass
+ else:
+ log("Runtime {}".format(elapsed))
+
+
class TestInit(TimedTestCase):
"""
Test all the flavors of C_Initialize().
@@ -444,13 +460,12 @@ class TestKeys(TimedTestCase):
p11.C_VerifyInit(self.session, CKM_ECDSA_SHA256, o)
p11.C_Verify(self.session, hamster, sig)
- def extract_rsa_public_key(self, handle):
- from Crypto.PublicKey import RSA
+ def _extract_rsa_public_key(self, handle):
a = p11.C_GetAttributeValue(self.session, handle, CKA_MODULUS, CKA_PUBLIC_EXPONENT)
return RSA.construct((a[CKA_MODULUS], a[CKA_PUBLIC_EXPONENT]))
def assertRawRSASignatureMatches(self, handle, plain, sig):
- pubkey = self.extract_rsa_public_key(handle)
+ pubkey = self._extract_rsa_public_key(handle)
result = pubkey.encrypt(sig, 0)[0]
prefix = "\x00\x01" if False else "\x01" # XXX
expect = prefix + "\xff" * (len(result) - len(plain) - len(prefix) - 1) + "\x00" + plain
@@ -472,6 +487,8 @@ class TestKeys(TimedTestCase):
def test_gen_sign_verify_tralala_rsa_3416(self):
"Generate/sign/verify with RSA-3416 (no hashing, message to be signed not a hash at all)"
+ if not args.all_tests:
+ self.skipTest("Key length not a multiple of 32, so expected to fail (very slowly)")
tralala = "tralala-en-hopsasa"
public_key, private_key = p11.C_GenerateKeyPair(
self.session, CKM_RSA_PKCS_KEY_PAIR_GEN, CKA_MODULUS_BITS = 3416,
@@ -484,65 +501,14 @@ class TestKeys(TimedTestCase):
p11.C_VerifyInit(self.session, CKM_RSA_PKCS, public_key)
p11.C_Verify(self.session, tralala, sig)
- def test_load_sign_verify_rsa_3416(self):
- "Load/sign/verify with RSA-3416 and externally-supplied key (no hashing, message to be signed not a hash at all)"
- from Crypto.Util.number import long_to_bytes, inverse
- from Crypto.PublicKey import RSA
- from textwrap import dedent
- k = RSA.importKey(dedent('''\
- -----BEGIN PRIVATE KEY-----
- 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
- BvUE7g/dCNw3ISPEAgVJZUHJ
- -----END PRIVATE KEY-----
- '''))
- public_key = p11.C_CreateObject(
- self.session, CKA_TOKEN = True, CKA_CLASS = CKO_PUBLIC_KEY, CKA_KEY_TYPE = CKK_RSA, CKA_ID = "RSA-3416",
- CKA_VERIFY = True,
+ def _load_rsa_keypair(self, pem, cka_id = None):
+ k = RSA.importKey(pem)
+ public = dict(
+ CKA_TOKEN = True, CKA_CLASS = CKO_PUBLIC_KEY, CKA_KEY_TYPE = CKK_RSA, CKA_VERIFY = True,
CKA_MODULUS = k.n,
CKA_PUBLIC_EXPONENT = k.e)
- private_key = p11.C_CreateObject(
- self.session, CKA_TOKEN = True, CKA_CLASS = CKO_PRIVATE_KEY, CKA_KEY_TYPE = CKK_RSA, CKA_ID = "RSA-3416",
- CKA_SIGN = True,
+ private = dict(
+ CKA_TOKEN = True, CKA_CLASS = CKO_PRIVATE_KEY, CKA_KEY_TYPE = CKK_RSA, CKA_SIGN = True,
CKA_MODULUS = k.n,
CKA_PUBLIC_EXPONENT = k.e,
CKA_PRIVATE_EXPONENT= k.d,
@@ -551,7 +517,39 @@ class TestKeys(TimedTestCase):
CKA_COEFFICIENT = inverse(k.q, k.p),
CKA_EXPONENT_1 = k.d % (k.p - 1),
CKA_EXPONENT_2 = k.d % (k.q - 1))
+ if cka_id is not None:
+ public[CKA_ID] = private[CKA_ID] = cka_id
+ public_key = p11.C_CreateObject(self.session, public)
+ private_key = p11.C_CreateObject(self.session, private)
self.assertIsKeypair(public_key, private_key)
+ return public_key, private_key
+
+ @unittest.skipUnless(pycrypto_loaded, "requires PyCrypto")
+ def test_load_sign_verify_rsa_1024(self):
+ "Load/sign/verify with RSA-1024-SHA-512 and externally-supplied key"
+ public_key, private_key = self._load_rsa_keypair(rsa_1024_pem, "RSA-1024")
+ hamster = "Your mother was a hamster"
+ p11.C_SignInit(self.session, CKM_SHA512_RSA_PKCS, private_key)
+ sig = p11.C_Sign(self.session, hamster)
+ self.assertIsInstance(sig, str)
+ p11.C_VerifyInit(self.session, CKM_SHA512_RSA_PKCS, public_key)
+ p11.C_Verify(self.session, hamster, sig)
+
+ @unittest.skipUnless(pycrypto_loaded, "requires PyCrypto")
+ def test_load_sign_verify_rsa_2048(self):
+ "Load/sign/verify with RSA-2048-SHA-512 and externally-supplied key"
+ public_key, private_key = self._load_rsa_keypair(rsa_2048_pem, "RSA-2048")
+ hamster = "Your mother was a hamster"
+ p11.C_SignInit(self.session, CKM_SHA512_RSA_PKCS, private_key)
+ sig = p11.C_Sign(self.session, hamster)
+ self.assertIsInstance(sig, str)
+ p11.C_VerifyInit(self.session, CKM_SHA512_RSA_PKCS, public_key)
+ p11.C_Verify(self.session, hamster, sig)
+
+ @unittest.skipUnless(pycrypto_loaded, "requires PyCrypto")
+ def test_load_sign_verify_rsa_3416(self):
+ "Load/sign/verify with RSA-3416-SHA-512 and externally-supplied key"
+ public_key, private_key = self._load_rsa_keypair(rsa_3416_pem, "RSA-3416")
hamster = "Your mother was a hamster"
p11.C_SignInit(self.session, CKM_SHA512_RSA_PKCS, private_key)
sig = p11.C_Sign(self.session, hamster)
@@ -561,6 +559,8 @@ class TestKeys(TimedTestCase):
def test_gen_sign_verify_rsa_3416(self):
"Generate/sign/verify with RSA-3416-SHA-512"
+ if not args.all_tests:
+ self.skipTest("Key length not a multiple of 32, so expected to fail (very slowly)")
public_key, private_key = p11.C_GenerateKeyPair(
self.session, CKM_RSA_PKCS_KEY_PAIR_GEN, CKA_MODULUS_BITS = 3416,
CKA_ID = "RSA-3416", CKA_SIGN = True, CKA_VERIFY = True, CKA_TOKEN = True)
@@ -592,5 +592,108 @@ class TestKeys(TimedTestCase):
p11.C_VerifyInit(self.session, CKM_SHA512_RSA_PKCS, public_key)
p11.C_Verify(self.session, hamster, sig)
+
+# Keys for preload tests, here rather than inline because they're
+# bulky. These are in PKCS #8 format, see PyCrypto or the "pkey" and
+# "genpkey" commands to OpenSSL's command line tool.
+
+rsa_1024_pem = '''\
+-----BEGIN PRIVATE KEY-----
+MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIwSaEpCTVJvbd4Z
+B1P8H9EgFlZqats7PeBIOlC2Q1zla7wBmNJkX5Jkez8tF3l22Sn99c6c6PuhyhzB
+dZtifQbZniKCJEzyby5MXZeSr20rPdrqiB9FX13mmtLN7ii4nLyAYFAQ4R8ZvdH2
+dRIWtxwhS7d4AyrWYhJkemIvSApfAgMBAAECgYAmL1Zy+AQwNuRSqawPvynFTuQI
+Bta+kTXbEJWlLyrKBlkKVb0djfNn6zCWFmrR2A53nh4Gh0wUXRTGJg8znvPKJPcp
+45znc7aGQFmDivvl5m/UkbqET6SB6JyCOCKzYa1Rtn3YFMkf/3MgzrWIhFv+UNH/
+I5lSjzJcCrN4mgI+AQJBALcTNa0mOWRXX+6jssbf65Cx6wmHsrrptXiP9gKfwdkx
+697EzyvPDL8xwL20O+xBFehj866O/f8nOPP47imOPoECQQDD3gU8wD8MeWLqYcXI
+AdERIuuk1VnL36QOzn9NoPF01DLJcrcbN24i5/9tcza3Kdec8fexJTh/PMBvR8Zr
+w5jfAkAnFgrXtNl7+suYf4qjuxroAZRUrIwUK+F6pAG5/bG9VVMudIZmrAXkrBKi
+beB9SEgNHYnhMtY3q4AVVohChwQBAkAR1I5Jf3691fcJOylUEcZEdxdYhAuOoac/
+qdCw8mvIpOCSshy1H5CpINGB1zEt72MvaF+SAr9n5dHmz3Pir4WlAkB/ZccJ5QBH
+uBP0/flXdmhG5lC3MTMiiE7Rls/3L2t6S4xVDnQ81RYf7Car53WN7qSVSZnhDGsn
+BJpghq2nYUH1
+-----END PRIVATE KEY-----
+'''
+
+rsa_2048_pem = '''\
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCzvgb90hKxeDJy
+zeWz/F4JGZ3Acl1i3url3VPXHyoldyhuNC+8jf4iM7TGBYGLH+sYkBXWu9GD0erl
+KBMJMTBO8OdXulSAJh8r1Z8qNPSVNguvNgGQlRDGc7tZJ6gWFlzM2g5flED24bN9
+6Ir9O1cZi7xMc0Nzkn9Rms5IwPW8OB4IZZlbFC6Ih9vUSp06Tm3rQ/eQJkhLFbzM
+ejc9OH1LSpYtji44ohmy/jPJsmSlzwK5JSchZqbxl/msVw1t/nZS3loqKUMvzn9F
+iARLiaIrUKNCmSmL8HqEt2qKt0ESHG0vX07h5W5iHIJOuKhqcX3li8nFcwsOV3AB
+RsCRgeppAgMBAAECggEANEeTVQRjN4dUdRv6Me23lEIFJlKdYwKfpBhKKIoCAj+0
+XMmFEPzj7CLJ88bqNQMlqFFQaNLcT9Eg12Jelw/dkzhysYuaxGNSMbfCwc4BTd0Y
+bO/yaJFS/cXvujDUrQf4GgVapOZENwrS4E5hDuLRpLaGIF5uQhFcQuoaEgM99m6H
+TzOIhtu3DjdbfSsmkGVQ7xUVFcvaCrMVoq06dvUH4HpYTKeeqgcVv++XjIe83Nzv
++oN5U2oFOzrYpGGHN6jrekmmbEaxy8UHOySC6Y+UyRrPEy9q1ZgkkC9VCrM7E28/
+4PETw8MI7uNoosuFXofctKjtRvC5Sn9zW3dyv9NkAQKBgQDSkdDy4+xN2kA7gdry
+4eLKUzKRNSbnoPrCu2TENUR8oTjbJ52LQEbGa9HIKaM33701hJsc34C6adJgoY9c
+PoBcJgxOI7N40A/rI3c8krKS5X/ViBY3TzJsP3LaBKDdfioaTDVTjhYD6v2eu7Lt
+4eIqa8sVA4PhLSVGRW53ZSYjwQKBgQDahY6cR8WgPseQJTkCbKyfKCSuz6nioZpC
+stkpKhJepzE6AKZTLLWvNIPNT/khl40aby5seLNkY3Tb9cYBX2iShGv4cguPMiAl
+hb7Uljz19i6YaX74gkTjsYpv44ddLSZp+/FTOl0C0I8WocQpb8B2d4BgvfxgHrHb
+KCHnyihQqQKBgQC7PKPixt7hnzdMcrxhCpDiPbaSPgQZJSC1NXJ1sbPzalynKwPA
+xefpGgiRBs02qsGRLBfNRcQuflhuSlqyuHTk+4Qnm0FEJSZyfLfS6dLWIjJYikjO
+56I7dPPIfyMXsM75UVh9srNKypK4qciCFEBKXk1XoyeKe91QLf77NbsDQQKBgDnY
+CLwNs56Lf8AEWmbt5XPr6GntxoabSH5HYXyoClzL3RgBfAWgXCeYuxrqBISD3XIV
+5DAKc1IrkY94K4XJf6DpNLt7VNv+5MuJ783ORyzEkej+ZAHcWef74y1jCT386aI8
+ctEZLe3Ez1uqToa5cjTpxS3WnKvE9EeTBAabWLihAoGBAKft+PG+j+QhZeHSGxRz
+mY6AQHH4z7IU94fmXiT2kTsG8/svhPNfsmVt7UCSJbCaYsrcuR2NonVV8wn/U793
+LcnLAV+WObeduMTBCasJw8IniFwFhhfkxOtmlevExE1I3ENwkRlJ7NdROky4pnd5
+LGmN2EOOlFijEGxBfw+Wb1rQ
+-----END PRIVATE KEY-----
+'''
+
+rsa_3416_pem = '''\
+-----BEGIN PRIVATE KEY-----
+MIIHvgIBADANBgkqhkiG9w0BAQEFAASCB6gwggekAgEAAoIBrADgnFnBx2/mTFGM
+pEQ891GLfN8lnCJsMTN3zkQKCAV1iQpXXoOzq+mFDudpYsBZRq15AZxPc6ZejD5Q
+P8PTIPdNWquC7u5mUsxLc12iVvXn3OBvxQyf/U+8S3Y2OsuVr9oTAU/PS4lO/bct
+GgTmGnuRgWSgKl+tqsmABqEDOvEGIK7MHiwL7XbFgxPTV9nhP6Qaox0/eBD1Cq0K
+pQ2DmwVCMglQl2s2kmmqT9HV/iZD/WuvxpnRYpGLtUQLgVbCO50spH/PSrsnaiIk
+DMzPreaRjNVhKz2cVAJysCdGygY0vUtZILlA9gngL/arQYV2eSwTyvpzZwiJOcVV
+d2A8Beebo0bWG2pnBnWNBlp20s+UQRheYJZapIgd5tmHLb9sJLeC6QRJzgCLweO3
+jaGzwN96q5/Wgjldn5a/eW1w0anwx34BVOkOJxvcvvhgleI7vlpZ93tuWsJ8xqjU
+0mRB3NhRhlVxS1UJbgF1LbciLvcLJ7QpCM5ExS1tpZFBeSY+sov+UQo05T66ZapY
+Wwbh7AqDI0F0J+j5pPUG0+Whkju4oxB0FUd69ggMmCaHAgMBAAECggGrYJYbat7u
+WaQ79TS2O1lG8aqy8qNfkhLeRQin7YBhiJdzoPp9vAeTFarBDGpwuHNSKZTtuKTM
+yB+atDuXY/TrI5J36ogAcHPucgucGjE28Yvj32xm722omhoBLXS/ExFZv45y2Xts
+AlHMMVLdBG4i8QEpWk6ecjndCHbRSmhQOQhY4mGfI0nsJyckoV9HzDrnwKSf8Ska
+caUzoD41v4AsFLkblFJowkDXu2szmsf9gIM7iYznnEi8uc0rA5+MxV2JSyc55tQG
+Av76y3HNqQjo+3IKWAyWFvujkBQRePw5HVMxmw9i5KIo41LGbZsMkwNkVq6pu6Gf
+rQGKaLD9L0o4h1zI8pOaNs8pX5SdtzhjYr3AsXdarL//dES6tVfFqVhN824debEC
+nIMEAaUmLFaergh37tl9BqKJncvWn/nqkKt2AmW3K07uXhbNBGp5VJiBJL1ade1k
+16t8c9HA1KqzEBXpWdiAoX1ezRkdlLNswOi0rjBFhLBlX7/8Nzlp7c4Mz7ioEXGc
+2yTEs2CgwYa+Tc0qKV1Q+zOJ97MNyi6NLsoG9FSpeNvmSwEqYQKB1g+Hdma2H+ud
+d5yLzONKanWzYddjnjxifgepBaHi0tKDysjGk9N/0LAX9sEOQsIqwVjE/OwpQFAy
+ScI1dvx1nTCw2S0XCtUIgSxmFq6ZO2BaduL6J6KxMN1JYkME7U5uAeAlH9gcKzqK
+sUPCPQE+q6rjVinK7oWp4ZeGEl+dGZyqGy1aScBN5Ie1URi9LYRZHCuKjyUYSmwF
+95QPc64HqYwr8k6zSfW7Hj5Ier8AWNlhO1/o0Q8OAMA42kQIAYpC23YZXQqqYifH
+80kEj4jl0Tvo+35jTjkCgdYOdryFRF/ukhz1ykZ3It3YfrkP56KuQ0a/GJvJ+XJU
+wMPunvuQ3rVjIWAB1JTl2ASUl8QJEsHuLeXO1mwtNpFHy5FAmi+VSnpIaTE6YHRX
+5/P/renuOeozwBPky95gULdRAgwTOdP0UucQ7I7U7FAXUronQHWrQTFbemnnXQYP
+m0yy2gxZ4TPRquaYP26sKWk6ollrPrtObppgpPtmKFVrdah9GMtPG0/ArrecP1g5
+6JF0KXX2bR/7lr1QK7ETMcopaTW9zTVhTA9go1aoVP99J1O/Bxq/AoHWBM495NEt
+laN4VXip4hiwU1Y8zAPm/vbX25UByjRAW6cvROy26HegZC42TU4VeLL0fH0RbB/j
+6C13x+L1vHDFQUEpJBwCXSSxnMTG9iczScEVE26of19oKMLB5s2Khn/ikrPKY/1r
+n0U2UCq26EC1rT+G9Y34PGLzDgoOe4pJV8MIgAN12U4Bj8GbpBU/FbrhzdOmMquO
+tFkwYaBagxuZ62faJ2KyW5oZZNrXKW55EGRXlHme4JLLxrCRUwZLO7cu5SA6O8e4
+cmkdL5Z6uLmuA2U5FsayeQKB1glMG8ySchP5yjHYr0j/mZkDhFQL8o+P4VcPK30+
+IlcGfivR+CVccz5ggsVKb9f67p7Rm4q1iwFecX1uaaT6kZKT8S+UrQeLE2WecK10
+uPSUvkxY76lZgwl265LD1ZMV73BcH4TwRCWmcK95UCrgKG+FlvGKRtkpk9+YpaC6
+NB4uFrRU42GXGGcrMwUkqTBzghfVqiL89QvqnsOG6a72OEpWHFMlb/LOvIpABPij
+40N+EpmX2SLpbIidkd2J6E5NUAUkgw4Zbbm4WZ4mAJs93+kEMZn2qCMCgdYLpnPB
+uZ6pDpmXxKmfGRqzZVCwYJk9myya2ZcAmjfEHGpLHcy99mDSgb9BaUnceGizgSnG
+KQCxAnX7xFXshz52DIfGZqKANyuuCRXv34aB0PxHozmlmAjuU5Y8I8PcioOqNLh3
+ZEcXNEVhhaZKGi9yz7QXZsauxjYGjgsGvaV+yPrzWcnIWsKW0X0aC3eIDOaw8yCC
+F9qQXfT559lNaH3+aBCVlDL17HtOkax8J04vI1gEbqIyd9vn34+iFBcC5TBq9qZT
+BvUE7g/dCNw3ISPEAgVJZUHJ
+-----END PRIVATE KEY-----
+'''
+
+
if __name__ == "__main__":
main()