Esempio n. 1
0
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
Esempio n. 2
0
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))
Esempio n. 3
0
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
Esempio n. 4
0
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
Esempio n. 5
0
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
Esempio n. 6
0
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