diff options
author | Rob Austein <sra@hactrn.net> | 2017-04-14 15:11:20 -0400 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2017-04-14 15:11:20 -0400 |
commit | 46836ba77583759f649625fe22bd744694b92f90 (patch) | |
tree | b5258f0a7db3fedc5bb34f9201bae1b05f63bdfc /cryptech | |
parent | 8956d3e045e32748cdb8df7b2f69cb6c9e0837d5 (diff) |
Python interface API will need to be cryptech.libhal for installation.
Diffstat (limited to 'cryptech')
-rw-r--r-- | cryptech/__init__.py | 0 | ||||
-rw-r--r-- | cryptech/libhal.py | 674 |
2 files changed, 674 insertions, 0 deletions
diff --git a/cryptech/__init__.py b/cryptech/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/cryptech/__init__.py diff --git a/cryptech/libhal.py b/cryptech/libhal.py new file mode 100644 index 0000000..0c6b3f6 --- /dev/null +++ b/cryptech/libhal.py @@ -0,0 +1,674 @@ +# Copyright (c) 2016, NORDUnet A/S +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# - Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# - Neither the name of the NORDUnet nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +""" +A Python interface to the Cryptech libhal RPC API. +""" + +# A lot of this is hand-generated XDR data structure encoding. If and +# when we ever convert the C library to use data structures processed +# by rpcgen, we may want to rewrite this code to use the output of +# something like https://github.com/floodlight/xdr.git -- in either +# case the generated code would just be for the data structures, we're +# not likely to want to use the full ONC RPC mechanism. + +import os +import uuid +import xdrlib +import socket +import logging +import contextlib + +logger = logging.getLogger(__name__) + + +SLIP_END = chr(0300) # indicates end of packet +SLIP_ESC = chr(0333) # indicates byte stuffing +SLIP_ESC_END = chr(0334) # ESC ESC_END means END data byte +SLIP_ESC_ESC = chr(0335) # ESC ESC_ESC means ESC data byte + + +def slip_encode(buffer): + return SLIP_END + buffer.replace(SLIP_ESC, SLIP_ESC + SLIP_ESC_ESC).replace(SLIP_END, SLIP_ESC + SLIP_ESC_END) + SLIP_END + +def slip_decode(buffer): + return buffer.strip(SLIP_END).replace(SLIP_ESC + SLIP_ESC_END, SLIP_END).replace(SLIP_ESC + SLIP_ESC_ESC, SLIP_ESC) + + +HAL_OK = 0 + +class HALError(Exception): + "LibHAL error" + + table = [None] + + @classmethod + def define(cls, **kw): + assert len(kw) == 1 + name, text = kw.items()[0] + e = type(name, (cls,), dict(__doc__ = text)) + cls.table.append(e) + globals()[name] = e + +HALError.define(HAL_ERROR_BAD_ARGUMENTS = "Bad arguments given") +HALError.define(HAL_ERROR_UNSUPPORTED_KEY = "Unsupported key type or key length") +HALError.define(HAL_ERROR_IO_SETUP_FAILED = "Could not set up I/O with FPGA") +HALError.define(HAL_ERROR_IO_TIMEOUT = "I/O with FPGA timed out") +HALError.define(HAL_ERROR_IO_UNEXPECTED = "Unexpected response from FPGA") +HALError.define(HAL_ERROR_IO_OS_ERROR = "Operating system error talking to FPGA") +HALError.define(HAL_ERROR_IO_BAD_COUNT = "Bad byte count") +HALError.define(HAL_ERROR_CSPRNG_BROKEN = "CSPRNG is returning nonsense") +HALError.define(HAL_ERROR_KEYWRAP_BAD_MAGIC = "Bad magic number while unwrapping key") +HALError.define(HAL_ERROR_KEYWRAP_BAD_LENGTH = "Length out of range while unwrapping key") +HALError.define(HAL_ERROR_KEYWRAP_BAD_PADDING = "Non-zero padding detected unwrapping key") +HALError.define(HAL_ERROR_IMPOSSIBLE = "\"Impossible\" error") +HALError.define(HAL_ERROR_ALLOCATION_FAILURE = "Memory allocation failed") +HALError.define(HAL_ERROR_RESULT_TOO_LONG = "Result too long for buffer") +HALError.define(HAL_ERROR_ASN1_PARSE_FAILED = "ASN.1 parse failed") +HALError.define(HAL_ERROR_KEY_NOT_ON_CURVE = "EC key is not on its purported curve") +HALError.define(HAL_ERROR_INVALID_SIGNATURE = "Invalid signature") +HALError.define(HAL_ERROR_CORE_NOT_FOUND = "Requested core not found") +HALError.define(HAL_ERROR_CORE_BUSY = "Requested core busy") +HALError.define(HAL_ERROR_KEYSTORE_ACCESS = "Could not access keystore") +HALError.define(HAL_ERROR_KEY_NOT_FOUND = "Key not found") +HALError.define(HAL_ERROR_KEY_NAME_IN_USE = "Key name in use") +HALError.define(HAL_ERROR_NO_KEY_SLOTS_AVAILABLE = "No key slots available") +HALError.define(HAL_ERROR_PIN_INCORRECT = "PIN incorrect") +HALError.define(HAL_ERROR_NO_CLIENT_SLOTS_AVAILABLE = "No client slots available") +HALError.define(HAL_ERROR_FORBIDDEN = "Forbidden") +HALError.define(HAL_ERROR_XDR_BUFFER_OVERFLOW = "XDR buffer overflow") +HALError.define(HAL_ERROR_RPC_TRANSPORT = "RPC transport error") +HALError.define(HAL_ERROR_RPC_PACKET_OVERFLOW = "RPC packet overflow") +HALError.define(HAL_ERROR_RPC_BAD_FUNCTION = "Bad RPC function number") +HALError.define(HAL_ERROR_KEY_NAME_TOO_LONG = "Key name too long") +HALError.define(HAL_ERROR_MASTERKEY_NOT_SET = "Master key (Key Encryption Key) not set") +HALError.define(HAL_ERROR_MASTERKEY_FAIL = "Master key generic failure") +HALError.define(HAL_ERROR_MASTERKEY_BAD_LENGTH = "Master key of unacceptable length") +HALError.define(HAL_ERROR_KS_DRIVER_NOT_FOUND = "Keystore driver not found") +HALError.define(HAL_ERROR_KEYSTORE_BAD_CRC = "Bad CRC in keystore") +HALError.define(HAL_ERROR_KEYSTORE_BAD_BLOCK_TYPE = "Unsupported keystore block type") +HALError.define(HAL_ERROR_KEYSTORE_LOST_DATA = "Keystore appears to have lost data") +HALError.define(HAL_ERROR_BAD_ATTRIBUTE_LENGTH = "Bad attribute length") +HALError.define(HAL_ERROR_ATTRIBUTE_NOT_FOUND = "Attribute not found") +HALError.define(HAL_ERROR_NO_KEY_INDEX_SLOTS = "No key index slots available") +HALError.define(HAL_ERROR_KSI_INDEX_UUID_MISORDERED = "Key index UUID misordered") +HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_ORPHANED = "Key index chunk orphaned") +HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_MISSING = "Key index chunk missing") +HALError.define(HAL_ERROR_KSI_INDEX_CHUNK_OVERLAPS = "Key index chunk overlaps") +HALError.define(HAL_ERROR_KEYSTORE_WRONG_BLOCK_TYPE = "Wrong block type in keystore") +HALError.define(HAL_ERROR_RPC_PROTOCOL_ERROR = "RPC protocol error") +HALError.define(HAL_ERROR_NOT_IMPLEMENTED = "Not implemented") + + +class Enum(int): + + def __new__(cls, name, value): + self = int.__new__(cls, value) + self._name = name + setattr(self.__class__, name, self) + return self + + def __str__(self): + return self._name + + def __repr__(self): + return "<Enum:{0.__class__.__name__} {0._name}:{0:d}>".format(self) + + _counter = 0 + + @classmethod + def define(cls, names): + symbols = [] + for name in names.translate(None, "{}").split(","): + if "=" in name: + name, sep, expr = name.partition("=") + cls._counter = eval(expr.strip()) + if not isinstance(cls._counter, int): + raise TypeError + symbols.append(cls(name.strip(), cls._counter)) + cls._counter += 1 + cls.index = dict((int(symbol), symbol) for symbol in symbols) + globals().update((symbol._name, symbol) for symbol in symbols) + + def xdr_packer(self, packer): + packer.pack_uint(self) + + +class RPCFunc(Enum): pass + +RPCFunc.define(''' + RPC_FUNC_GET_VERSION, + RPC_FUNC_GET_RANDOM, + RPC_FUNC_SET_PIN, + RPC_FUNC_LOGIN, + RPC_FUNC_LOGOUT, + RPC_FUNC_LOGOUT_ALL, + RPC_FUNC_IS_LOGGED_IN, + RPC_FUNC_HASH_GET_DIGEST_LEN, + RPC_FUNC_HASH_GET_DIGEST_ALGORITHM_ID, + RPC_FUNC_HASH_GET_ALGORITHM, + RPC_FUNC_HASH_INITIALIZE, + RPC_FUNC_HASH_UPDATE, + RPC_FUNC_HASH_FINALIZE, + RPC_FUNC_PKEY_LOAD, + RPC_FUNC_PKEY_OPEN, + RPC_FUNC_PKEY_GENERATE_RSA, + RPC_FUNC_PKEY_GENERATE_EC, + RPC_FUNC_PKEY_CLOSE, + RPC_FUNC_PKEY_DELETE, + RPC_FUNC_PKEY_GET_KEY_TYPE, + RPC_FUNC_PKEY_GET_KEY_FLAGS, + RPC_FUNC_PKEY_GET_PUBLIC_KEY_LEN, + RPC_FUNC_PKEY_GET_PUBLIC_KEY, + RPC_FUNC_PKEY_SIGN, + RPC_FUNC_PKEY_VERIFY, + RPC_FUNC_PKEY_MATCH, + RPC_FUNC_PKEY_GET_KEY_CURVE, + RPC_FUNC_PKEY_SET_ATTRIBUTES, + RPC_FUNC_PKEY_GET_ATTRIBUTES, + RPC_FUNC_PKEY_EXPORT, + RPC_FUNC_PKEY_IMPORT, +''') + +class HALDigestAlgorithm(Enum): pass + +HALDigestAlgorithm.define(''' + HAL_DIGEST_ALGORITHM_NONE, + HAL_DIGEST_ALGORITHM_SHA1, + HAL_DIGEST_ALGORITHM_SHA224, + HAL_DIGEST_ALGORITHM_SHA256, + HAL_DIGEST_ALGORITHM_SHA512_224, + HAL_DIGEST_ALGORITHM_SHA512_256, + HAL_DIGEST_ALGORITHM_SHA384, + HAL_DIGEST_ALGORITHM_SHA512 +''') + +class HALKeyType(Enum): pass + +HALKeyType.define(''' + HAL_KEY_TYPE_NONE, + HAL_KEY_TYPE_RSA_PRIVATE, + HAL_KEY_TYPE_RSA_PUBLIC, + HAL_KEY_TYPE_EC_PRIVATE, + HAL_KEY_TYPE_EC_PUBLIC +''') + +class HALCurve(Enum): pass + +HALCurve.define(''' + HAL_CURVE_NONE, + HAL_CURVE_P256, + HAL_CURVE_P384, + HAL_CURVE_P521 +''') + +class HALUser(Enum): pass + +HALUser.define(''' + HAL_USER_NONE, + HAL_USER_NORMAL, + HAL_USER_SO, + HAL_USER_WHEEL +''') + +HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE = (1 << 0) +HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT = (1 << 1) +HAL_KEY_FLAG_USAGE_DATAENCIPHERMENT = (1 << 2) +HAL_KEY_FLAG_TOKEN = (1 << 3) +HAL_KEY_FLAG_PUBLIC = (1 << 4) +HAL_KEY_FLAG_EXPORTABLE = (1 << 5) + +HAL_PKEY_ATTRIBUTE_NIL = (0xFFFFFFFF) + + +class UUID(uuid.UUID): + + def xdr_packer(self, packer): + packer.pack_bytes(self.bytes) + + +def cached_property(func): + + attr_name = "_" + func.__name__ + + def wrapped(self): + try: + value = getattr(self, attr_name) + except AttributeError: + value = func(self) + setattr(self, attr_name, value) + return value + + wrapped.__name__ = func.__name__ + + return property(wrapped) + + +class Handle(object): + + def __int__(self): + return self.handle + + def __cmp__(self, other): + return cmp(self.handle, int(other)) + + def xdr_packer(self, packer): + packer.pack_uint(self.handle) + + +class Digest(Handle): + + def __init__(self, hsm, handle, algorithm): + self.hsm = hsm + self.handle = handle + self.algorithm = algorithm + + def update(self, data): + self.hsm.hash_update(self, data) + + def finalize(self, length = None): + return self.hsm.hash_finalize(self, length or self.digest_length) + + @cached_property + def algorithm_id(self): + return self.hsm.hash_get_digest_algorithm_id(self.algorithm) + + @cached_property + def digest_length(self): + return self.hsm.hash_get_digest_length(self.algorithm) + + +class LocalDigest(object): + """ + Implements same interface as Digest class, but using PyCrypto, to + support mixed-mode PKey operations. This only supports algorithms + that PyCrypto supports, so no SHA512/224 or SHA512/256, sorry. + """ + + def __init__(self, hsm, handle, algorithm, key): + from Crypto.Hash import HMAC, SHA, SHA224, SHA256, SHA384, SHA512 + self.hsm = hsm + self.handle = handle + self.algorithm = algorithm + try: + h = self._algorithms[algorithm] + except AttributeError: + self._algorithms = { + HAL_DIGEST_ALGORITHM_SHA1 : SHA.SHA1Hash, + HAL_DIGEST_ALGORITHM_SHA224 : SHA224.SHA224Hash, + HAL_DIGEST_ALGORITHM_SHA256 : SHA256.SHA256Hash, + HAL_DIGEST_ALGORITHM_SHA384 : SHA384.SHA384Hash, + HAL_DIGEST_ALGORITHM_SHA512 : SHA512.SHA512Hash + } + h = self._algorithms[algorithm] + self.digest_length = h.digest_size + self.algorithm_id = chr(0x30) + chr(2 + len(h.oid)) + h.oid + self._context = HMAC.HMAC(key = key, digestmod = h) if key else h() + + def update(self, data): + self._context.update(data) + + def finalize(self, length = None): + return self._context.digest() + + def finalize_padded(self, pkey): + if pkey.key_type not in (HAL_KEY_TYPE_RSA_PRIVATE, HAL_KEY_TYPE_RSA_PUBLIC): + return self.finalize() + # PKCS #1.5 requires the digest to be wrapped up in an ASN.1 DigestInfo object. + from Crypto.Util.asn1 import DerSequence, DerNull, DerOctetString + return DerSequence([DerSequence([self._context.oid, DerNull().encode()]).encode(), + DerOctetString(self.finalize()).encode()]).encode() + + +class PKey(Handle): + + def __init__(self, hsm, handle, uuid): + self.hsm = hsm + self.handle = handle + self.uuid = uuid + self.deleted = False + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if not self.deleted: + self.close() + + def close(self): + self.hsm.pkey_close(self) + + def delete(self): + self.hsm.pkey_delete(self) + self.deleted = True + + @cached_property + def key_type(self): + return self.hsm.pkey_get_key_type(self) + + @cached_property + def key_curve(self): + return self.hsm.pkey_get_key_curve(self) + + @cached_property + def key_flags(self): + return self.hsm.pkey_get_key_flags(self) + + @cached_property + def public_key_len(self): + return self.hsm.pkey_get_public_key_len(self) + + @cached_property + def public_key(self): + return self.hsm.pkey_get_public_key(self, self.public_key_len) + + def sign(self, hash = 0, data = "", length = 1024): + return self.hsm.pkey_sign(self, hash = hash, data = data, length = length) + + def verify(self, hash = 0, data = "", signature = None): + self.hsm.pkey_verify(self, hash = hash, data = data, signature = signature) + + def set_attributes(self, attributes = None, **kwargs): + assert attributes is None or not kwargs + self.hsm.pkey_set_attributes(self, attributes or kwargs) + + def get_attributes(self, attributes): + attrs = self.hsm.pkey_get_attributes(self, attributes, 0) + attrs = dict((k, v) for k, v in attrs.iteritems() if v != HAL_PKEY_ATTRIBUTE_NIL) + result = dict((a, None) for a in attributes) + result.update(self.hsm.pkey_get_attributes(self, attrs.iterkeys(), sum(attrs.itervalues()))) + return result + + def export_pkey(self, pkey): + return self.hsm.pkey_export(pkey = pkey, kekek = self, pkcs8_max = 2560, kek_max = 512) + + def import_pkey(self, pkcs8, kek, flags = 0): + return self.hsm.pkey_import(kekek = self, pkcs8 = pkcs8, kek = kek, flags = flags) + +class HSM(object): + + mixed_mode = False + debug_io = False + + def _raise_if_error(self, status): + if status != 0: + raise HALError.table[status]() + + def __init__(self, sockname = os.getenv("CRYPTECH_RPC_CLIENT_SOCKET_NAME", + "/tmp/.cryptech_muxd.rpc")): + self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.socket.connect(sockname) + self.sockfile = self.socket.makefile("rb") + + def _send(self, msg): # Expects an xdrlib.Packer + msg = slip_encode(msg.get_buffer()) + if self.debug_io: + logger.debug("send: %s", ":".join("{:02x}".format(ord(c)) for c in msg)) + self.socket.sendall(msg) + + def _recv(self, code): # Returns an xdrlib.Unpacker + closed = False + while True: + msg = [self.sockfile.read(1)] + while msg[-1] != SLIP_END: + if msg[-1] == "": + raise HAL_ERROR_RPC_TRANSPORT() + msg.append(self.sockfile.read(1)) + if self.debug_io: + logger.debug("recv: %s", ":".join("{:02x}".format(ord(c)) for c in msg)) + msg = slip_decode("".join(msg)) + if not msg: + continue + msg = xdrlib.Unpacker("".join(msg)) + if msg.unpack_uint() != code: + continue + return msg + + _pack_builtin = (((int, long), "_pack_uint"), + (str, "_pack_bytes"), + ((list, tuple, set), "_pack_array"), + (dict, "_pack_items")) + + def _pack_arg(self, packer, arg): + if hasattr(arg, "xdr_packer"): + return arg.xdr_packer(packer) + for cls, method in self._pack_builtin: + if isinstance(arg, cls): + return getattr(self, method)(packer, arg) + raise RuntimeError("Don't know how to pack {!r} ({!r})".format(arg, type(arg))) + + def _pack_args(self, packer, args): + for arg in args: + self._pack_arg(packer, arg) + + def _pack_uint(self, packer, arg): + packer.pack_uint(arg) + + def _pack_bytes(self, packer, arg): + packer.pack_bytes(arg) + + def _pack_array(self, packer, arg): + packer.pack_uint(len(arg)) + self._pack_args(packer, arg) + + def _pack_items(self, packer, arg): + packer.pack_uint(len(arg)) + for name, value in arg.iteritems(): + self._pack_arg(packer, name) + self._pack_arg(packer, HAL_PKEY_ATTRIBUTE_NIL if value is None else value) + + @contextlib.contextmanager + def rpc(self, code, *args, **kwargs): + client = kwargs.get("client", 0) + packer = xdrlib.Packer() + packer.pack_uint(code) + packer.pack_uint(client) + self._pack_args(packer, args) + self._send(packer) + unpacker = self._recv(code) + client = unpacker.unpack_uint() + self._raise_if_error(unpacker.unpack_uint()) + yield unpacker + unpacker.done() + + def get_version(self): + with self.rpc(RPC_FUNC_GET_VERSION) as r: + return r.unpack_uint() + + def get_random(self, n): + with self.rpc(RPC_FUNC_GET_RANDOM, n) as r: + return r.unpack_bytes() + + def set_pin(self, user, pin, client = 0): + with self.rpc(RPC_FUNC_SET_PIN, user, pin, client = client): + return + + def login(self, user, pin, client = 0): + with self.rpc(RPC_FUNC_LOGIN, user, pin, client = client): + return + + def logout(self, client = 0): + with self.rpc(RPC_FUNC_LOGOUT, client = client): + return + + def logout_all(self): + with self.rpc(RPC_FUNC_LOGOUT_ALL): + return + + def is_logged_in(self, user, client = 0): + with self.rpc(RPC_FUNC_IS_LOGGED_IN, user, client = client): + return + + def hash_get_digest_length(self, alg): + with self.rpc(RPC_FUNC_HASH_GET_DIGEST_LEN, alg) as r: + return r.unpack_uint() + + def hash_get_digest_algorithm_id(self, alg, max_len = 256): + with self.rpc(RPC_FUNC_HASH_GET_DIGEST_ALGORITHM_ID, alg, max_len) as r: + return r.unpack_bytes() + + def hash_get_algorithm(self, handle): + with self.rpc(RPC_FUNC_HASH_GET_ALGORITHM, handle) as r: + return HALDigestAlgorithm.index[r.unpack_uint()] + + def hash_initialize(self, alg, key = "", client = 0, session = 0, mixed_mode = None): + if mixed_mode is None: + mixed_mode = self.mixed_mode + if mixed_mode: + return LocalDigest(self, 0, alg, key) + else: + with self.rpc(RPC_FUNC_HASH_INITIALIZE, session, alg, key, client = client) as r: + return Digest(self, r.unpack_uint(), alg) + + def hash_update(self, handle, data): + with self.rpc(RPC_FUNC_HASH_UPDATE, handle, data): + return + + def hash_finalize(self, handle, length = None): + if length is None: + length = self.hash_get_digest_length(self.hash_get_algorithm(handle)) + with self.rpc(RPC_FUNC_HASH_FINALIZE, handle, length) as r: + return r.unpack_bytes() + + def pkey_load(self, der, flags = 0, client = 0, session = 0): + with self.rpc(RPC_FUNC_PKEY_LOAD, session, der, flags, client = client) as r: + pkey = PKey(self, r.unpack_uint(), UUID(bytes = r.unpack_bytes())) + logger.debug("Loaded pkey %s", pkey.uuid) + return pkey + + def pkey_open(self, uuid, client = 0, session = 0): + with self.rpc(RPC_FUNC_PKEY_OPEN, session, uuid, client = client) as r: + pkey = PKey(self, r.unpack_uint(), uuid) + logger.debug("Opened pkey %s", pkey.uuid) + return pkey + + def pkey_generate_rsa(self, keylen, flags = 0, exponent = "\x01\x00\x01", client = 0, session = 0): + with self.rpc(RPC_FUNC_PKEY_GENERATE_RSA, session, keylen, exponent, flags, client = client) as r: + pkey = PKey(self, r.unpack_uint(), UUID(bytes = r.unpack_bytes())) + logger.debug("Generated RSA pkey %s", pkey.uuid) + return pkey + + def pkey_generate_ec(self, curve, flags = 0, client = 0, session = 0): + with self.rpc(RPC_FUNC_PKEY_GENERATE_EC, session, curve, flags, client = client) as r: + pkey = PKey(self, r.unpack_uint(), UUID(bytes = r.unpack_bytes())) + logger.debug("Generated EC pkey %s", pkey.uuid) + return pkey + + def pkey_close(self, pkey): + try: + logger.debug("Closing pkey %s", pkey.uuid) + except AttributeError: + pass + with self.rpc(RPC_FUNC_PKEY_CLOSE, pkey): + return + + def pkey_delete(self, pkey): + try: + logger.debug("Deleting pkey %s", pkey.uuid) + except AttributeError: + pass + with self.rpc(RPC_FUNC_PKEY_DELETE, pkey): + return + + def pkey_get_key_type(self, pkey): + with self.rpc(RPC_FUNC_PKEY_GET_KEY_TYPE, pkey) as r: + return HALKeyType.index[r.unpack_uint()] + + def pkey_get_key_curve(self, pkey): + with self.rpc(RPC_FUNC_PKEY_GET_KEY_CURVE, pkey) as r: + return HALCurve.index[r.unpack_uint()] + + def pkey_get_key_flags(self, pkey): + with self.rpc(RPC_FUNC_PKEY_GET_KEY_FLAGS, pkey) as r: + return r.unpack_uint() + + def pkey_get_public_key_len(self, pkey): + with self.rpc(RPC_FUNC_PKEY_GET_PUBLIC_KEY_LEN, pkey) as r: + return r.unpack_uint() + + def pkey_get_public_key(self, pkey, length = None): + if length is None: + length = self.pkey_get_public_key_len(pkey) + with self.rpc(RPC_FUNC_PKEY_GET_PUBLIC_KEY, pkey, length) as r: + return r.unpack_bytes() + + def pkey_sign(self, pkey, hash = 0, data = "", length = 1024): + assert not hash or not data + if isinstance(hash, LocalDigest): + hash, data = 0, hash.finalize_padded(pkey) + with self.rpc(RPC_FUNC_PKEY_SIGN, pkey, hash, data, length) as r: + return r.unpack_bytes() + + def pkey_verify(self, pkey, hash = 0, data = "", signature = None): + assert not hash or not data + if isinstance(hash, LocalDigest): + hash, data = 0, hash.finalize_padded(pkey) + with self.rpc(RPC_FUNC_PKEY_VERIFY, pkey, hash, data, signature): + return + + def pkey_match(self, type = 0, curve = 0, mask = 0, flags = 0, + attributes = {}, length = 64, client = 0, session = 0): + u = UUID(int = 0) + n = length + s = 0 + while n == length: + with self.rpc(RPC_FUNC_PKEY_MATCH, session, type, curve, mask, flags, + attributes, s, length, u, client = client) as r: + s = r.unpack_uint() + n = r.unpack_uint() + for i in xrange(n): + u = UUID(bytes = r.unpack_bytes()) + yield u + + def pkey_set_attributes(self, pkey, attributes): + with self.rpc(RPC_FUNC_PKEY_SET_ATTRIBUTES, pkey, attributes): + return + + def pkey_get_attributes(self, pkey, attributes, attributes_buffer_len = 2048): + attributes = tuple(attributes) + with self.rpc(RPC_FUNC_PKEY_GET_ATTRIBUTES, pkey, attributes, attributes_buffer_len) as r: + n = r.unpack_uint() + if n != len(attributes): + raise HAL_ERROR_RPC_PROTOCOL_ERROR + if attributes_buffer_len > 0: + return dict((r.unpack_uint(), r.unpack_bytes()) for i in xrange(n)) + else: + return dict((r.unpack_uint(), r.unpack_uint()) for i in xrange(n)) + + def pkey_export(self, pkey, kekek, pkcs8_max = 2560, kek_max = 512): + with self.rpc(RPC_FUNC_PKEY_EXPORT, pkey, kekek, pkcs8_max, kek_max) as r: + pkcs8, kek = r.unpack_bytes(), r.unpack_bytes() + logger.debug("Exported pkey %s", pkey.uuid) + return pkcs8, kek + + def pkey_import(self, kekek, pkcs8, kek, flags = 0, client = 0, session = 0): + with self.rpc(RPC_FUNC_PKEY_IMPORT, session, kekek, pkcs8, kek, flags, client = client) as r: + pkey = PKey(self, r.unpack_uint(), UUID(bytes = r.unpack_bytes())) + logger.debug("Imported pkey %s", pkey.uuid) + return pkey |