From 6e1e375b239a91efdaec355181f03954a700adf2 Mon Sep 17 00:00:00 2001 From: Ashley Straw Date: Fri, 22 Feb 2019 14:17:47 -0500 Subject: [PATCH] Merge changes feb_2019 Change-Id: I5611bcd37e3975a2b9e145a1842b478739b54a4f --- pycryptoki/attributes.py | 284 ++++++++++++++--------- pycryptoki/ca_extensions/derive_wrap.py | 56 +++++ pycryptoki/ca_extensions/per_key_auth.py | 53 +++++ pycryptoki/cryptoki.py | 20 +- pycryptoki/daemon/rpyc_pycryptoki.py | 12 + pycryptoki/defines.py | 17 ++ requirements.txt | 4 +- setup.py | 2 +- test_requirements.txt | 4 +- tests/functional/test_wrap_unwrap.py | 27 ++- tests/unittests/test_attr_conversions.py | 12 +- tox.ini | 8 +- 12 files changed, 362 insertions(+), 137 deletions(-) create mode 100644 pycryptoki/ca_extensions/derive_wrap.py create mode 100644 pycryptoki/ca_extensions/per_key_auth.py diff --git a/pycryptoki/attributes.py b/pycryptoki/attributes.py index 98a7cfa..c440293 100755 --- a/pycryptoki/attributes.py +++ b/pycryptoki/attributes.py @@ -8,30 +8,83 @@ import datetime import logging from collections import defaultdict -from ctypes import cast, c_void_p, create_string_buffer, c_bool, \ - c_ulong, pointer, POINTER, sizeof, c_char, string_at, c_ubyte +from ctypes import ( + cast, + c_void_p, + create_string_buffer, + c_bool, + c_ulong, + pointer, + POINTER, + sizeof, + c_char, + string_at, + c_ubyte, +) from functools import wraps -from six import b, string_types, integer_types, text_type, binary_type +from six import b, string_types, integer_types, binary_type from pycryptoki.conversions import from_bytestring -from .cryptoki import CK_ATTRIBUTE, CK_BBOOL, CK_ATTRIBUTE_TYPE, CK_ULONG, \ - CK_BYTE, CK_CHAR -from .defines import CKA_EKM_UID, CKA_GENERIC_1, CKA_GENERIC_2, \ - CKA_GENERIC_3 -from .defines import CKA_USAGE_LIMIT, CKA_USAGE_COUNT, CKA_CLASS, CKA_TOKEN, \ - CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION, CKA_CERTIFICATE_TYPE, \ - CKA_ISSUER, CKA_SERIAL_NUMBER, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE, \ - CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER, \ - CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE, \ - CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT, \ - CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT, \ - CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS, CKA_SUBPRIME_BITS, \ - CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_LOCAL, \ - CKA_MODIFIABLE, CKA_EXTRACTABLE, CKA_ALWAYS_SENSITIVE, CKA_NEVER_EXTRACTABLE, \ - CKA_CCM_PRIVATE, CKA_FINGERPRINT_SHA1, CKA_FINGERPRINT_SHA256, CKA_OUID, CKA_UNWRAP_TEMPLATE, \ - CKA_DERIVE_TEMPLATE, \ - CKA_X9_31_GENERATED, CKA_VALUE, CKA_BYTES_REMAINING +from .cryptoki import CK_ATTRIBUTE, CK_BBOOL, CK_ATTRIBUTE_TYPE, CK_ULONG, CK_BYTE, CK_CHAR +from .defines import CKA_EKM_UID, CKA_GENERIC_1, CKA_GENERIC_2, CKA_GENERIC_3 +from .defines import ( + CKA_USAGE_LIMIT, + CKA_USAGE_COUNT, + CKA_CLASS, + CKA_TOKEN, + CKA_PRIVATE, + CKA_LABEL, + CKA_APPLICATION, + CKA_CERTIFICATE_TYPE, + CKA_ISSUER, + CKA_SERIAL_NUMBER, + CKA_KEY_TYPE, + CKA_SUBJECT, + CKA_ID, + CKA_SENSITIVE, + CKA_ENCRYPT, + CKA_DECRYPT, + CKA_WRAP, + CKA_UNWRAP, + CKA_SIGN, + CKA_SIGN_RECOVER, + CKA_VERIFY, + CKA_VERIFY_RECOVER, + CKA_DERIVE, + CKA_START_DATE, + CKA_END_DATE, + CKA_MODULUS, + CKA_MODULUS_BITS, + CKA_PUBLIC_EXPONENT, + CKA_PRIVATE_EXPONENT, + CKA_PRIME_1, + CKA_PRIME_2, + CKA_EXPONENT_1, + CKA_EXPONENT_2, + CKA_COEFFICIENT, + CKA_PRIME, + CKA_SUBPRIME, + CKA_BASE, + CKA_PRIME_BITS, + CKA_SUBPRIME_BITS, + CKA_VALUE_BITS, + CKA_VALUE_LEN, + CKA_LOCAL, + CKA_MODIFIABLE, + CKA_EXTRACTABLE, + CKA_ALWAYS_SENSITIVE, + CKA_NEVER_EXTRACTABLE, + CKA_CCM_PRIVATE, + CKA_FINGERPRINT_SHA1, + CKA_FINGERPRINT_SHA256, + CKA_OUID, + CKA_UNWRAP_TEMPLATE, + CKA_DERIVE_TEMPLATE, + CKA_X9_31_GENERATED, + CKA_VALUE, + CKA_BYTES_REMAINING, +) LOG = logging.getLogger(__name__) @@ -118,9 +171,12 @@ def to_char_array(val, reverse=False): :class:`ctypes.c_ulong` size of array) """ if reverse: - LOG.debug("Attempting to convert CK_ATTRIBUTE(len:%s, data:%s, type:%s) " - "back to ascii string", - val.usValueLen, val.pValue, val.type) + LOG.debug( + "Attempting to convert CK_ATTRIBUTE(len:%s, data:%s, type:%s) back to ascii string", + val.usValueLen, + val.pValue, + val.type, + ) data = cast(val.pValue, POINTER(CK_CHAR)) ret_data = string_at(data, val.usValueLen) @@ -158,7 +214,7 @@ def to_ck_date(val, reverse=False): return string_at(cast(val.pValue, POINTER(c_char)), val.usValueLen) if isinstance(val, dict): - val = datetime.date(year=val['year'], month=val['month'], day=val['day']) + val = datetime.date(year=val["year"], month=val["month"], day=val["day"]) if isinstance(val, string_types): if len(val) != 8: @@ -191,9 +247,13 @@ def to_byte_array(val, reverse=False): :class:`ctypes.c_ulong` size of array) """ if reverse: - LOG.debug("Attempting to convert CK_ATTRIBUTE(len:%s, data:%s, type:%s) back to hex", - val.usValueLen, val.pValue, val.type) - data_list = list(cast(val.pValue, POINTER(c_ubyte))[0:val.usValueLen]) + LOG.debug( + "Attempting to convert CK_ATTRIBUTE(len:%s, data:%s, type:%s) back to hex", + val.usValueLen, + val.pValue, + val.type, + ) + data_list = list(cast(val.pValue, POINTER(c_ubyte))[0 : val.usValueLen]) fin = binascii.hexlify(bytearray(data_list)) LOG.debug("Final hex data: %s", fin) return fin @@ -226,7 +286,7 @@ def to_byte_array(val, reverse=False): fmt = "{:0%sb}" % width str_val = fmt.format(val) n = 8 - str_array = [str_val[i:i + n] for i in range(0, len(str_val), n)] + str_array = [str_val[i : i + n] for i in range(0, len(str_val), n)] byte_array = (CK_BYTE * len(str_array))(*[int(x, 2) for x in str_array]) return cast(pointer(byte_array), c_void_p), CK_ULONG(sizeof(byte_array)) @@ -254,84 +314,77 @@ def to_sub_attributes(val, reverse=False): # Default any unset transform to :func:`to_byte_array` KEY_TRANSFORMS = defaultdict(lambda: to_byte_array) -KEY_TRANSFORMS.update({ - # int, long - CKA_CLASS: to_long, - CKA_CERTIFICATE_TYPE: to_long, - CKA_KEY_TYPE: to_long, - CKA_VALUE_LEN: to_long, - CKA_MODULUS_BITS: to_long, - CKA_PRIME_BITS: to_long, - CKA_SUBPRIME_BITS: to_long, - CKA_VALUE_BITS: to_long, - CKA_USAGE_COUNT: to_long, - CKA_USAGE_LIMIT: to_long, - CKA_BYTES_REMAINING: to_long, - - # int, bool - CKA_TOKEN: to_bool, - CKA_PRIVATE: to_bool, - CKA_SENSITIVE: to_bool, - CKA_ENCRYPT: to_bool, - CKA_DECRYPT: to_bool, - CKA_WRAP: to_bool, - CKA_UNWRAP: to_bool, - CKA_SIGN: to_bool, - CKA_SIGN_RECOVER: to_bool, - CKA_VERIFY: to_bool, - CKA_VERIFY_RECOVER: to_bool, - CKA_DERIVE: to_bool, - CKA_CCM_PRIVATE: to_bool, - CKA_LOCAL: to_bool, - CKA_MODIFIABLE: to_bool, - CKA_EXTRACTABLE: to_bool, - CKA_ALWAYS_SENSITIVE: to_bool, - CKA_NEVER_EXTRACTABLE: to_bool, - CKA_X9_31_GENERATED: to_bool, - - # str, list(?) - CKA_LABEL: to_char_array, - CKA_APPLICATION: to_char_array, - CKA_ISSUER: to_char_array, - CKA_SUBJECT: to_char_array, - CKA_ID: to_char_array, - CKA_EKM_UID: to_char_array, - CKA_GENERIC_1: to_char_array, - CKA_GENERIC_2: to_char_array, - CKA_GENERIC_3: to_char_array, - - # str, dict, datetime - CKA_START_DATE: to_ck_date, - CKA_END_DATE: to_ck_date, - - # Generic data. - CKA_VALUE: to_byte_array, - CKA_SERIAL_NUMBER: to_byte_array, - CKA_MODULUS: to_byte_array, - CKA_PUBLIC_EXPONENT: to_byte_array, - CKA_PRIVATE_EXPONENT: to_byte_array, - CKA_PRIME_1: to_byte_array, - CKA_PRIME_2: to_byte_array, - CKA_EXPONENT_1: to_byte_array, - CKA_EXPONENT_2: to_byte_array, - CKA_COEFFICIENT: to_byte_array, - CKA_PRIME: to_byte_array, - CKA_SUBPRIME: to_byte_array, - CKA_BASE: to_byte_array, - CKA_FINGERPRINT_SHA1: to_byte_array, - CKA_FINGERPRINT_SHA256: to_byte_array, - CKA_OUID: to_byte_array, - - # Dict - CKA_UNWRAP_TEMPLATE: to_sub_attributes, - CKA_DERIVE_TEMPLATE: to_sub_attributes, -}) - -CONVERSIONS = {CK_ULONG: to_long, - CK_BBOOL: to_bool, - c_char: to_char_array, - CK_BYTE: to_byte_array - } +KEY_TRANSFORMS.update( + { + # int, long + CKA_CLASS: to_long, + CKA_CERTIFICATE_TYPE: to_long, + CKA_KEY_TYPE: to_long, + CKA_VALUE_LEN: to_long, + CKA_MODULUS_BITS: to_long, + CKA_PRIME_BITS: to_long, + CKA_SUBPRIME_BITS: to_long, + CKA_VALUE_BITS: to_long, + CKA_USAGE_COUNT: to_long, + CKA_USAGE_LIMIT: to_long, + CKA_BYTES_REMAINING: to_long, + # int, bool + CKA_TOKEN: to_bool, + CKA_PRIVATE: to_bool, + CKA_SENSITIVE: to_bool, + CKA_ENCRYPT: to_bool, + CKA_DECRYPT: to_bool, + CKA_WRAP: to_bool, + CKA_UNWRAP: to_bool, + CKA_SIGN: to_bool, + CKA_SIGN_RECOVER: to_bool, + CKA_VERIFY: to_bool, + CKA_VERIFY_RECOVER: to_bool, + CKA_DERIVE: to_bool, + CKA_CCM_PRIVATE: to_bool, + CKA_LOCAL: to_bool, + CKA_MODIFIABLE: to_bool, + CKA_EXTRACTABLE: to_bool, + CKA_ALWAYS_SENSITIVE: to_bool, + CKA_NEVER_EXTRACTABLE: to_bool, + CKA_X9_31_GENERATED: to_bool, + # str, list(?) + CKA_LABEL: to_char_array, + CKA_APPLICATION: to_char_array, + CKA_ISSUER: to_char_array, + CKA_SUBJECT: to_char_array, + CKA_ID: to_char_array, + CKA_EKM_UID: to_char_array, + CKA_GENERIC_1: to_char_array, + CKA_GENERIC_2: to_char_array, + CKA_GENERIC_3: to_char_array, + # str, dict, datetime + CKA_START_DATE: to_ck_date, + CKA_END_DATE: to_ck_date, + # Generic data. + CKA_VALUE: to_byte_array, + CKA_SERIAL_NUMBER: to_byte_array, + CKA_MODULUS: to_byte_array, + CKA_PUBLIC_EXPONENT: to_byte_array, + CKA_PRIVATE_EXPONENT: to_byte_array, + CKA_PRIME_1: to_byte_array, + CKA_PRIME_2: to_byte_array, + CKA_EXPONENT_1: to_byte_array, + CKA_EXPONENT_2: to_byte_array, + CKA_COEFFICIENT: to_byte_array, + CKA_PRIME: to_byte_array, + CKA_SUBPRIME: to_byte_array, + CKA_BASE: to_byte_array, + CKA_FINGERPRINT_SHA1: to_byte_array, + CKA_FINGERPRINT_SHA256: to_byte_array, + CKA_OUID: to_byte_array, + # Dict + CKA_UNWRAP_TEMPLATE: to_sub_attributes, + CKA_DERIVE_TEMPLATE: to_sub_attributes, + } +) + +CONVERSIONS = {CK_ULONG: to_long, CK_BBOOL: to_bool, c_char: to_char_array, CK_BYTE: to_byte_array} class Attributes(dict): @@ -373,8 +426,8 @@ def __init__(self, *args, **kwargs): args = [] if kwargs is None: kwargs = {} - if 'new_transforms' in kwargs: - self.new_transforms = kwargs.pop('new_transforms') + if "new_transforms" in kwargs: + self.new_transforms = kwargs.pop("new_transforms") else: self.new_transforms = {} super(Attributes, self).__init__(*args, **kwargs) @@ -396,17 +449,16 @@ def get_c_struct(self): ret_struct[index] = blank_attr elif key in self.new_transforms: p_value, ul_length = self.new_transforms[key](value) - ret_struct[index] = CK_ATTRIBUTE(CK_ATTRIBUTE_TYPE(key), - p_value, - ul_length) + ret_struct[index] = CK_ATTRIBUTE(CK_ATTRIBUTE_TYPE(key), p_value, ul_length) else: if key not in KEY_TRANSFORMS: - LOG.warning("Using default `to_byte_array` transformation for key %s " - "and data %s", key, value) + LOG.warning( + "Using default `to_byte_array` transformation for key %s and data %s", + key, + value, + ) p_value, ul_length = KEY_TRANSFORMS[key](value) - ret_struct[index] = CK_ATTRIBUTE(CK_ATTRIBUTE_TYPE(key), - p_value, - ul_length) + ret_struct[index] = CK_ATTRIBUTE(CK_ATTRIBUTE_TYPE(key), p_value, ul_length) return ret_struct @staticmethod diff --git a/pycryptoki/ca_extensions/derive_wrap.py b/pycryptoki/ca_extensions/derive_wrap.py new file mode 100644 index 0000000..8f9ec78 --- /dev/null +++ b/pycryptoki/ca_extensions/derive_wrap.py @@ -0,0 +1,56 @@ +""" +derive and wrap extended method +""" +import logging +from ctypes import c_ubyte, string_at + +from pycryptoki.defines import CKR_OK +from pycryptoki.common_utils import AutoCArray +from pycryptoki.attributes import Attributes +from pycryptoki.cryptoki import CA_DeriveKeyAndWrap, CK_OBJECT_HANDLE, CK_ULONG +from pycryptoki.mechanism import parse_mechanism +from pycryptoki.exceptions import make_error_handle_function + +LOG = logging.getLogger(__name__) + + +def ca_derive_key_and_wrap(h_session, derive_mechanism, h_base_key, derive_template, + wrapping_key, wrap_mechanism, output_buffer=2048): + """ + Derive a key from the base key and wrap it off the HSM using the wrapping key + + :param int h_session: The session to use + :param int h_base_key: The base key + :param dict derive_template: A python template of attributes to set on derived key + :param derive_mechanism: See the :py:func:`~pycryptoki.mechanism.parse_mechanism` function + for possible values. + :param int wrapping_key: The wrapping key based on the encryption flavor + :param wrap_mechanism: See the :py:func:`~pycryptoki.mechanism.parse_mechanism` function + for possible values. + :param output_buffer: The size of the wrapped key, defaulted to a cert size + :returns: (Retcode, python bytestring representing wrapped key) + :rtype: tuple + """ + # derive key parameters preparation + derive_mech = parse_mechanism(derive_mechanism) + c_template = Attributes(derive_template).get_c_struct() + # wrapping key parameter preparation + wrap_mech = parse_mechanism(wrap_mechanism) + # derive key and wrap function requires the size and in that way is + # inconsistent with wrap function + size = CK_ULONG(output_buffer) + wrapped_key = AutoCArray(ctype=c_ubyte, + size=size) + + ret = CA_DeriveKeyAndWrap(h_session, derive_mech, CK_OBJECT_HANDLE(h_base_key), + c_template, CK_ULONG(len(derive_template)), + wrap_mech, CK_OBJECT_HANDLE(wrapping_key), + wrapped_key.array, wrapped_key.size) + + if ret != CKR_OK: + return ret, None + + return ret, string_at(wrapped_key.array, wrapped_key.size.contents.value) + + +ca_derive_key_and_wrap_ex = make_error_handle_function(ca_derive_key_and_wrap) diff --git a/pycryptoki/ca_extensions/per_key_auth.py b/pycryptoki/ca_extensions/per_key_auth.py new file mode 100644 index 0000000..453db7a --- /dev/null +++ b/pycryptoki/ca_extensions/per_key_auth.py @@ -0,0 +1,53 @@ +""" +Module to work with PKA / Per key authorization +""" +from ctypes import cast +from _ctypes import POINTER +from pycryptoki.attributes import to_byte_array +from pycryptoki.cryptoki import (CA_SetAuthorizationData, + CA_AuthorizeKey) +from pycryptoki.cryptoki import (CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_UTF8CHAR) +from pycryptoki.exceptions import make_error_handle_function + +def ca_set_authorization_data(h_session, h_object, old_auth_data, new_auth_data): + """ + User changes authorization data on key object (private, secret) + + :param h_session: session handle + :param object: key handle to update + :param old_auth_data: byte list, e.g. [11, 12, 13, ..] + :param new_auth_data: byte list, e.g. [11, 12, 13, ..] + :return: Ret code + """ + old_auth_data_ptr, old_auth_data_length = to_byte_array(old_auth_data) + old_auth_data_ptr = cast(old_auth_data_ptr, POINTER(CK_UTF8CHAR)) + + new_auth_data_ptr, new_auth_data_length = to_byte_array(new_auth_data) + new_auth_data_ptr = cast(new_auth_data_ptr, POINTER(CK_UTF8CHAR)) + + h_object = CK_OBJECT_HANDLE(h_object) + h_session = CK_SESSION_HANDLE(h_session) + + return CA_SetAuthorizationData(h_session, h_object, old_auth_data_ptr, old_auth_data_length, + new_auth_data_ptr, new_auth_data_length) + +ca_set_authorization_data_ex = make_error_handle_function(ca_set_authorization_data) + +def ca_authorize_key(h_session, h_object, auth_data): + """ + User authorizes key within session or access for use + + :param h_session: session handle + :param object: key handle to authorize + :param auth_data: authorization byte list, e.g. [11, 12, 13, ..] + :return: Ret code + """ + auth_data_ptr, auth_data_length = to_byte_array(auth_data) + auth_data_ptr = cast(auth_data_ptr, POINTER(CK_UTF8CHAR)) + + h_object = CK_OBJECT_HANDLE(h_object) + h_session = CK_SESSION_HANDLE(h_session) + + return CA_AuthorizeKey(h_session, h_object, auth_data_ptr, auth_data_length) + +ca_authorize_key_ex = make_error_handle_function(ca_authorize_key) diff --git a/pycryptoki/cryptoki.py b/pycryptoki/cryptoki.py index 33981e5..add4a50 100755 --- a/pycryptoki/cryptoki.py +++ b/pycryptoki/cryptoki.py @@ -671,7 +671,8 @@ class CK_AES_GCM_PARAMS(Structure): ('ulTagBits', CK_ULONG), ] CK_AES_GCM_PARAMS_PTR = CK_AES_GCM_PARAMS - +CK_UTF8CHAR = CK_BYTE +CK_UTF8CHAR_PTR = POINTER(CK_UTF8CHAR) class CK_XOR_BASE_DATA_KDF_PARAMS(Structure): pass @@ -1054,6 +1055,11 @@ class CK_SFNT_CA_FUNCTION_LIST(Structure): CK_CA_ReadUtilizationMetrics = CFUNCTYPE(CK_RV, CK_SESSION_HANDLE) CK_CA_ReadAndResetUtilizationMetrics = CFUNCTYPE(CK_RV, CK_SESSION_HANDLE) CK_CA_ReadAllUtilizationCounters = CFUNCTYPE(CK_RV, CK_SESSION_HANDLE, CK_UTILIZATION_COUNTER_PTR, CK_ULONG_PTR) +#pka +CK_CA_SetAuthorizationData = CFUNCTYPE(CK_RV, CK_SESSION_HANDLE, CK_OBJECT_HANDLE, + CK_UTF8CHAR_PTR, CK_ULONG, CK_UTF8CHAR_PTR, CK_ULONG) +CK_CA_AuthorizeKey = CFUNCTYPE(CK_RV, CK_SESSION_HANDLE, CK_OBJECT_HANDLE, + CK_UTF8CHAR_PTR, CK_ULONG) CK_CA_ManualKCV = CFUNCTYPE(CK_RV, CK_SESSION_HANDLE) CK_CA_SetLKCV = CFUNCTYPE(CK_RV, CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) CK_CA_SetKCV = CFUNCTYPE(CK_RV, CK_SESSION_HANDLE, CK_BYTE_PTR, CK_ULONG) @@ -1387,6 +1393,8 @@ class CK_SFNT_CA_FUNCTION_LIST(Structure): ('CA_DescribeUtilizationBinId', CK_CA_DescribeUtilizationBinId), ('CA_ReadAndResetUtilizationMetrics', CK_CA_ReadAndResetUtilizationMetrics), ('CA_ReadAllUtilizationCounters', CK_CA_ReadAllUtilizationCounters), + ('CA_SetAuthorizationData', CK_CA_SetAuthorizationData), + ('CA_AuthorizeKey', CK_CA_AuthorizeKey), ('CA_ManualKCV', CK_CA_ManualKCV), ('CA_SetLKCV', CK_CA_SetLKCV), ('CA_SetKCV', CK_CA_SetKCV), @@ -1699,6 +1707,14 @@ class CK_SFNT_CA_FUNCTION_LIST(Structure): CA_ReadAllUtilizationCounters = make_late_binding_function('CA_ReadAllUtilizationCounters') CA_ReadAllUtilizationCounters.restype = CK_RV CA_ReadAllUtilizationCounters.argtypes = [CK_SESSION_HANDLE, CK_UTILIZATION_COUNTER_PTR, CK_ULONG_PTR] +#pka +CA_SetAuthorizationData = make_late_binding_function('CA_SetAuthorizationData') +CA_SetAuthorizationData.restype = CK_RV +CA_SetAuthorizationData.argtypes = [CK_SESSION_HANDLE, CK_OBJECT_HANDLE, + CK_UTF8CHAR_PTR, CK_ULONG, CK_UTF8CHAR_PTR, CK_ULONG] +CA_AuthorizeKey = make_late_binding_function('CA_AuthorizeKey') +CA_AuthorizeKey.restype = CK_RV +CA_AuthorizeKey.argtypes = [CK_SESSION_HANDLE, CK_OBJECT_HANDLE, CK_UTF8CHAR_PTR, CK_ULONG] CA_ManualKCV = make_late_binding_function('CA_ManualKCV') CA_ManualKCV.restype = CK_RV CA_ManualKCV.argtypes = [CK_SESSION_HANDLE] @@ -2491,8 +2507,6 @@ class CK_MECHANISM_INFO(Structure): CK_MECHANISM_INFO_PTR = POINTER(CK_MECHANISM_INFO) CK_C_GetMechanismInfo = CFUNCTYPE(CK_RV, CK_SLOT_ID, CK_MECHANISM_TYPE, CK_MECHANISM_INFO_PTR) -CK_UTF8CHAR = CK_BYTE -CK_UTF8CHAR_PTR = POINTER(CK_UTF8CHAR) CK_C_InitToken = CFUNCTYPE(CK_RV, CK_SLOT_ID, CK_UTF8CHAR_PTR, CK_ULONG, CK_UTF8CHAR_PTR) CK_C_InitPIN = CFUNCTYPE(CK_RV, CK_SESSION_HANDLE, CK_UTF8CHAR_PTR, CK_ULONG) CK_C_SetPIN = CFUNCTYPE(CK_RV, CK_SESSION_HANDLE, CK_UTF8CHAR_PTR, CK_ULONG, CK_UTF8CHAR_PTR, diff --git a/pycryptoki/daemon/rpyc_pycryptoki.py b/pycryptoki/daemon/rpyc_pycryptoki.py index fa2e505..21d1860 100755 --- a/pycryptoki/daemon/rpyc_pycryptoki.py +++ b/pycryptoki/daemon/rpyc_pycryptoki.py @@ -41,6 +41,7 @@ ca_insert, ca_insert_ex, ca_sim_insert, ca_sim_insert_ex, ca_sim_extract_ex, ca_sim_extract, ca_sim_multisign, ca_sim_multisign_ex) +from pycryptoki.ca_extensions.derive_wrap import ca_derive_key_and_wrap, ca_derive_key_and_wrap_ex from pycryptoki.ca_extensions.session import ca_get_session_info, ca_get_session_info_ex from pycryptoki.ca_extensions.object_handler import ca_destroy_multiple_objects, \ ca_destroy_multiple_objects_ex, ca_get_object_handle, ca_get_object_handle_ex @@ -75,6 +76,10 @@ ca_read_utilization_metrics_ex, ca_read_and_reset_utilization_metrics, ca_read_and_reset_utilization_metrics_ex) +from pycryptoki.ca_extensions.per_key_auth import (ca_set_authorization_data, + ca_set_authorization_data_ex, + ca_authorize_key, + ca_authorize_key_ex) from pycryptoki.key_generator import (c_destroy_object, c_destroy_object_ex, c_generate_key_pair, c_generate_key_pair_ex, c_generate_key, c_generate_key_ex, @@ -400,6 +405,8 @@ def _rpyc_getattr(self, name): exposed_ca_get_object_handle_ex = staticmethod(ca_get_object_handle_ex) exposed_ca_get_session_info = staticmethod(ca_get_session_info) exposed_ca_get_session_info_ex = staticmethod(ca_get_session_info_ex) + exposed_ca_derive_key_and_wrap = staticmethod(ca_derive_key_and_wrap) + exposed_ca_derive_key_and_wrap_ex = staticmethod(ca_derive_key_and_wrap_ex) exposed_ca_read_all_utilization_counters = staticmethod(ca_read_all_utilization_counters) exposed_ca_read_all_utilization_counters_ex = staticmethod(ca_read_all_utilization_counters_ex) @@ -410,6 +417,11 @@ def _rpyc_getattr(self, name): exposed_ca_read_and_reset_utilization_metrics_ex =\ staticmethod(ca_read_and_reset_utilization_metrics_ex) + exposed_ca_set_authorization_data = staticmethod(ca_set_authorization_data) + exposed_ca_set_authorization_data_ex = staticmethod(ca_set_authorization_data_ex) + exposed_ca_authorize_key = staticmethod(ca_authorize_key) + exposed_ca_authorize_key_ex = staticmethod(ca_authorize_key_ex) + def server_launch(service, ip, port, config): """ Target for the multiprocessing Pycryptoki service. diff --git a/pycryptoki/defines.py b/pycryptoki/defines.py index e74dbc8..c481dcb 100755 --- a/pycryptoki/defines.py +++ b/pycryptoki/defines.py @@ -664,6 +664,8 @@ CONTAINER_CONFIG_SECURE_TRUSTED_CHANNEL = 37 CONTAINER_CONFIG_FAST_PATH = 38 CONTAINER_CONFIG_ENFORCE_START_AND_END_ATTRIBUTES = 39 +CONTAINER_CONFIG_PER_KEY_AUTH = 40 +CONTAINER_CONFIG_LEGACY_PARTITION = 41 HSM_CONFIG_ENABLE_PIN_AUTHENTICATION = 0 HSM_CONFIG_ENABLE_PED_AUTHENTICATION = 1 HSM_CONFIG_PERFORMANCE_LEVEL = 2 @@ -1111,6 +1113,10 @@ CKA_SUPPORTED_CMS_ATTRIBUTES = 0x00000503 CKA_ALLOWED_MECHANISMS = (CKF_ARRAY_ATTRIBUTE | 0x00000600) CKA_VENDOR_DEFINED = 0x80000000 +CKA_AUTH_DATA = CKA_VENDOR_DEFINED + 0x1005 +CKA_ASSIGNED = CKA_VENDOR_DEFINED + 0x1006 +CKA_KEY_STATUS = CKA_VENDOR_DEFINED + 0x1007 +CKA_FAILED_KEY_AUTH_COUNT = CKA_VENDOR_DEFINED + 0x1008 CKM_RSA_PKCS_KEY_PAIR_GEN = 0x00000000 CKM_RSA_PKCS = 0x00000001 CKM_RSA_9796 = 0x00000002 @@ -1635,6 +1641,7 @@ CKH_TEMPERATURE = (CKH_VENDOR_DEFINED | 0x00000001) CKH_BATTERY = (CKH_VENDOR_DEFINED | 0x00000002) CKH_FAN = (CKH_VENDOR_DEFINED | 0x00000003) +CKM_UNSUPPORTED = 0xffffffff CKM_VENDOR_DEFINED_OLD_XXX = 0x00008000 CKM_CAST_KEY_GEN_OLD_XXX = CKM_VENDOR_DEFINED_OLD_XXX + 0 CKM_CAST_ECB_OLD_XXX = CKM_VENDOR_DEFINED_OLD_XXX + 1 @@ -1911,6 +1918,16 @@ CKR_RNG_RESEED_TOO_EARLY = (CKR_VENDOR_DEFINED + 0X75) CKR_HSM_TAMPERED = (CKR_VENDOR_DEFINED + 0X76) CKR_INVALID_UTILIZATION_METRICS = (CKR_VENDOR_DEFINED + 0X7F) +CKR_ASSIGNED_KEY_REQUIRES_AUTH_DATA = (CKR_VENDOR_DEFINED + 0x8F) +CKR_ROLE_CANNOT_MAKE_KEYS_ASSIGNED = (CKR_VENDOR_DEFINED + 0x90) +CKR_INVALID_ASSIGNED_ATTRIBUTE_TRANSITION = (CKR_VENDOR_DEFINED + 0x91) +CKR_AUTH_DATA_TOO_LARGE = (CKR_VENDOR_DEFINED + 0x92) +CKR_AUTH_DATA_TOO_SMALL = (CKR_VENDOR_DEFINED + 0x93) +CKR_OH_AUTH_DATA_NOT_PROVIDED = (CKR_VENDOR_DEFINED + 0x94) +CKR_ASSIGNED_KEY_FAILED_ATTRIBUTE_DEPENDENCIES = (CKR_VENDOR_DEFINED + 0x95) +CKR_KEY_CANNOT_BE_AUTHORIZED = (CKR_VENDOR_DEFINED + 0x96) +CKR_KEY_NOT_AUTHORIZED = (CKR_VENDOR_DEFINED + 0x97) +CKR_AUTH_DATA_INCORRECT = (CKR_VENDOR_DEFINED + 0x98) CKR_OBJECT_READ_ONLY = (CKR_VENDOR_DEFINED + 0x114) CKR_KEY_NOT_ACTIVE = (CKR_VENDOR_DEFINED + 0x136) CKO_TOKEN_ROLE_POLICY_SET = (CKO_VENDOR_DEFINED + 0x0001) diff --git a/requirements.txt b/requirements.txt index 042cca4..c6f74c2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ rpyc==3.4.4 -hypothesis -pytest +hypothesis==4.6 +pytest==3.10 future mock six diff --git a/setup.py b/setup.py index 21fa8e7..2dbaa14 100755 --- a/setup.py +++ b/setup.py @@ -18,6 +18,6 @@ 'pycryptoki.mechanism', 'pycryptoki.ca_extensions'], scripts=['pycryptoki/daemon/rpyc_pycryptoki.py'], - tests_require=['pytest', 'hypothesis', 'mock', 'pytz'], + tests_require=['pytest==3.10.1', 'hypothesis==4.6.1', 'mock', 'pytz'], install_requires=['future', 'rpyc==3.4.4', 'six'] ) diff --git a/test_requirements.txt b/test_requirements.txt index ca66ecb..18ba8b2 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -1,4 +1,4 @@ pytz mock -pytest -hypothesis +pytest==3.10.1 +hypothesis==4.6.1 diff --git a/tests/functional/test_wrap_unwrap.py b/tests/functional/test_wrap_unwrap.py index 730a2c8..29de9c9 100755 --- a/tests/functional/test_wrap_unwrap.py +++ b/tests/functional/test_wrap_unwrap.py @@ -2,11 +2,13 @@ Testcases for wrapping/unwrapping keys. """ import logging +from distutils.version import LooseVersion import pytest +from pycryptoki.ca_extensions.derive_wrap import ca_derive_key_and_wrap_ex from pycryptoki.default_templates import MECHANISM_LOOKUP_EXT as LOOKUP -from pycryptoki.default_templates import get_default_key_template +from pycryptoki.default_templates import get_default_key_template, CKM_SHA256_KEY_DERIVATION from pycryptoki.defines import (CKM_DES_ECB, CKM_DES_CBC, CKM_DES_CBC_PAD, CKM_DES_KEY_GEN, CKM_DES3_ECB, CKM_DES3_CBC, CKM_DES3_CBC_PAD, CKM_DES3_KEY_GEN, CKM_AES_ECB, CKM_AES_CBC, CKM_AES_CBC_PAD, CKM_AES_KEY_GEN, @@ -14,12 +16,13 @@ CKM_CAST5_ECB, CKM_CAST5_CBC, CKM_CAST5_CBC_PAD, CKM_CAST5_KEY_GEN, CKM_SEED_ECB, CKM_SEED_CBC, CKM_SEED_KEY_GEN, - CKR_OK, CKA_DECRYPT, CKA_VERIFY, CKA_UNWRAP, + CKR_OK, CKA_DECRYPT, CKA_VERIFY, CKA_UNWRAP, CKM_AES_KWP, CKA_VALUE_LEN, CKA_EXTRACTABLE) from pycryptoki.encryption import c_wrap_key, c_unwrap_key, c_encrypt, c_decrypt -from pycryptoki.key_generator import c_destroy_object, c_generate_key +from pycryptoki.key_generator import c_destroy_object, c_generate_key, c_generate_key_ex from pycryptoki.lookup_dicts import ret_vals_dictionary from pycryptoki.test_functions import verify_object_attributes +from . import config as hsm_config logger = logging.getLogger(__name__) @@ -174,6 +177,24 @@ def test_wrap_unwrap_key(self, mech, k_type, keys): if h_unwrapped_key: c_destroy_object(self.h_session, h_unwrapped_key) + @pytest.mark.xfail(LooseVersion(hsm_config.get('firmware', "6.2.1")) < LooseVersion("7"), + reason="Mechanism not supported on SA6") + @pytest.mark.parametrize('params', [{'iv': []}, {'iv': list(range(4))}]) + def test_derive_key_and_wrap(self, params): + """ + Tests CA_DeriveKeyAndWrap function + + :param params:valid AES_KWP wrap mechanism + """ + key_template = get_default_key_template(CKM_AES_KEY_GEN) + h_base_key = c_generate_key_ex(self.h_session, CKM_AES_KEY_GEN, key_template) + derived_key_template = key_template.copy() + h_wrapping_key = c_generate_key_ex(self.h_session, CKM_AES_KEY_GEN, key_template) + wrap_mech = {"mech_type": CKM_AES_KWP, "params": params} + wrapped_key = ca_derive_key_and_wrap_ex(self.h_session, CKM_SHA256_KEY_DERIVATION, h_base_key, + derived_key_template, h_wrapping_key, wrap_mech) + assert wrapped_key, "CA_DeriveKeyAndWrap returned an empty buffer" + @pytest.mark.parametrize(('mech', 'k_type'), PARAM_LIST, ids=[LOOKUP[m][0] for m, _ in PARAM_LIST]) def test_encrypt_wrap_unwrap_decrypt_key(self, mech, k_type, keys): diff --git a/tests/unittests/test_attr_conversions.py b/tests/unittests/test_attr_conversions.py index 2149407..751de57 100644 --- a/tests/unittests/test_attr_conversions.py +++ b/tests/unittests/test_attr_conversions.py @@ -2,17 +2,17 @@ Unit tests for python/c type conversions """ import logging -from _ctypes import POINTER +from ctypes import POINTER from binascii import hexlify from collections import defaultdict from ctypes import cast, c_void_p, c_ulong, sizeof +from datetime import date from string import ascii_letters as letters import mock import pytest from hypothesis import given -from hypothesis.extra.datetime import dates -from hypothesis.strategies import integers, floats, text, booleans, lists, dictionaries, one_of +from hypothesis.strategies import integers, dates, floats, text, booleans, lists, dictionaries, one_of from six import b, integer_types from pycryptoki.attributes import (CK_ATTRIBUTE, @@ -170,7 +170,7 @@ def test_to_char_array_fail_obj(self): """ self.force_fail(object(), to_char_array, TypeError) - @given(dates(min_year=1900)) + @given(dates(min_value=date(1900, 1, 1))) def test_to_ck_date_string(self, date_val): """ to_ck_date() with param: @@ -183,7 +183,7 @@ def test_to_ck_date_string(self, date_val): py_date = self.reverse_case(pointer, leng, to_ck_date) assert b(date_string) == py_date - @given(dates(min_year=1900)) + @given(dates(min_value=date(1900, 1, 1))) def test_to_ck_date_dict(self, date_val): """ to_ck_date() with param: @@ -196,7 +196,7 @@ def test_to_ck_date_dict(self, date_val): py_date = self.reverse_case(pointer, leng, to_ck_date) assert b(date_val.strftime("%Y%m%d")) == py_date - @given(dates(min_year=1900)) + @given(dates(min_value=date(1900, 1, 1))) def test_to_ck_date(self, date_val): """ to_ck_date() with param: diff --git a/tox.ini b/tox.ini index 2dea4be..f2a1d80 100644 --- a/tox.ini +++ b/tox.ini @@ -1,15 +1,15 @@ [tox] -envlist=py27,py35,pypy +envlist=py27,py35,pypy,py36,py37 [testenv] -deps=pytest +deps=pytest==3.10.1 six rpyc - hypothesis + hypothesis==4.6.1 mock pytz future pytest-cov -commands=py.test \ +commands=pytest \ tests/unittests \ --junitxml=junit-{envname}.xml \ --showlocals \