def c_verify(h_session, h_key, data_to_verify, signature, mechanism): """Verifies data with the given signature, key and mechanism. .. note:: If data is a list or tuple of strings, multi-part operations will be used. :param int h_session: Session handle :param data_to_verify: The data to sign, either a string or a list of strings. If this is a list a multipart operation will be used (using C_...Update and C_...Final) ex: - "This is a proper argument of some data to use in the function" - ["This is another format of data this", "function will accept.", "It will operate on these strings in parts"] :param bytes signature: Signature with which to verify the data. :param int h_key: The verifying key :param mechanism: See the :py:func:`~pycryptoki.mechanism.parse_mechanism` function for possible values. :return: retcode of verify operation """ mech = parse_mechanism(mechanism) # Initialize the verify operation ret = C_VerifyInit(h_session, mech, CK_ULONG(h_key)) if ret != CKR_OK: return ret # if a list is passed out do a verify operation on each string in the list, # otherwise just do one verify operation is_multi_part_operation = isinstance(data_to_verify, list) or isinstance( data_to_verify, tuple) if is_multi_part_operation: ret = do_multipart_verify(h_session, data_to_verify, signature) else: # Prepare the data to verify c_data_to_verify, plain_data_len = to_byte_array( from_bytestring(data_to_verify)) c_data_to_verify = cast(c_data_to_verify, POINTER(c_ubyte)) c_signature, c_sig_length = to_byte_array(from_bytestring(signature)) c_signature = cast(c_signature, POINTER(c_ubyte)) # Actually verify the data ret = C_Verify(h_session, c_data_to_verify, plain_data_len, c_signature, c_sig_length) return ret
def to_byte_array(val, reverse=False): """Converts an arbitrarily sized integer, list, or byte array into a byte array. It'll zero-pad the bit length so it's a multiple of 8, then convert the int to binary, split the binary string into sections of 8, then place each section into a slot in a :class:`ctypes.c_ubyte` array (converting to small int). :param val: Value to convert :param reverse: Whether to convert from C -> Python :return: (:class:`ctypes.c_void_p` ptr to :class:`pycryptoki.cryptoki.CK_BYTE` array, :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]) fin = binascii.hexlify(bytearray(data_list)) LOG.debug("Final hex data: %s", fin) return fin if not isinstance(val, (binary_type, collections.Iterable, integer_types)): raise TypeError("Unknown conversion to byte array for type {}".format( type(val))) if isinstance(val, binary_type): # Hex-string in form '0xdeadbeef'' if val.startswith(b"0x"): val = val.replace(b"0x", b"", 1) # Raw byte data: '\xde\xad\xbe\xef" if "\\x" in repr(val): val = list(from_bytestring(val)) byte_array = (CK_BYTE * len(val))(*val) # Hex string: '01af' else: val = int(val, 16) elif isinstance(val, collections.Iterable): py_bytes = bytearray(val) byte_array = (CK_BYTE * len(py_bytes))(*py_bytes) if isinstance(val, integer_types): # Explicitly convert to a long. Python doesn't like X.bit_length() where X is an int # and not a variable assigned an int. x = val width = x.bit_length() width += 8 - ((width % 8) or 8) 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)] 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))
def do_multipart_verify(h_session, input_data_list, signature): """ Do a multipart verify operation :param int h_session: Session handle :param input_data_list: list of data to verify with :param signature: signature to verify :return: The result code """ error = None for index, chunk in enumerate(input_data_list): data_chunk, data_chunk_len = to_byte_array(from_bytestring(chunk)) data_chunk = cast(data_chunk, POINTER(c_ubyte)) ret = C_VerifyUpdate(h_session, data_chunk, data_chunk_len) if ret != CKR_OK: error = ret break # An C_VerifyUpdate failed. We should still try to call C_**Final() though to ensure # that the # operation is still finalized, but we'll return the original error code. if error: ret = C_VerifyFinal( h_session, cast(create_string_buffer(b"", MAX_BUFFER), CK_BYTE_PTR), CK_ULONG(MAX_BUFFER)) LOG.debug( "C_VerifyFinal call after a C_VerifyUpdate failure returned:" " %s (%s)", ret_vals_dictionary.get(ret, "Unknown retcode"), str(hex(ret))) return error, None # Finalizing multipart decrypt operation c_sig_data, c_sig_data_len = to_byte_array(from_bytestring(signature)) output = cast(c_sig_data, CK_BYTE_PTR) ret = C_VerifyFinal(h_session, output, c_sig_data_len) return ret
def c_sign(h_session, h_key, data_to_sign, mechanism, output_buffer=None): """Signs the given data with given key and mechanism. .. note:: If data is a list or tuple of strings, multi-part operations will be used. :param int h_session: Session handle :param data_to_sign: The data to sign, either a string or a list of strings. If this is a list a multipart operation will be used (using C_...Update and C_...Final) ex: - "This is a proper argument of some data to use in the function" - ["This is another format of data this", "function will accept.", "It will operate on these strings in parts"] :param int h_key: The signing key :param mechanism: See the :py:func:`~pycryptoki.mechanism.parse_mechanism` function for possible values. :param list|int output_buffer: Integer or list of integers that specify a size of output buffer to use for an operation. By default will query with NULL pointer buffer to get required size of buffer. :return: (retcode, python string of signed data) :rtype: tuple """ mech = parse_mechanism(mechanism) # Initialize the sign operation ret = C_SignInit(h_session, byref(mech), CK_ULONG(h_key)) if ret != CKR_OK: return ret, None # if a list is passed out do a sign operation on each string in the list, # otherwise just do one sign operation is_multi_part_operation = isinstance(data_to_sign, (list, tuple)) if is_multi_part_operation: ret, signature_string = do_multipart_sign_or_digest( h_session, C_SignUpdate, C_SignFinal, data_to_sign, output_buffer=output_buffer) else: # Prepare the data to sign c_data_to_sign, plain_date_len = to_byte_array( from_bytestring(data_to_sign)) c_data_to_sign = cast(c_data_to_sign, POINTER(c_ubyte)) if output_buffer is not None: size = CK_ULONG(output_buffer) signed_data = AutoCArray(ctype=c_ubyte, size=size) ret = C_Sign(h_session, c_data_to_sign, plain_date_len, signed_data.array, signed_data.size) else: signed_data = AutoCArray(ctype=c_ubyte) @refresh_c_arrays(1) def _sign(): """Perform the signing operation""" return C_Sign(h_session, c_data_to_sign, plain_date_len, signed_data.array, signed_data.size) ret = _sign() if ret != CKR_OK: return ret, None signature_string = string_at(signed_data.array, signed_data.size.contents.value) return ret, signature_string
def do_multipart_sign_or_digest(h_session, c_update_function, c_final_function, input_data_list, output_buffer=None): """ Do a multipart sign or digest operation :param int h_session: Session handle :param func c_update_function: signing update function :param func c_final_function: signing finalization function :param iterable input_data_list: Iterable of data to sign. :param int output_buffer: Integer that specifies a size of an output bufffer to use for the Sign/Digeste operation. By default will query with NULL pointer buffer to get required size of buffer :return: The result code, A python string representing the signature """ error = None for index, chunk in enumerate(input_data_list): data_chunk, data_chunk_len = to_byte_array(from_bytestring(chunk)) data_chunk = cast(data_chunk, POINTER(c_ubyte)) ret = c_update_function(h_session, data_chunk, data_chunk_len) if ret != CKR_OK: LOG.debug("%s call on chunk %.20s (%s/%s) Failed w/ ret %s (%s)", c_update_function.__name__, chunk, index + 1, len(input_data_list), ret_vals_dictionary.get(ret, "Unknown retcode"), str(hex(ret))) error = ret break # An Update function failed. We should still try to call C_**Final() though to ensure that the # operation is still finalized, but we'll return the original error code. if error: ret = c_final_function( h_session, cast(create_string_buffer(b'', MAX_BUFFER), CK_BYTE_PTR), CK_ULONG(MAX_BUFFER)) LOG.debug("%s call after a %s failure returned: %s (%s)", c_final_function.__name__, c_update_function.__name__, ret_vals_dictionary.get(ret, "Unknown retcode"), str(hex(ret))) return error, None if output_buffer is not None: size = CK_ULONG(output_buffer) out_data = AutoCArray(ctype=c_ubyte, size=size) ret = c_final_function(h_session, out_data.array, out_data.size) else: out_data = AutoCArray(ctype=c_ubyte) @refresh_c_arrays(1) def _final(): """ Closure to acces AutoCArray properties correctly """ return c_final_function(h_session, out_data.array, out_data.size) ret = _final() if ret != CKR_OK: return ret, None else: python_string = string_at(out_data.array, out_data.size.contents.value) return ret, python_string
def c_digest(h_session, data_to_digest, digest_flavor, mechanism=None, output_buffer=None): """Digests some data :param int h_session: Session handle :param bytes data_to_digest: The data to digest, either a string or a list of strings. If this is a list a multipart operation will be used :param int digest_flavor: The flavour of the mechanism to digest (MD2, SHA-1, HAS-160, SHA224, SHA256, SHA384, SHA512) :param mechanism: See the :py:func:`~pycryptoki.mechanism.parse_mechanism` function for possible values. If None will use digest flavor. :param list|int output_buffer: Integer or list of integers that specify a size of output buffer to use for an operation. By default will query with NULL pointer buffer to get required size of buffer. :returns: (retcode, a python string of the digested data) :rtype: tuple """ if mechanism is None: mech = parse_mechanism(digest_flavor) else: mech = parse_mechanism(mechanism) # Initialize Digestion ret = C_DigestInit(h_session, mech) if ret != CKR_OK: return ret, None # if a list is passed out do an digest operation on each string in the list, otherwise just # do one digest operation is_multi_part_operation = isinstance(data_to_digest, (list, tuple)) if is_multi_part_operation: ret, digested_python_string = do_multipart_sign_or_digest(h_session, C_DigestUpdate, C_DigestFinal, data_to_digest, output_buffer=output_buffer) else: # Get arguments c_data_to_digest, c_digest_data_len = to_byte_array(from_bytestring(data_to_digest)) c_data_to_digest = cast(c_data_to_digest, POINTER(c_ubyte)) if output_buffer is not None: size = CK_ULONG(output_buffer) digested_data = AutoCArray(ctype=c_ubyte, size=size) ret = C_Digest(h_session, c_data_to_digest, c_digest_data_len, digested_data.array, digested_data.size) else: digested_data = AutoCArray(ctype=c_ubyte) @refresh_c_arrays(1) def _digest(): """ Perform the digest operations """ return C_Digest(h_session, c_data_to_digest, c_digest_data_len, digested_data.array, digested_data.size) ret = _digest() if ret != CKR_OK: return ret, None # Convert Digested data into a python string digested_python_string = string_at(digested_data.array, digested_data.size.contents.value) return ret, digested_python_string