"""
PKCS11 Operations related to Signing and Verifying data
"""
import logging
from _ctypes import POINTER
from ctypes import create_string_buffer, cast, byref, string_at, c_ubyte
from pycryptoki.conversions import from_bytestring
from .attributes import to_char_array, to_byte_array
from .common_utils import refresh_c_arrays, AutoCArray
from .conversions import from_bytestring
from .cryptoki import CK_ULONG, \
CK_BYTE_PTR, C_SignInit, C_Sign
from .cryptoki import C_VerifyInit, C_Verify, C_SignUpdate, \
C_SignFinal, C_VerifyUpdate, C_VerifyFinal
from .defines import CKR_OK
from .encryption import MAX_BUFFER
from .exceptions import make_error_handle_function
from .lookup_dicts import ret_vals_dictionary
from .mechanism import parse_mechanism
LOG = logging.getLogger(__name__)
[docs]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
c_sign_ex = make_error_handle_function(c_sign)
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
[docs]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
[docs]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
c_verify_ex = make_error_handle_function(c_verify)