#!/usr/bin/env python """ Time PKCS #11 signatures. """ import collections import threading import argparse import datetime import queue import sys from Crypto.Hash.SHA256 import SHA256Hash as SHA256 from Crypto.Hash.SHA384 import SHA384Hash as SHA384 from Crypto.Hash.SHA512 import SHA512Hash as SHA512 try: from cryptech.py11 import * except ImportError: # Kludge to let us run script from repo without installing cryptech.py11 from os.path import dirname, abspath sys.path.append(dirname(dirname(abspath(sys.argv[0])))) from cryptech.py11 import * from cryptech.py11 import default_so_name as libpkcs11_default class RSAKey(object): def __init__(self, hash, pem): from Crypto.PublicKey import RSA self.h = hash self.k = RSA.importKey(pem) self.ckm = CKM_RSA_PKCS def create(self, args, p11, session, text): from Crypto.Util.number import inverse from Crypto.Signature import PKCS1_v1_5 from Crypto.Signature.PKCS1_v1_5 import EMSA_PKCS1_V1_5_ENCODE, PKCS115_SigScheme self.handle = p11.C_CreateObject( session, CKA_SIGN = True, CKA_TOKEN = args.token, CKA_CLASS = CKO_PRIVATE_KEY, CKA_KEY_TYPE = CKK_RSA, CKA_MODULUS = self.k.n, CKA_PUBLIC_EXPONENT = self.k.e, CKA_PRIVATE_EXPONENT= self.k.d, CKA_PRIME_1 = self.k.p, CKA_PRIME_2 = self.k.q, CKA_COEFFICIENT = inverse(self.k.q, self.k.p), CKA_EXPONENT_1 = self.k.d % (self.k.p - 1), CKA_EXPONENT_2 = self.k.d % (self.k.q - 1)) h = self.h(text) tbs = EMSA_PKCS1_V1_5_ENCODE(h, (self.k.n.bit_length() + 7) / 8)[2:] self.tbs = tbs[tbs.index("\x00") + 1:] self.sig = PKCS115_SigScheme(self.k).sign(h) def verify(self, sig): assert sig == self.sig class ECDSAKey(object): def __init__(self, hash, pem): from ecdsa.keys import SigningKey self.h = hash self.sk = SigningKey.from_pem(pem) self.vk = self.sk.get_verifying_key() self.ckm = CKM_ECDSA def create(self, args, p11, session, text): self.handle = p11.C_CreateObject( session, CKA_SIGN = True, CKA_CLASS = CKO_PRIVATE_KEY, CKA_KEY_TYPE = CKK_EC, CKA_EC_PARAMS = self.sk.curve.encoded_oid, CKA_VALUE = self.sk.privkey.secret_multiplier) self.text = text self.tbs = self.h(text).digest() def verify(self, sig): assert self.vk.verify(sig, self.text, self.h) class Results(object): def __init__(self, name): self.lock = threading.RLock() self.name = name self.sum = datetime.timedelta(seconds = 0) self.t0 = datetime.datetime.now() self.t1 = None self.n = 0 def add(self, t0, t1): with self.lock: delta = t1 - t0 self.sum += t1 - t0 self.n += 1 if not args.quiet: print("{:4d} {}".format(self.n, delta)) @property def mean(self): return self.sum / self.n @property def throughput(self): return (self.t1 - self.t0) / self.n @property def sigs_per_second(self): return self.n / (self.t1 - self.t0).total_seconds() def report(self): with self.lock: if self.t1 is None: self.t1 = datetime.datetime.now() print("{0.name} sigs/second {0.sigs_per_second} mean {0.mean} throughput {0.throughput} (n {0.n}, t0 {0.t0}, t1 {0.t1})".format(self)) class Worker(threading.Thread): def run(self): session = p11.C_OpenSession(args.slot) while True: k = q.get() try: t0 = datetime.datetime.now() p11.C_SignInit(session, k.ckm, k.handle) sig = p11.C_Sign(session, k.tbs) t1 = datetime.datetime.now() if args.verify: k.verify(sig) results.add(t0, t1) finally: q.task_done() def main(): parser = argparse.ArgumentParser(description = __doc__, formatter_class = argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("-i", "--iterations", default = 100, type = int, help = "iterations") parser.add_argument("-p", "--pin", default = "fnord", help = "user PIN") parser.add_argument("-s", "--slot", default = 0, type = int, help = "slot number") parser.add_argument("-t", "--token", action = "store_true", help = "store key on token") parser.add_argument("-l", "--libpkcs11", default = libpkcs11_default, help = "PKCS #11 library") parser.add_argument("-k", "--keys", choices = tuple(key_table), nargs = "+", default = list(key_table), help = "keys to test") parser.add_argument("-q", "--quiet", action = "store_true", help = "be less chatty") parser.add_argument("-T", "--threads", default = 4, type = int, help = "worker thread count") parser.add_argument("-V", "--verify", action = "store_true", help = "verify signatures") global args args = parser.parse_args() global q q = queue.Queue() global p11 p11 = PKCS11(args.libpkcs11) if not args.quiet: print("Initializing") p11.C_Initialize(CKF_OS_LOCKING_OK) session = p11.C_OpenSession(args.slot) p11.C_Login(session, CKU_USER, args.pin) for i in range(args.threads): w = Worker() w.setDaemon(True) w.start() for name in args.keys: if not args.quiet: print("Starting test with key {}, {} iterations".format(name, args.iterations)) k = key_table[name] k.create(args, p11, session, "Your mother was a hamster") global results results = Results(name) for i in range(args.iterations): q.put(k) q.join() results.report() p11.C_DestroyObject(session, k.handle) p11.C_CloseAllSessions(args.slot) p11.C_Finalize() key_table = collections.OrderedDict() key_table.update(rsa_1024 = RSAKey(SHA256, '''\ -----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQC95QlDOvlQhdCe/a7eIoX9SGPVfXfA8/62ilnF+NcwLrhxkr2R 4EVQB65+9AbxqM8Hqol6fhZzmDs48cl2tOFGJpE8iMhiFm4i6SGl2RXYaG0xi+lJ FrXXCLFQovIEMuukBE129wra1xIB72tYR14lu8REX+Mhpbuz44M1jlCrlQIDAQAB AoGAeU928l8bZIiH9PnlG318kYkMVhd4SGjXQK/zl9hXSC2goNV4i1d1kCHIJMwq H3mTALe+aeVg3GnU85Tq+g2llzogoyXl8q902KbvImrM/XSbsue9/oj0OSgw+jKB faFzX6FxAtNV5pmU9QiwauBIl/3yPCF9ifim5zg+pWCqLaECQQD59Z/R6TrTHxp6 w2vH4CJyP5KORcf+eMa50SAriMVBXsJzsBiLLVxKIZfWbQn9gytJqJZKmIHezZQm dyam84fpAkEAwnvSF27RhxLXE037+t7k5MZti6BfNTeUBrwffteepL6qax9HK+h9 IQZ1vfNIqjZm8i7kQQyy4L8tRnk8mjZmzQJBAIUwfXWTilW+yBRMFx1M7+3itAv9 YODWqEWRCkxIN5tqi8CrP5jBleCmX8rRFTaxcxpvq42aD/GRp3SLntvs/ikCQCSg GOKc1gyv+Z0DFK8cBtMmoz6mRwfInbHe/7dtd8zis0lVLJwSPm5Xvxi0ljyn3h9B wW6Wq6Ezn50j+8u27wkCQQCcIFE01BDAdtFHtTJ3aaEM9IdMCYrcJ0I/Y0NTE2M6 lsTSiPyQjc4dQQJxFduvWHLx28bx+l7FTav7FaKntCJo -----END RSA PRIVATE KEY----- ''')) key_table.update(rsa_2048 = RSAKey(SHA256, '''\ -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAsbvq6syhDXD/OMVAuLoMceGQLUIiezfShVqFfyMqADjqhFRW Wbonn0XV9ZkypU4Ib9n6PtLATNaefhpsUlI4s+20YTlQ7GiwJ9p97/N1o1u060ja 4LdqhfYtn8GZX+JAfa5NqpmLKCJ58XJ3q28MPLRwYp5yKckjkzchZHFyjs1W7r5a JfeJ/vsQusX3klmCehJ1jxSHPh8o6lTjFMnBK8t360YTu0UGK/RUcEAYO7l7FWjd 8PjZfawXIrOAhCLkVvDFfpsl2oyFIL9d1QE87WdyyZXAtWLs62gnX+kiBq9gUhu5 GsgcQifHBcRiGZfH0TRIMgIsSjsorzHqJ9uoQwIDAQABAoIBAGqzx5/5A8NfEEpT 2bxNLcV8xqL1LmBNLh0TMEwYn1GM2fZh74lkwf7T3VTaCVbGlzgXZC4tNneq7XIF iPyPEi2rSnyH/XZAj2kNukfBIOHW37HVhloco14TYmajwuGWomMRrtz521pYAF+c +g042N7k8Qez2hQOBkaOdYSouz7RNdJUGUocRhcSkh+QZTBwtQxrkuhhHN+zkIri +Q09hF2hAliHrh6mow8ci0gRsXnZzsdJfTX8CasHWTIll4gfrvWnUY7iYqB6ynRU YN+7IgQXMUFLziIlH1qN+DlEYdznsgAPSS3JdTWh0cvjiO8wTFAnOIdsj+BpKoDB PK2zzDkCgYEA3TP8h4Ds/y1tDijE3Sarrg0vWuY97sJmAla4qFHH4hscZ84NDzTM I/ohLPJgpeR2MaBqZmVk9UFrd3rdt3/lX6kSO7Kffa9rVgfOB4CqJ4joso3j0qY8 V/iVBcDcD1h4aXCRX2tMJICUTgVU/N8/2wBEElcOUjZHGlcHmbHndlUCgYEAzbFm ttPuIHrYHkmOm/DjYB65+WD8gfPMdeUbnx+yIt5xwimIbq1qWzl4++goTAAyaU/7 qM9IfveRue0B7yjnPa8fjN+CcXMGM6a3BIqeIv1icfgjHxlt7D+64FpENWXHvFE0 MhRliINfkTHm+U4+1s0045a+bLdTbfVly1gATDcCgYEAyOaoWmFL3k7hl1SLx9eR YVj0Q3iNk0XX5BPjTmxIQCEjYVwRHFh1d897Rhk0kja26kepmypH0UADXNaofDqa lpE10CZhGIOz1sTr6ICBCbscrN6VpgH5GGTa5AjPVNijNBBa1/DZjOWCzIGnOKuC kWLicE3E4gIN/exBKOQdNqkCgYEAjA5PMg38BoGexoCvad8L81b4qqUvSg0HGv91 X1Plp3hvXRWKoFHUKWlox528UoOPz8V2ReteIZXQ1BhdSMtBKO8lPHa0CyuW/XR3 CdCY/Jorfg7HW1WlU0fRpxHPf8xdxAxGzhK1T86kM+kWrIpqnzf62zy5TK1HUYfW WC8DhOECgYBzU8hIA0PU7aRPUs0o9MO9XcvVPvdX6UOKdNb9CnBMudS/chKHJUYP d0fFAiVaRX0JMQ0RSrenxCqfWVtW3T3wFYNHB/IFRIUT3I44wwXJTNOeoi3FDTMx EQfc0UFoFHyc3mYEKR4zHheqQG5OFBN89LqG3S+O69vc1qwCvNKL+Q== -----END RSA PRIVATE KEY----- ''')) key_table.update(rsa_4096 = RSAKey(SHA256, '''\ -----BEGIN RSA PRIVATE KEY----- MIIJKAIBAAKCAgEAzWpYSQt+DrUNI+EJT/ko92wM2POfFnmm3Kc34nmuK6sez0DJ r9Vib9R5K46RNgqcUdAodjU6cy3/MZA53SqP7RwR/LQtWmK2a+T4iey2vQZ0iCDA 2NI4gjgmCAjZnOD/m5yUXjCig/wJ8pGXolg8oHnvdeLg1joIOSF9OudLrI6aJnDg OfdegzmCWXmWl7TXrqHVIWZhZZF7qQZRso6ZQ1/8mjpvVD0drASBxMnIzvpe4ynr Y2NB807X/D5bbScp292ZKTNf5unPN1SsFy5ymzfLZrfNksYef6xPXcVr6OiObi49 De8e11aNPj6fgLzzqAu1rjjrDkgvXx5G7gPJXq1aq6uxB2cKMrRS+ivmyC8vQlzP lQwW20oYeeOfCg7ddNAJcu3jNTuNJaZdhc9szpVhV8DXZoXe/RzUNjZH7wUqueHy fpbLwS+h3McJqrbWFdCQBivZnoI05cF2JIHEeR3S0Gyo2/IheNeFX2Tt8oDnHY4a olRHiR5CMdM8UoGSxR9Y12fZ9dcqdCH3d6wDAsBDHTCE8ZIwFwhW6iA+g54YE3X7 BlsgWr60poCDgH+CJjh0VDVxqL7r+w76sD9WAQMa7Gb+Mp2XCYnIZPXTrsmwVbZ9 s5vFXUEODYom6qBlbZB8gyZzee5Skc1jx2fnmqxRtflA4W3xVAQFof2rFiUCAwEA AQKCAgBxhQXJSFqf0hqy61h0I+Qp6EKpWuleSFiYtKjDti803tql+s37KFfAKZHV KnLBhNeitwDFYuEsag0P3P69ZRopFUwzdXdi7g6WTfG0d2b9y6V23XL14Cduf400 /38TnZxk6QFtlD8b5ZuxvBgqlczbeseFRJ6whV2qBQHqHYzKjfxOpi6kmjpXFt8c h39b04smbTUVwjitIttOK7nWjcvRWiiFKyn/Sc8uE0eL81/QUrlBnRcC1AXMapQe SG/KQMx3P123UTb8q9XiZB6+qOKZORplZ8pqBKcyM42g6suZ6XtdFJyVKMLIioKA FaecQ8/73IzI/ZeZSvcy/85/FwSfGjHD7C7vL9kfg77no+IvHYlBYiIqtTddpQH5 LGJAJnOGtk047/OjTmL8QyylvDAv8jBeZZdbOX7L+8jk5DbHmfUcDjvBS9g+Fbfk jDurphrp1dHn/YgaA27NZs87TPWX1aVPiOlXEhO9SHHiiKCHDpBzV1gW/eiho33s +uEr57ZoakzonN/zNb7KqHUO/ZGwMg+V9bVIgThqbdgmxNz7JFz14CN79yPmW5QT 1P1v7a6xWaZTALe2HGvy0B+iRzhLpay1tI4O/omPj9vUzVJwGHztVt0RddcmA9wV Y3qglRNl+YvNlm6BUn8KwPIqki8JoioA8J1EQ5mz/K0fbrzcOQKCAQEA8TCqq0pb mfxtsf42zhsSUw1VdcCIG0IYcSWxIiCfujryAQX1tmstZeRchlykXmdO+JDcpvMy BKBD7188JEWjCX1IRRtHxTJ5WG+pE8sNPLNL8eZVZ+CEbNjVk4dtWGLwyNm+rQkM NmOlm+7ZHdezBXljZOeqZbdsTSDQcGYG8JxlvLpAN60pjIGvTdTrdnksMhB4PK+l 7KtyEVDWXU/VT6kqhP3Ri1doHv/81BplgfjEJM8ZxmasfP4SlJ1olKqsHMFSrclj ZCAemKEexVyzg8cHm9ghj6MLQZe3gs94V6h8I2ifrBBNHMrZgYg2Db0GeyYrr+kZ GDjT0DZp0jgyfwKCAQEA2gdTclmrfAU/67ziOJbjkMgfhBdteE1BbJDNUca0zB6q Ju4BwNgt0cxHMbltgE2/8hWP95HIaQdh1mIgwSK93MxRbaAvAQfF3muJxvMR5Mru DejE+IEK9eZetgkNHGWyfiFzBWHda/Z9PQkqYtRfop5qFBVAPZ4YzR5hT0j64eDQ N/z9C0ZB6RL9EcXJgEYgGI3wP8Qsrw3JRBQN0SCVRmrEJm4WIXs+CEHOk56/VbPM v82uwbHVghS0U9bEZvNoeq7ZQjS2tRXXRJeOgQyCNvYy670T0KvQZoDb59EbEDSz eQZS1J7rDEBHW+VwRSJA8noMEgZdEv8AxbEF2CddWwKCAQAMwH71iXvoW1FNbNxm 70V7wKO5ExHfJxJ1wQFphYIMbZtn9HG2UFpZHcbKj9Fc8GdbewU/inIljnepC0b5 v/jLwqT0imm0AmQqCdVNp5mukOg+BOiVEmjN/HTmVO2yE6EZbXHIYkcUBRa3dNxj 2IitjGp15k27DQSb21VJ7AsH46z5WnuUtgIRXLXxDoXYgLWWfApvYvYJ2lKwma6L xnHHwXDvESBoFpn5sZ0jdbXSNl3geFarh7gs753534ys940cBBij+ZbYr14Owc4H r0wKdpZvZfD4UC2DLUtVjjSVpeHSWXC/vyjkkdEIKTR6a3kRP8ZliZR7FF4Wjxnv NGtvAoIBAEu5g6gRsNewUxUjU0boUT115ExSfrjrzC9S05z1cNH8TIic3YsHClL1 qjyA9KE9X89K4efQgFTKNZbqGgo6cMsBQ77ZhbnL41Nu8jlhLvPR74BxOgg9eXsS eg6rchxMzgO0xmg2J1taDwFl74zHyjeG4bz77IX6JQ8I4C9TX5+YH3lyqsiBrF6x M6g6k9Ozh24/zhO3pPVfymmUtX/O20nLxzi5v4H9dfwULxVia33upsxvOaUYiNlX K5J641gGbmE93UN7X4HhhhTStrHnkEpalDEASKOPKSCQ3M/U9ptYUoVURuyGDYkB wkcOl0HLtdcBwLN59lWkr7X519fNREUCggEBAMk39k+shD2DW8ubE/LgoforwfT2 558FPxpZ+pGwMHL3ZnLuQuiROyPyQZj/ZmmLAa2TPrwS/ssln46Y2KesejWK/0Hq 8SaFLhOjacF8u5IOOKBZvx+HOT6ctRNBVyzt9A8wu0DE6nzc5HQpm9TMXrOLuZ0L u22yFikwoIgYpU6hBdbg1mnirZS/ZyqJV9gWB6ZYyUAUGdgBqL6euSAAqBp93qz8 sQLesqTufT1mVZd/ndLyvjDJjNKUE0w1g/1xNtg6N5aM+7pc/DwE/s+EtCxc/858 dQYLBHIPcw6e0FdL3nTs44BpAqcK28N5eWbe/KaZ3EA0lHRmyOQ++WgU6jo= -----END RSA PRIVATE KEY----- ''')) key_table.update(ecdsa_p256 = ECDSAKey(SHA256, '''\ -----BEGIN EC PRIVATE KEY----- MHcCAQEEIPBrVhD1iFF2e8wPkPf4N1038iR8xPgku/CVOT8lcSztoAoGCCqGSM49 AwEHoUQDQgAE3mB5BmN5Fa4fV74LsDWIpBUxktPqYGJ6WOBrjPs1HWkNU7JHO3qY 9yy+CXFSPb89GWQgb5wLtNPn4QYMj+KRTA== -----END EC PRIVATE KEY----- ''')) key_table.update(ecdsa_p384 = ECDSAKey(SHA384, '''\ -----BEGIN EC PRIVATE KEY----- MIGkAgEBBDCVGo35Hbrf3Iys7mWRIm5yjg+6vPIgzbp2jCbDyszBo+wTxmQambG4 g8yocp4wM6+gBwYFK4EEACKhZANiAATYwa+M8T8jsNHKmMZTvPPflUIfrjuZZo1D 3kkkmN4r6cTNctjaeRdAfD0X40l4yPnGIP9ParuKVVl1y0TdQ7BS3g/Gj/LP33HD ESP8gFDIKFCWSDX0uhmy+HsGsPwgNoY= -----END EC PRIVATE KEY----- ''')) key_table.update(ecdsa_p521 = ECDSAKey(SHA512, '''\ -----BEGIN EC PRIVATE KEY----- MIHcAgEBBEIBtf+LKhJNQEJRFQ2cGQPcviwfp9IKSnD5EFTqAPtv7/+t2FtmdHHP /fWIlZ7jcC5N9dWy6bKGu3+FqwgZAYLpsqigBwYFK4EEACOhgYkDgYYABADdfcUe P0oAZQ5308v5ijcg4hePvhvVi+QKcrwmE9kirXCFoYN1tzPmXZmw8lNJenrbwaNz opJR84LBHnomGPogAQGF0aRk0jE8w1j1oMfrrzV6vCWnkh7pyzsDnrLU1HrkWeqw ihzwMzYJgFzToDH+fCh7nrBFZZZ9P9gPYMlSM5UMeA== -----END EC PRIVATE KEY----- ''')) if __name__ == "__main__": main()