/*
 * COPYRIGHT (c) International Business Machines Corp. 2005-2017
 *
 * This program is provided under the terms of the Common Public License,
 * version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
 * software constitutes recipient's acceptance of CPL-1.0 terms which can be
 * found in the file LICENSE file or at
 * https://opensource.org/licenses/cpl1.0.php
 */

#include <windows.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>

#include "pkcs11types.h"
#include "regress.h"
#include "common.c"
#include "mech_to_str.h"

static CK_BBOOL true = TRUE;
static CK_BBOOL false = FALSE;

//
//
CK_RV do_SignVerify_SSL3_MD5_MAC(CK_SESSION_HANDLE session)
{
    CK_MECHANISM mech;
    CK_ULONG mac_size;
    CK_ULONG i;
    CK_RV rc = CKR_OK;

    CK_OBJECT_HANDLE h_key;
    CK_OBJECT_CLASS key_class = CKO_SECRET_KEY;
    CK_KEY_TYPE key_type = CKK_GENERIC_SECRET;
    CK_BBOOL false = FALSE;
    CK_BYTE hash[MD5_HASH_LEN];
    CK_BYTE data[50];
    CK_BYTE data2[500];
    CK_BYTE key_data[48];
    CK_ULONG hash_len;
    CK_ULONG data_len;
    CK_ATTRIBUTE key_attribs[] = {
        {CKA_CLASS, &key_class, sizeof(key_class)},
        {CKA_KEY_TYPE, &key_type, sizeof(key_type)},
        {CKA_TOKEN, &false, sizeof(false)},
        {CKA_VALUE, &key_data, sizeof(key_data)}
    };
    CK_SLOT_ID slot_id = SLOT_ID;

    testcase_begin("starting do_SignVerify_SSL3_MD5_MAC...\n");

    mac_size = 16;

    mech.mechanism = CKM_SSL3_MD5_MAC;
    mech.ulParameterLen = sizeof(CK_ULONG);
    mech.pParameter = &mac_size;


    /** skip test if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, mech.mechanism)) {
        testsuite_skip(48, "Slot %u doesn't support %s (0x%x)",
                       (unsigned int) slot_id,
                       mech_to_str(mech.mechanism),
                       (unsigned int) mech.mechanism);
        goto skipped;
    }

    for (i = 0; i < 48; i++)
        key_data[i] = i;

    memset(data, 0xb, 50);
    data_len = 50;

    rc = funcs->C_CreateObject(session, key_attribs, 4, &h_key);
    if (rc != CKR_OK) {
        if (is_rejected_by_policy(rc, session)) {
            testcase_skip("key import is not allowed by policy");
            return CKR_OK;
        }
        testcase_error("C_CreateObject() rc = %s", p11_get_ckr(rc));
        return rc;
    }

    testcase_new_assertion();
    rc = funcs->C_SignInit(session, &mech, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_SignInit() rc = %s", p11_get_ckr(rc));
        goto done;
    }

    hash_len = sizeof(hash);
    rc = funcs->C_Sign(session, data, data_len, hash, &hash_len);
    if (rc != CKR_OK) {
        testcase_fail("C_Sign() rc = %s", p11_get_ckr(rc));
        goto done;
    }

    if (hash_len != mac_size) {
        testcase_fail("Error: C_Sign generated bad MAC length\n");
        goto done;
    } else {
        testcase_pass("Successfully signed.");
    }

    testcase_new_assertion();
    rc = funcs->C_VerifyInit(session, &mech, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_VerifyInit() rc = %s", p11_get_ckr(rc));
        goto done;
    }

    rc = funcs->C_Verify(session, data, data_len, hash, hash_len);
    if (rc != CKR_OK)
        testcase_fail("C_Verify() rc = %s", p11_get_ckr(rc));
    else
        testcase_pass("Successfully verified.");

    rc = funcs->C_DestroyObject(session, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_DestroyObject() rc = %s", p11_get_ckr(rc));
        goto done;
    }
    // TESTCASE #2

    for (i = 0; i < 48; i++)
        key_data[i] = i;

    memset(data2, 0xb, 500);
    data_len = 500;

    rc = funcs->C_CreateObject(session, key_attribs, 4, &h_key);
    if (rc != CKR_OK) {
        testcase_error("C_CreateObject() rc = %s", p11_get_ckr(rc));
        goto done;
    }

    testcase_new_assertion();
    rc = funcs->C_SignInit(session, &mech, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_SignInit() rc = %s", p11_get_ckr(rc));
        goto done;
    }

    for (i = 0; i < 500; i += 100) {
        rc = funcs->C_SignUpdate(session, &data2[i], 100);
        if (rc != CKR_OK) {
            testcase_error("Iteration #%lu, C_SignUpdate() rc = %s", i / 100,
                           p11_get_ckr(rc));
            goto done;
        }
    }

    hash_len = sizeof(hash);
    rc = funcs->C_SignFinal(session, hash, &hash_len);
    if (rc != CKR_OK) {
        testcase_error("C_SignFinal() rc = %s", p11_get_ckr(rc));
        goto done;
    }

    if (hash_len != mac_size) {
        testcase_fail("Error: C_SignUpdate/Final generated bad MAC length\n");
        goto done;
    } else {
        testcase_pass("Sucessfully signed in multipart.");
    }

    testcase_new_assertion();
    rc = funcs->C_VerifyInit(session, &mech, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_VerifyInit() rc = %s", p11_get_ckr(rc));
        goto done;
    }

    for (i = 0; i < 500; i += 100) {
        rc = funcs->C_VerifyUpdate(session, &data2[i], 100);
        if (rc != CKR_OK) {
            testcase_error("Iteration #%lu, C_VerifyUpdate() rc = %s", i / 100,
                           p11_get_ckr(rc));
            goto done;
        }
    }

    rc = funcs->C_VerifyFinal(session, hash, hash_len);
    if (rc != CKR_OK)
        testcase_fail("C_VerifyFinal rc = %s", p11_get_ckr(rc));
    else
        testcase_pass("Successfully verified signature in multipart.");

done:
    if (funcs->C_DestroyObject(session, h_key) != CKR_OK)
        testcase_error("C_DestroyObject failed.");

skipped:
    return rc;
}


//
//
CK_RV do_SignVerify_SSL3_SHA1_MAC(CK_SESSION_HANDLE session)
{
    CK_MECHANISM mech;
    CK_ULONG mac_size;
    CK_ULONG i;
    CK_RV rc = CKR_OK;

    CK_OBJECT_HANDLE h_key;
    CK_OBJECT_CLASS key_class = CKO_SECRET_KEY;
    CK_KEY_TYPE key_type = CKK_GENERIC_SECRET;
    CK_BBOOL false = FALSE;
    CK_BYTE hash[SHA1_HASH_LEN];
    CK_BYTE data[50];
    CK_BYTE key_data[48];
    CK_ULONG hash_len;
    CK_ULONG data_len;
    CK_ATTRIBUTE key_attribs[] = {
        {CKA_CLASS, &key_class, sizeof(key_class)},
        {CKA_KEY_TYPE, &key_type, sizeof(key_type)},
        {CKA_TOKEN, &false, sizeof(false)},
        {CKA_VALUE, &key_data, sizeof(key_data)}
    };
    CK_SLOT_ID slot_id = SLOT_ID;

    testcase_begin("starting do_SignVerify_SSL3_SHA1_MAC...\n");

    mac_size = 20;

    mech.mechanism = CKM_SSL3_SHA1_MAC;
    mech.ulParameterLen = sizeof(CK_ULONG);
    mech.pParameter = &mac_size;

    /** skip test if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, mech.mechanism)) {
        testsuite_skip(48, "Slot %u doesn't support %s (0x%x)",
                       (unsigned int) slot_id,
                       mech_to_str(mech.mechanism),
                       (unsigned int) mech.mechanism);
        goto skipped;
    }

    for (i = 0; i < 48; i++)
        key_data[i] = i;

    memset(data, 0xb, 50);
    data_len = 50;

    rc = funcs->C_CreateObject(session, key_attribs, 4, &h_key);
    if (rc != CKR_OK) {
        if (is_rejected_by_policy(rc, session)) {
            testcase_skip("key import is not allowed by policy");
            return CKR_OK;
        }
        testcase_error("C_CreateObject() rc = %s", p11_get_ckr(rc));
        return rc;
    }

    testcase_new_assertion();
    rc = funcs->C_SignInit(session, &mech, h_key);
    if (rc != CKR_OK) {
        testcase_fail("C_SignInit() rc = %s", p11_get_ckr(rc));
        goto done;
    }

    hash_len = sizeof(hash);
    rc = funcs->C_Sign(session, data, data_len, hash, &hash_len);
    if (rc != CKR_OK) {
        testcase_fail("C_Sign() rc = %s", p11_get_ckr(rc));
        goto done;
    }

    if (hash_len != mac_size) {
        testcase_fail("Error: C_Sign generated bad MAC length\n");
        goto done;
    } else {
        testcase_pass("Successfully signed.");
    }

    testcase_new_assertion();
    rc = funcs->C_VerifyInit(session, &mech, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_VerifyInit() rc = %s", p11_get_ckr(rc));
        goto done;
    }

    rc = funcs->C_Verify(session, data, data_len, hash, hash_len);
    if (rc != CKR_OK)
        testcase_fail("C_Verify() rc = %s", p11_get_ckr(rc));
    else
        testcase_pass("Successfully verified signature.");

done:
    if (funcs->C_DestroyObject(session, h_key) != CKR_OK)
        testcase_error("C_DestroyObject() failed.");

skipped:
    return rc;
}


//
//
CK_RV do_SSL3_PreMasterKeyGen(CK_SESSION_HANDLE session)
{
    CK_MECHANISM mech;
    CK_VERSION version;
    CK_OBJECT_HANDLE h_key;
    CK_RV rc = CKR_OK;
    CK_SLOT_ID slot_id = SLOT_ID;

    testcase_begin("starting do_SSL3_PreMasterKeyGen...\n");

    version.major = 3;
    version.minor = 0;

    mech.mechanism = CKM_SSL3_PRE_MASTER_KEY_GEN;
    mech.pParameter = &version;
    mech.ulParameterLen = sizeof(CK_VERSION);

    /** skip test if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, mech.mechanism)) {
        testsuite_skip(1, "Slot %u doesn't support %s (0x%x)",
                       (unsigned int) slot_id,
                       mech_to_str(mech.mechanism),
                       (unsigned int) mech.mechanism);
        goto done;
    }

    testcase_new_assertion();
    rc = funcs->C_GenerateKey(session, &mech, NULL, 0, &h_key);
    if (rc != CKR_OK) {
        if (is_rejected_by_policy(rc, session)) {
            testcase_skip("key generation is not allowed by policy");
            return CKR_OK;
        }
        testcase_fail("C_GenerateKey() rc = %s", p11_get_ckr(rc));
    } else {
        testcase_pass("Successfully generated a generic secret key.");
    }

    if (funcs->C_DestroyObject(session, h_key) != CKR_OK)
        testcase_error("C_DestroyObject() failed");

done:
    return rc;
}


//
//
CK_RV do_SSL3_MasterKeyDerive(CK_SESSION_HANDLE session)
{
    CK_MECHANISM mech;
    CK_OBJECT_HANDLE h_pm_secret = CK_INVALID_HANDLE;
    CK_OBJECT_HANDLE h_mk = CK_INVALID_HANDLE;
    CK_RV rc = CKR_OK;

    CK_VERSION version = { 3, 0 };
    CK_ATTRIBUTE pm_tmpl[] = {
        {CKA_SENSITIVE, &false, sizeof(CK_BBOOL)},
        {CKA_EXTRACTABLE, &true, sizeof(CK_BBOOL)}
    };

    CK_BYTE client_random_data[256];
    CK_BYTE server_random_data[256];
    CK_ATTRIBUTE m_tmpl[] = {
        {CKA_SENSITIVE, &true, sizeof(CK_BBOOL)},
        {CKA_EXTRACTABLE, &false, sizeof(CK_BBOOL)}
    };

    CK_SSL3_MASTER_KEY_DERIVE_PARAMS mk_params;
    CK_ULONG i;

    CK_OBJECT_CLASS class;
    CK_KEY_TYPE keyType;
    CK_ATTRIBUTE test_tmpl[] = {
        {CKA_CLASS, &class, sizeof(class)},
        {CKA_KEY_TYPE, &keyType, sizeof(keyType)}
    };
    CK_SLOT_ID slot_id = SLOT_ID;

    testcase_begin("starting do_SSL3_MasterKeyDerive...\n");

    // generate the pre-master secret key
    //
    mech.mechanism = CKM_SSL3_PRE_MASTER_KEY_GEN;
    mech.pParameter = &version;
    mech.ulParameterLen = sizeof(CK_VERSION);

    /** skip test if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, mech.mechanism)) {
        testsuite_skip(32, "Slot %u doesn't support %s (0x%x)",
                       (unsigned int) slot_id,
                       mech_to_str(mech.mechanism),
                       (unsigned int) mech.mechanism);
        goto skipped;
    }

    testcase_new_assertion();
    rc = funcs->C_GenerateKey(session, &mech, pm_tmpl, 2, &h_pm_secret);
    if (rc != CKR_OK) {
        if (is_rejected_by_policy(rc, session)) {
            testcase_skip("key generation is not allowed by policy");
            return CKR_OK;
        }
        testcase_fail("C_GenerateKey() rc= %s", p11_get_ckr(rc));
        goto done;
    } else {
        testcase_pass("Successfully generated a generic secret key.");
    }

    // derive a master key
    //

    for (i = 0; i < 32; i++) {
        client_random_data[i] = i;
        server_random_data[i] = 32 - i;
    }

    mk_params.pVersion = &version;

    mk_params.RandomInfo.pClientRandom = client_random_data;
    mk_params.RandomInfo.pServerRandom = server_random_data;
    mk_params.RandomInfo.ulClientRandomLen = 32;
    mk_params.RandomInfo.ulServerRandomLen = 32;

    mech.mechanism = CKM_SSL3_MASTER_KEY_DERIVE;
    mech.pParameter = &mk_params;
    mech.ulParameterLen = sizeof(CK_SSL3_MASTER_KEY_DERIVE_PARAMS);

    testcase_new_assertion();
    rc = funcs->C_DeriveKey(session, &mech, h_pm_secret, m_tmpl, 2, &h_mk);
    if (rc != CKR_OK) {
        if (is_rejected_by_policy(rc, session)) {
            testcase_skip("Key derivation is not allowed by policy");
            goto done;
        }
        testcase_fail("C_DeriveKey() rc= %s", p11_get_ckr(rc));
        goto done;
    } else {
        testcase_pass("Successfully derived a key from pre-master.");
    }

    /*
     * This mechanism provides the following attributes:
     * CKA_CLASS = CKO_SECRET_KEY
     * CKA_KEY_TYPE = CKK_GENERIC_SECRET
     * CKA_VALUE_LEN = 48
     * Check that the newly derived key has these.
     */
    testcase_new_assertion();
    rc = funcs->C_GetAttributeValue(session, h_pm_secret, test_tmpl, 2);
    if (rc != CKR_OK) {
        testcase_error("C_GetAttributeValue() rc= %s", p11_get_ckr(rc));
        goto done;
    }
    if (*(CK_OBJECT_CLASS *) test_tmpl[0].pValue != CKO_SECRET_KEY) {
        testcase_fail("Derived key has incorrect class.");
        goto done;
    }

    if (*(CK_KEY_TYPE *) test_tmpl[1].pValue != CKK_GENERIC_SECRET) {
        testcase_fail("Derived key has incorrect key type.");
        goto done;
    } else {
        testcase_pass("Derived key has correct attributes.");
    }

done:
    if (funcs->C_DestroyObject(session, h_mk) != CKR_OK)
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));

    if (funcs->C_DestroyObject(session, h_pm_secret) != CKR_OK)
        testcase_error("C_DestroyObject() failed");

skipped:
    return rc;
}

CK_RV do_SSL3_MultipleKeysDerive(CK_SESSION_HANDLE session)
{
    CK_MECHANISM mech;
    CK_OBJECT_HANDLE h_pm_secret;
    CK_RV rc = CKR_OK;
    CK_ULONG i;

    CK_VERSION version = { 3, 0 };
    CK_BBOOL true_value = TRUE;
    CK_BBOOL false_value = FALSE;
    CK_ATTRIBUTE pm_tmpl[] = {
        {CKA_TOKEN, &true_value, sizeof(true_value)},
    };

    CK_BYTE client_random_data[32];
    CK_BYTE server_random_data[32];
    CK_ATTRIBUTE incomplete_tmpl[] = {
        {CKA_TOKEN, &false_value, sizeof(false_value)},
        {CKA_SENSITIVE, &false_value, sizeof(false_value)},
        {CKA_EXTRACTABLE, &true_value, sizeof(true_value)}
    };

    CK_OBJECT_CLASS class = CKO_SECRET_KEY;
    CK_KEY_TYPE key_type = CKK_AES;
    CK_ULONG key_len = 16;
    CK_ATTRIBUTE complete_tmpl[] = {
        {CKA_CLASS, &class, sizeof(class)},
        {CKA_KEY_TYPE, &key_type, sizeof(key_type)},
        {CKA_VALUE_LEN, &key_len, sizeof(CK_ULONG)},
        {CKA_TOKEN, &false_value, sizeof(false_value)},
        {CKA_SENSITIVE, &false_value, sizeof(false_value)},
        {CKA_EXTRACTABLE, &true_value, sizeof(true_value)}
    };

    CK_BYTE iv_client[128 / 8] = { 0, };
    CK_BYTE iv_server[128 / 8] = { 0, };

    CK_SSL3_KEY_MAT_OUT param_out = {
        .hClientMacSecret = 0,
        .hServerMacSecret = 0,
        .hClientKey = 0,
        .hServerKey = 0,
        .pIVClient = iv_client,
        .pIVServer = iv_server,
    };

    CK_SSL3_KEY_MAT_PARAMS params = {
        .ulMacSizeInBits = 128,
        .ulKeySizeInBits = key_len * 8,
        .ulIVSizeInBits = 128,
        .bIsExport = FALSE,
        .RandomInfo =
        {
            .pClientRandom = client_random_data,
            .ulClientRandomLen = sizeof(client_random_data),
            .pServerRandom = server_random_data,
            .ulServerRandomLen = sizeof(server_random_data),
        },
        .pReturnedKeyMaterial = &param_out,
    };
    CK_SLOT_ID slot_id = SLOT_ID;

    testcase_begin("starting do_SSL3_MultipleKeysDerive...\n");

    // generate the pre-master secret key
    //
    mech.mechanism = CKM_SSL3_PRE_MASTER_KEY_GEN;
    mech.pParameter = &version;
    mech.ulParameterLen = sizeof(CK_VERSION);

    /** skip test if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, mech.mechanism)) {
        testsuite_skip(3, "Slot %u doesn't support %s (0x%x)",
                       (unsigned int) slot_id,
                       mech_to_str(mech.mechanism),
                       (unsigned int) mech.mechanism);
        goto skipped;
    }

    testcase_new_assertion();
    rc = funcs->C_GenerateKey(session, &mech, pm_tmpl,
                              sizeof(pm_tmpl) / sizeof(*pm_tmpl), &h_pm_secret);
    if (rc != CKR_OK) {
        if (is_rejected_by_policy(rc, session)) {
            testcase_skip("Key generation is not allowed by policy");
            goto done;
        }
        testcase_fail("C_GenerateKey() rc= %s", p11_get_ckr(rc));
        goto done;
    } else {
        testcase_pass("Successfully generated a generic secret key.");
    }

    for (i = 0; i < sizeof(client_random_data); i++) {
        client_random_data[i] = i;
        server_random_data[i] = sizeof(client_random_data) - i;
    }

    mech.mechanism = CKM_SSL3_KEY_AND_MAC_DERIVE;
    mech.pParameter = &params;
    mech.ulParameterLen = sizeof(params);

    /*
     * Try deriving the key without required attributes...
     */
    testcase_new_assertion();
    rc = funcs->C_DeriveKey(session, &mech, h_pm_secret, incomplete_tmpl,
                            sizeof(incomplete_tmpl) / sizeof(*incomplete_tmpl),
                            NULL);
    if (is_rejected_by_policy(rc, session)) {
        testcase_skip("key derivation is not allowed by policy");
        goto done;
    }
    if (rc != CKR_TEMPLATE_INCOMPLETE) {
        testcase_fail("C_DeriveKey did not recognize missing attributes.");
        goto done;
    } else {
        testcase_pass("Success, could not derive key without required "
                      "attributes.");
    }

    /*
     * Now derive key with required attributes...
     */

    testcase_new_assertion();
    rc = funcs->C_DeriveKey(session, &mech, h_pm_secret, complete_tmpl,
                            sizeof(complete_tmpl) / sizeof(*complete_tmpl),
                            NULL);
    if (rc != CKR_OK) {
        if (is_rejected_by_policy(rc, session)) {
            testcase_skip("key derivation is not allowed by policy");
            goto done;
        }
        testcase_fail("C_DeriveKey() rc= %s", p11_get_ckr(rc));
        goto done;
    } else {
        testcase_pass("Successfully derived a keys from pre-master.");
    }


    if (funcs->C_DestroyObject(session, param_out.hClientMacSecret))
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
    if (funcs->C_DestroyObject(session, param_out.hServerMacSecret))
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
    if (funcs->C_DestroyObject(session, param_out.hClientKey))
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
    if (funcs->C_DestroyObject(session, param_out.hServerKey))
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));

done:
    if (funcs->C_DestroyObject(session, h_pm_secret) != CKR_OK)
        testcase_error("C_DestroyObject() failed");

skipped:
    return rc;
}

CK_RV do_TLS_PreMasterKeyGen(CK_SESSION_HANDLE session)
{
    CK_MECHANISM mech;
    CK_VERSION version;
    CK_OBJECT_HANDLE h_key;
    CK_RV rc = CKR_OK;
    CK_SLOT_ID slot_id = SLOT_ID;

    testcase_begin("starting do_TLS_PreMasterKeyGen...\n");

    version.major = 3;
    version.minor = 3;

    mech.mechanism = CKM_TLS_PRE_MASTER_KEY_GEN;
    mech.pParameter = &version;
    mech.ulParameterLen = sizeof(CK_VERSION);

    /** skip test if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, mech.mechanism)) {
        testsuite_skip(1, "Slot %u doesn't support %s (0x%x)",
                       (unsigned int) slot_id,
                       mech_to_str(mech.mechanism),
                       (unsigned int) mech.mechanism);
        goto done;
    }

    testcase_new_assertion();
    rc = funcs->C_GenerateKey(session, &mech, NULL, 0, &h_key);
    if (rc != CKR_OK) {
        if (is_rejected_by_policy(rc, session)) {
            testcase_skip("key generation is not allowed by policy");
            return CKR_OK;
        }
        testcase_fail("C_GenerateKey() rc = %s", p11_get_ckr(rc));
    } else {
        testcase_pass("Successfully generated a generic secret key.");
    }

    if (funcs->C_DestroyObject(session, h_key) != CKR_OK)
        testcase_error("C_DestroyObject() failed");

done:
    return rc;
}

CK_RV do_TLS_MultipleKeysDerive(CK_SESSION_HANDLE session)
{
    CK_MECHANISM mech;
    CK_OBJECT_HANDLE h_pm_secret;
    CK_RV rc = CKR_OK;
    CK_ULONG i;

    CK_VERSION version = { 3, 3 };
    CK_BBOOL true_value = TRUE;
    CK_BBOOL false_value = FALSE;
    CK_ATTRIBUTE pm_tmpl[] = {
        {CKA_TOKEN, &true_value, sizeof(true_value)},
    };

    CK_BYTE client_random_data[32];
    CK_BYTE server_random_data[32];
    CK_ATTRIBUTE incomplete_tmpl[] = {
        {CKA_TOKEN, &false_value, sizeof(false_value)},
        {CKA_SENSITIVE, &false_value, sizeof(false_value)},
        {CKA_EXTRACTABLE, &true_value, sizeof(true_value)}
    };

    CK_OBJECT_CLASS class = CKO_SECRET_KEY;
    CK_KEY_TYPE key_type = CKK_AES;
    CK_ULONG key_len = 16;
    CK_ATTRIBUTE complete_tmpl[] = {
        {CKA_CLASS, &class, sizeof(class)},
        {CKA_KEY_TYPE, &key_type, sizeof(key_type)},
        {CKA_VALUE_LEN, &key_len, sizeof(CK_ULONG)},
        {CKA_TOKEN, &false_value, sizeof(false_value)},
        {CKA_SENSITIVE, &false_value, sizeof(false_value)},
        {CKA_EXTRACTABLE, &true_value, sizeof(true_value)}
    };

    CK_BYTE iv_client[128 / 8] = { 0, };
    CK_BYTE iv_server[128 / 8] = { 0, };

    CK_SSL3_KEY_MAT_OUT param_out = {
        .hClientMacSecret = 0,
        .hServerMacSecret = 0,
        .hClientKey = 0,
        .hServerKey = 0,
        .pIVClient = iv_client,
        .pIVServer = iv_server,
    };

    CK_SSL3_KEY_MAT_PARAMS params = {
        .ulMacSizeInBits = 128,
        .ulKeySizeInBits = key_len * 8,
        .ulIVSizeInBits = 128,
        .bIsExport = FALSE,
        .RandomInfo =
        {
            .pClientRandom = client_random_data,
            .ulClientRandomLen = sizeof(client_random_data),
            .pServerRandom = server_random_data,
            .ulServerRandomLen = sizeof(server_random_data),
        },
        .pReturnedKeyMaterial = &param_out,
    };
    CK_SLOT_ID slot_id = SLOT_ID;

    testcase_begin("starting do_TLS_MultipleKeysDerive...\n");

    // generate the pre-master secret key
    //
    mech.mechanism = CKM_TLS_PRE_MASTER_KEY_GEN;
    mech.pParameter = &version;
    mech.ulParameterLen = sizeof(CK_VERSION);

    /** skip test if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, mech.mechanism)) {
        testsuite_skip(3, "Slot %u doesn't support %s (0x%x)",
                       (unsigned int) slot_id,
                       mech_to_str(mech.mechanism),
                       (unsigned int) mech.mechanism);
        goto skipped;
    }

    testcase_new_assertion();
    rc = funcs->C_GenerateKey(session, &mech, pm_tmpl,
                              sizeof(pm_tmpl) / sizeof(*pm_tmpl), &h_pm_secret);
    if (rc != CKR_OK) {
        if (is_rejected_by_policy(rc, session)) {
            testcase_skip("Key generation is not allowed by policy");
            goto done;
        }
        testcase_fail("C_GenerateKey() rc= %s", p11_get_ckr(rc));
        goto done;
    } else {
        testcase_pass("Successfully generated a generic secret key.");
    }

    for (i = 0; i < sizeof(client_random_data); i++) {
        client_random_data[i] = i;
        server_random_data[i] = sizeof(client_random_data) - i;
    }

    mech.mechanism = CKM_TLS_KEY_AND_MAC_DERIVE;
    mech.pParameter = &params;
    mech.ulParameterLen = sizeof(params);

    /*
     * Try deriving the key without required attributes...
     */
    testcase_new_assertion();
    rc = funcs->C_DeriveKey(session, &mech, h_pm_secret, incomplete_tmpl,
                            sizeof(incomplete_tmpl) / sizeof(*incomplete_tmpl),
                            NULL);
    if (is_rejected_by_policy(rc, session)) {
        testcase_skip("key derivation is not allowed by policy");
        goto done;
    }
    if (rc != CKR_TEMPLATE_INCOMPLETE) {
        testcase_fail("C_DeriveKey did not recognize missing attributes.");
        goto done;
    } else {
        testcase_pass("Success, could not derive key without required "
                      "attributes.");
    }

    /*
     * Now derive key with required attributes...
     */

    testcase_new_assertion();
    rc = funcs->C_DeriveKey(session, &mech, h_pm_secret, complete_tmpl,
                            sizeof(complete_tmpl) / sizeof(*complete_tmpl),
                            NULL);
    if (rc != CKR_OK) {
        if (is_rejected_by_policy(rc, session)) {
            testcase_skip("key derivation is not allowed by policy");
            goto done;
        }
        testcase_fail("C_DeriveKey() rc= %s", p11_get_ckr(rc));
        goto done;
    } else {
        testcase_pass("Successfully derived a keys from pre-master.");
    }


    if (funcs->C_DestroyObject(session, param_out.hClientMacSecret))
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
    if (funcs->C_DestroyObject(session, param_out.hServerMacSecret))
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
    if (funcs->C_DestroyObject(session, param_out.hClientKey))
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
    if (funcs->C_DestroyObject(session, param_out.hServerKey))
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));

done:
    if (funcs->C_DestroyObject(session, h_pm_secret) != CKR_OK)
        testcase_error("C_DestroyObject() failed");

skipped:
    return rc;
}

CK_RV ssl3_functions(void)
{
    CK_RV rc;
    SYSTEMTIME t1, t2;
    CK_SLOT_ID slot_id = SLOT_ID;
    CK_SESSION_HANDLE session;
    CK_FLAGS flags;
    CK_BYTE user_pin[PKCS11_MAX_PIN_LEN];
    CK_ULONG user_pin_len;

    testcase_rw_session();
    testcase_user_login();

    GetSystemTime(&t1);
    rc = do_SSL3_PreMasterKeyGen(session);
    if (rc && !no_stop)
        goto testcase_cleanup;
    GetSystemTime(&t2);
    process_time(t1, t2);

    GetSystemTime(&t1);
    rc = do_SSL3_MasterKeyDerive(session);
    if (rc && !no_stop)
        goto testcase_cleanup;
    GetSystemTime(&t2);
    process_time(t1, t2);

    GetSystemTime(&t1);
    rc = do_SSL3_MultipleKeysDerive(session);
    if (rc && !no_stop)
        goto testcase_cleanup;
    GetSystemTime(&t2);
    process_time(t1, t2);

    GetSystemTime(&t1);
    rc = do_SignVerify_SSL3_SHA1_MAC(session);
    if (rc && !no_stop)
        goto testcase_cleanup;
    GetSystemTime(&t2);
    process_time(t1, t2);

    GetSystemTime(&t1);
    rc = do_SignVerify_SSL3_MD5_MAC(session);
    if (rc && !no_stop)
        return rc;
    GetSystemTime(&t2);
    process_time(t1, t2);

    GetSystemTime(&t1);
    rc = do_TLS_PreMasterKeyGen(session);
    if (rc && !no_stop)
        goto testcase_cleanup;
    GetSystemTime(&t2);
    process_time(t1, t2);

    GetSystemTime(&t1);
    rc = do_TLS_MultipleKeysDerive(session);
    if (rc && !no_stop)
        goto testcase_cleanup;
    GetSystemTime(&t2);
    process_time(t1, t2);

testcase_cleanup:
    testcase_user_logout();
    rc = funcs->C_CloseAllSessions(slot_id);
    if (rc != CKR_OK) {
        testcase_error("C_CloseSessions rc=%s", p11_get_ckr(rc));
    }

    return rc;
}

int main(int argc, char **argv)
{
    CK_C_INITIALIZE_ARGS cinit_args;
    int rc;
    CK_RV rv;

    rc = do_ParseArgs(argc, argv);
    if (rc != 1)
        return rc;

    printf("Using slot #%lu...\n\n", SLOT_ID);
    printf("With option: no_init: %d\n", no_init);

    rc = do_GetFunctionList();
    if (!rc) {
        PRINT_ERR("ERROR do_GetFunctionList() Failed , rc = 0x%0x\n", rc);
        return rc;
    }

    memset(&cinit_args, 0x0, sizeof(cinit_args));
    cinit_args.flags = CKF_OS_LOCKING_OK;

    // SAB Add calls to ALL functions before the C_Initialize gets hit

    funcs->C_Initialize(&cinit_args);

    {
        CK_SESSION_HANDLE hsess = 0;

        rc = funcs->C_GetFunctionStatus(hsess);
        if (rc != CKR_FUNCTION_NOT_PARALLEL)
            return rc;

        rc = funcs->C_CancelFunction(hsess);
        if (rc != CKR_FUNCTION_NOT_PARALLEL)
            return rc;

    }

    testcase_setup();

    rv = ssl3_functions();

    testcase_print_result();

    funcs->C_Finalize(NULL);

    return testcase_return(rv);
}
