summaryrefslogtreecommitdiff
path: root/pkcs11.py
diff options
context:
space:
mode:
Diffstat (limited to 'pkcs11.py')
-rw-r--r--pkcs11.py92
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))