diff options
Diffstat (limited to 'pkcs11.py')
-rw-r--r-- | pkcs11.py | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/pkcs11.py b/pkcs11.py new file mode 100644 index 0000000..5256f36 --- /dev/null +++ b/pkcs11.py @@ -0,0 +1,92 @@ +import typing +import subprocess +import hashlib +import re +import os +import OpenSSL.crypto as cr + + +class HashAlg: + __slots__ = ('len', 'hashlib_name', 'mech', 'ident') + + def __init__(self, len, hashlib_name, mech, ident): + self.len = len + self.hashlib_name = hashlib_name + self.mech = mech + self.ident = ident + +HASH_ALG = { + 'SHA1': HashAlg(20, 'sha1', 'SHA1-RSA-PKCS', b'\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'), + 'SHA256': HashAlg(32, 'sha256', 'SHA256-RSA-PKCS', b'\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'), + 'SHA384': HashAlg(48, 'sha384', 'SHA384-RSA-PKCS', b'\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'), + 'SHA512': HashAlg(64, 'sha512', 'SHA512-RSA-PKCS', b'\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'), +} + + +class Key: + __slots__ = ('reader', 'id', 'cert_data', 'cert', 'use_rsa_pkcs', 'pin') + + reader: str + id: str + cert_data: bytes + cert: typing.Any + use_rsa_pkcs: bool + pin: str + + def __init__(self, reader: str, id: str) -> None: + self.reader = reader + self.id = id + cert_data = self.cert_data = subprocess.check_output(['pkcs11-tool', '--slot', reader, '-y', 'cert', '-r', '-d', id]) + self.cert = cr.load_certificate(cr.FILETYPE_ASN1, cert_data) + # TODO: autodetect based on key? + self.use_rsa_pkcs = True + + def sign(self, msg, hash_alg): + if self.use_rsa_pkcs: + mech = 'RSA-PKCS' + msg = hash_alg.ident + hashlib.new(hash_alg.hashlib_name, msg).digest() + else: + mech = hash_alg.mech + + env = os.environ.copy() + env['PIN'] = self.pin + proc = subprocess.run( + ['pkcs11-tool', '--slot', self.reader, '--id', self.id, '-m', mech, '--pin', 'env:PIN', '--sign'], + input = msg, + stdout = subprocess.PIPE, + env = env, + check = True, + ) + + return proc.stdout + +class Store: + __slots__ = ('_impl',) + + def __init__(self): + self._impl = cr.X509Store() + + def load_file(self, path): + self._impl.load_locations(path, None) + + def cert_chain(self, key: Key): + ctx = cr.X509StoreContext(self._impl, key.cert) + return ctx.get_verified_chain() + +def list() -> typing.Iterable[Key]: + lines = subprocess.check_output(['pkcs11-tool', '--list-slots'], text=True).splitlines() + readers = [] + for line in lines: + m = re.fullmatch('Slot ([0-9]+) \\(.*\\): .*', line) + if m is None: + continue + readers.append(m.group(1)) + + certs = {} + for reader in readers: + lines = subprocess.check_output(['pkcs11-tool', '--slot', reader, '-y', 'cert', '-O'], text=True).splitlines() + for line in lines: + m = re.fullmatch(' ID *: *([0-9a-f]+)$', line) + if m is None: + continue + yield Key(reader, m.group(1)) |