/*
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>

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

/** Tests DES encryption with published test vectors. **/
CK_RV do_EncryptDES(struct published_test_suite_info *tsuite)
{
    unsigned int i;
    CK_BYTE expected[BIG_REQUEST];
    CK_BYTE actual[BIG_REQUEST];
    CK_ULONG expected_len, actual_len;

    CK_BYTE user_pin[PKCS11_MAX_PIN_LEN];
    CK_ULONG user_pin_len;
    CK_SESSION_HANDLE session;
    CK_MECHANISM mech;
    CK_OBJECT_HANDLE h_key;
    CK_RV rc;
    CK_FLAGS flags;
    CK_SLOT_ID slot_id = SLOT_ID;

    /** begin testsuite **/
    testsuite_begin("%s Encryption.", tsuite->name);
    testcase_rw_session();
    testcase_user_login();


    /** skip test if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, tsuite->mechanism)) {
        testsuite_skip(tsuite->tvcount,
                       "Slot %u doesn't support %s (%u)",
                       (unsigned int) slot_id,
                       mech_to_str(tsuite->mechanism),
                       (unsigned int) tsuite->mechanism);
        goto testcase_cleanup;
    }

    /** iterate over test vectors **/
    for (i = 0; i < tsuite->tvcount; i++) {

        /** begin testcase **/
        testcase_begin("%s Encryption with published test vector "
                       "%d.", tsuite->name, i);

        rc = CKR_OK;            // set rc

        /** clear buffers **/
        memset(expected, 0, sizeof(expected));
        memset(actual, 0, sizeof(actual));

        /** get ciphertext (expected results) **/
        expected_len = tsuite->tv[i].clen;
        memcpy(expected, tsuite->tv[i].ciphertext, expected_len);

        /** get plaintext **/
        actual_len = tsuite->tv[i].plen;
        memcpy(actual, tsuite->tv[i].plaintext, actual_len);

        /** get mech **/
        mech.mechanism = tsuite->mechanism;
        mech.ulParameterLen = tsuite->tv[i].ivlen;
        mech.pParameter = tsuite->tv[i].iv;

        /** create key handle. **/
        rc = create_DESKey(session,
                           tsuite->tv[i].key, tsuite->tv[i].klen, &h_key);
        if (rc != CKR_OK) {
            testcase_error("C_CreateObject rc=%s", p11_get_ckr(rc));
            goto testcase_cleanup;
        }

        /** initialize single encryption **/
        rc = funcs->C_EncryptInit(session, &mech, h_key);
        if (rc != CKR_OK) {
            testcase_error("C_EncryptInit rc=%s", p11_get_ckr(rc));
            goto error;
        }

        /** do single (in-place) encryption **/
        rc = funcs->C_Encrypt(session, actual, actual_len, actual, &actual_len);
        if (rc != CKR_OK) {
            testcase_error("C_Encrypt rc=%s", p11_get_ckr(rc));
            goto error;
        }

        /** compare encryption results with expected results. **/
        testcase_new_assertion();

        if (actual_len != expected_len) {
            testcase_fail("encrypted data length does not "
                          "match test vector's encrypted data "
                          "length.\n\nexpected length=%ld, but found"
                          " length=%ld\n", expected_len, actual_len);
        } else if (memcmp(actual, expected, expected_len)) {
            testcase_fail("encrypted data does not match test "
                          "vector's encrypted data");
        } else {
            testcase_pass("%s Encryption with test vector %d "
                          "passed.", tsuite->name, i);
        }

        /** clean up **/
        rc = funcs->C_DestroyObject(session, h_key);
        if (rc != CKR_OK) {
            testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
        }

    }
    goto testcase_cleanup;

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

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

    return rc;
}

/** Tests DES multipart encryption with published test vectors. **/
CK_RV do_EncryptUpdateDES(struct published_test_suite_info * tsuite)
{
    unsigned int i;
    CK_BYTE expected[BIG_REQUEST];
    CK_BYTE plaintext[BIG_REQUEST];
    CK_BYTE crypt[BIG_REQUEST];
    CK_ULONG expected_len, p_len, crypt_len = 0, k;

    CK_BYTE user_pin[PKCS11_MAX_PIN_LEN];
    CK_ULONG user_pin_len;
    CK_SESSION_HANDLE session;
    CK_MECHANISM mech;
    CK_OBJECT_HANDLE h_key;
    CK_RV rc;
    CK_FLAGS flags;
    CK_SLOT_ID slot_id = SLOT_ID;

    /** begin testsuite **/
    testsuite_begin("%s Multipart Encryption.", tsuite->name);
    testcase_rw_session();
    testcase_user_login();

    /** skip test if slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, tsuite->mechanism)) {
        testsuite_skip(tsuite->tvcount,
                       "Slot %u doesn't support %s (%u)",
                       (unsigned int) slot_id,
                       mech_to_str(tsuite->mechanism),
                       (unsigned int) tsuite->mechanism);
        goto testcase_cleanup;
    }

    /** iterate over test vectors **/
    for (i = 0; i < tsuite->tvcount; i++) {

        /** begin test **/
        testcase_begin("%s Multipart Encryption with published "
                       "test vector %d.", tsuite->name, i);

        rc = CKR_OK;            // set rc


        /** clear buffers **/
        memset(expected, 0, sizeof(expected));
        memset(crypt, 0, sizeof(crypt));
        memset(plaintext, 0, sizeof(plaintext));

        /** get ciphertext (expected results) **/
        expected_len = tsuite->tv[i].clen;
        memcpy(expected, tsuite->tv[i].ciphertext, expected_len);

        /** get plaintext **/
        p_len = tsuite->tv[i].plen;
        memcpy(plaintext, tsuite->tv[i].plaintext, p_len);

        /** get mech **/
        mech.mechanism = tsuite->mechanism;
        mech.ulParameterLen = tsuite->tv[i].ivlen;
        mech.pParameter = tsuite->tv[i].iv;

        /** create key handle **/
        rc = create_DESKey(session,
                           tsuite->tv[i].key, tsuite->tv[i].klen, &h_key);
        if (rc != CKR_OK) {
            testcase_error("C_CreateObject rc=%s", p11_get_ckr(rc));
            goto testcase_cleanup;
        }

        /** initialize multipart encryption **/
        rc = funcs->C_EncryptInit(session, &mech, h_key);
        if (rc != CKR_OK) {
            testcase_error("C_EncryptInit rc=%s", p11_get_ckr(rc));
            goto error;
        }

        /* do multipart encryption
         * for chunks, -1 is NULL, and 0 is empty string,
         * and a value > 0 is amount of data from test vector's
         * plaintext data. The is way we test input in various sizes.
         */
        k = 0;
        if (tsuite->tv[i].num_chunks) {
            int j;
            CK_ULONG outlen, len;
            CK_BYTE *data_chunk = NULL;

            crypt_len = 0;
            outlen = sizeof(crypt);

            for (j = 0; j < tsuite->tv[i].num_chunks; j++) {
                if (tsuite->tv[i].chunks[j] == -1) {
                    len = 0;
                    data_chunk = NULL;
                } else if (tsuite->tv[i].chunks[j] == 0) {
                    len = 0;
                    data_chunk = (CK_BYTE *) "";
                } else {
                    len = tsuite->tv[i].chunks[j];
                    data_chunk = plaintext + k;
                }

                rc = funcs->C_EncryptUpdate(session, data_chunk,
                                            len, &crypt[crypt_len], &outlen);
                if (rc != CKR_OK) {
                    testcase_error("C_EncryptUpdate rc=%s", p11_get_ckr(rc));
                    goto error;
                }
                k += len;
                crypt_len += outlen;
                outlen = sizeof(crypt) - crypt_len;
            }
        } else {
            crypt_len = sizeof(crypt);
            rc = funcs->C_EncryptUpdate(session, plaintext, p_len,
                                        crypt, &crypt_len);
            if (rc != CKR_OK) {
                testcase_error("C_EncryptUpdate rc=%s", p11_get_ckr(rc));
                goto error;
            }
        }

        /** finalize multipart encryption **/
        k = sizeof(crypt) - crypt_len;
        rc = funcs->C_EncryptFinal(session, &crypt[p_len], &k);
        if (rc != CKR_OK) {
            testcase_error("C_EncryptFinal rc=%s", p11_get_ckr(rc));
            goto error;
        }

        /** compare encryption results with expected results. **/
        testcase_new_assertion();

        if (crypt_len != expected_len) {
            testcase_fail("encrypted multipart data length "
                          "does not match test vector's encrypted "
                          "data length.\n\nexpected length=%ld, "
                          "but found length=%ld\n", expected_len, crypt_len);
        } else if (memcmp(crypt, expected, expected_len)) {
            testcase_fail("encrypted multipart data does not "
                          "match test vector's encrypted data.\n");
        } else {
            testcase_pass("%s Multipart Encryption with test "
                          "vector %d passed.", tsuite->name, i);
        }

        rc = funcs->C_DestroyObject(session, h_key);
        if (rc != CKR_OK) {
            testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
            goto testcase_cleanup;
        }
    }
    goto testcase_cleanup;

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

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

    return rc;
}

/** Tests DES decryption with published test vectors. **/
CK_RV do_DecryptDES(struct published_test_suite_info * tsuite)
{
    unsigned int i;
    CK_BYTE expected[BIG_REQUEST];
    CK_BYTE actual[BIG_REQUEST];
    CK_ULONG expected_len, actual_len;

    CK_BYTE user_pin[PKCS11_MAX_PIN_LEN];
    CK_ULONG user_pin_len;
    CK_SESSION_HANDLE session;
    CK_MECHANISM mech;
    CK_OBJECT_HANDLE h_key;
    CK_RV rc;
    CK_FLAGS flags;
    CK_SLOT_ID slot_id = SLOT_ID;

    /** begin testsuite **/
    testsuite_begin("%s Decryption.", tsuite->name);
    testcase_rw_session();
    testcase_user_login();

    rc = CKR_OK;

    /** query the slot to see if the mech is supported. **/
    if (!mech_supported(slot_id, tsuite->mechanism)) {
        testsuite_skip(tsuite->tvcount,
                       "Slot %u doesn't support %s (%u)",
                       (unsigned int) slot_id,
                       mech_to_str(tsuite->mechanism),
                       (unsigned int) tsuite->mechanism);
        goto testcase_cleanup;
    }

    /** iterate over test vector **/
    for (i = 0; i < tsuite->tvcount; i++) {

        /** begin test **/
        testcase_begin("%s Decryption with published test vector " "%d.",
                       tsuite->name, i);

        rc = CKR_OK;

        /** create key handle. **/
        rc = create_DESKey(session,
                           tsuite->tv[i].key, tsuite->tv[i].klen, &h_key);
        if (rc != CKR_OK) {
            testcase_error("C_CreateObject rc=%s", p11_get_ckr(rc));
            goto testcase_cleanup;
        }

        /** get mech **/
        mech.mechanism = tsuite->mechanism;
        mech.ulParameterLen = tsuite->tv[i].ivlen;
        mech.pParameter = tsuite->tv[i].iv;

        /** clear buffers **/
        memset(expected, 0, sizeof(expected));
        memset(actual, 0, sizeof(actual));

        /** get plaintext (expected results) **/
        expected_len = tsuite->tv[i].plen;
        memcpy(expected, tsuite->tv[i].plaintext, expected_len);

        /** get ciphertext **/
        actual_len = tsuite->tv[i].clen;
        memcpy(actual, tsuite->tv[i].ciphertext, actual_len);

        /** initialize single decryption **/
        rc = funcs->C_DecryptInit(session, &mech, h_key);
        if (rc != CKR_OK) {
            testcase_error("C_DecryptInit rc=%s", p11_get_ckr(rc));
            goto error;
        }

        /** do single (in-place) decryption **/
        rc = funcs->C_Decrypt(session, actual, actual_len, actual, &actual_len);
        if (rc != CKR_OK) {
            testcase_error("C_Decrypt rc=%s", p11_get_ckr(rc));
            goto error;
        }

        /** compare decryption results with expected results. **/
        testcase_new_assertion();

        if (actual_len != expected_len) {
            testcase_fail("decrypted data length does not "
                          "match test vector's decrypted data "
                          "length.\n\nexpected length=%ld, but found"
                          " length=%ld\n", expected_len, actual_len);
        } else if (memcmp(actual, expected, expected_len)) {
            testcase_fail("decrypted data does not match test "
                          "vector's decrypted data.\n");
        } else {
            testcase_pass("%s Decryption with test vector %d "
                          "passed.", tsuite->name, i);
        }

    }

    /** clean up **/
    rc = funcs->C_DestroyObject(session, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
    }
    goto testcase_cleanup;

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

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

    return rc;
}

/** Tests DES multipart decryption with published test vectors. **/
CK_RV do_DecryptUpdateDES(struct published_test_suite_info * tsuite)
{
    unsigned int i;
    CK_BYTE expected[BIG_REQUEST];
    CK_BYTE cipher[BIG_REQUEST];
    CK_BYTE plaintext[BIG_REQUEST];
    CK_ULONG expected_len, p_len, cipher_len, k;

    CK_BYTE user_pin[PKCS11_MAX_PIN_LEN];
    CK_ULONG user_pin_len;
    CK_SESSION_HANDLE session;
    CK_MECHANISM mech;
    CK_OBJECT_HANDLE h_key;
    CK_RV rc;
    CK_FLAGS flags;
    CK_SLOT_ID slot_id = SLOT_ID;

    /** begin testsuite **/
    testsuite_begin("%s Multipart Decryption.", tsuite->name);
    testcase_rw_session();
    testcase_user_login();

    rc = CKR_OK;                // set rc

    /** skip testsuite if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, tsuite->mechanism)) {
        testsuite_skip(tsuite->tvcount,
                       "Slot %u doesn't support %s (%u)",
                       (unsigned int) slot_id,
                       mech_to_str(tsuite->mechanism),
                       (unsigned int) tsuite->mechanism);
        goto testcase_cleanup;
    }

    /** iterate over test vectors **/
    for (i = 0; i < tsuite->tvcount; i++) {

        /** begin test **/
        testcase_begin("%s Multipart Decryption with published "
                       "test vector %d.", tsuite->name, i);

        rc = CKR_OK;            // set rc

        /** clear buffers **/
        memset(expected, 0, sizeof(expected));
        memset(cipher, 0, sizeof(cipher));
        memset(plaintext, 0, sizeof(plaintext));

        /** get plaintext (expected results) **/
        expected_len = tsuite->tv[i].plen;
        memcpy(expected, tsuite->tv[i].plaintext, expected_len);

        /** get ciphertext **/
        cipher_len = tsuite->tv[i].clen;
        memcpy(cipher, tsuite->tv[i].ciphertext, cipher_len);

        /** get mech **/
        mech.mechanism = tsuite->mechanism;
        mech.ulParameterLen = tsuite->tv[i].ivlen;
        mech.pParameter = tsuite->tv[i].iv;

        /** create key handle. **/
        rc = create_DESKey(session,
                           tsuite->tv[i].key, tsuite->tv[i].klen, &h_key);
        if (rc != CKR_OK) {
            testcase_error("C_CreateObject rc=%s", p11_get_ckr(rc));
            goto testcase_cleanup;
        }

        /** initialize multipart decryption **/
        rc = funcs->C_DecryptInit(session, &mech, h_key);
        if (rc != CKR_OK) {
            testcase_error("C_DecryptInit rc=%s", p11_get_ckr(rc));
            goto error;
        }

        /* do multipart encryption
         * for chunks, -1 is NULL, and 0 is empty string,
         * and a value > 0 is amount of data from test vector's
         * plaintext data. The is way we test input in various sizes.
         */
        k = 0;
        if (tsuite->tv[i].num_chunks) {
            int j;
            CK_ULONG outlen, len;
            CK_BYTE *data_chunk = NULL;

            p_len = 0;
            outlen = sizeof(plaintext);
            for (j = 0; j < tsuite->tv[i].num_chunks; j++) {
                if (tsuite->tv[i].chunks[j] == -1) {
                    len = 0;
                    data_chunk = NULL;
                } else if (tsuite->tv[i].chunks[j] == 0) {
                    len = 0;
                    data_chunk = (CK_BYTE *) "";
                } else {
                    len = tsuite->tv[i].chunks[j];
                    data_chunk = cipher + k;
                }

                rc = funcs->C_DecryptUpdate(session, data_chunk,
                                            len, &plaintext[p_len], &outlen);
                if (rc != CKR_OK) {
                    testcase_error("C_DecryptUpdate rc=%s", p11_get_ckr(rc));
                    goto error;
                }
                k += len;
                p_len += outlen;
                outlen = sizeof(plaintext) - p_len;
            }
        } else {
            p_len = sizeof(plaintext);
            rc = funcs->C_DecryptUpdate(session, cipher, cipher_len,
                                        plaintext, &p_len);
            if (rc != CKR_OK) {
                testcase_error("C_DecryptUpdate rc=%s", p11_get_ckr(rc));
                goto error;
            }
        }

        /** finalize multipart decryption **/
        k = sizeof(plaintext) - p_len;
        rc = funcs->C_DecryptFinal(session, &plaintext[k], &k);
        if (rc != CKR_OK) {
            testcase_error("C_DecryptFinal rc=%s", p11_get_ckr(rc));
            goto error;
        }

        /** compare decryption results with expected results. **/
        testcase_new_assertion();

        if (p_len != expected_len) {
            testcase_fail("decrypted multipart data length "
                          "does not  match test vector's decrypted "
                          "data length.\n\nexpected length=%ld, but "
                          "found length=%ld\n", expected_len, p_len);
        } else if (memcmp(plaintext, expected, expected_len)) {
            testcase_fail("decrypted multipart data does not "
                          "match test vector's decrypted data.\n");
        } else {
            testcase_pass("%s Multipart Decryption with test "
                          "vector  %d passed.", tsuite->name, i);
        }

        /** clean up **/
        rc = funcs->C_DestroyObject(session, h_key);
        if (rc != CKR_OK) {
            testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
            goto testcase_cleanup;
        }

    }
    goto testcase_cleanup;

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

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

    return rc;
}

/** Tests DES encryption & decryption with generated (secure) keys. **/
CK_RV do_EncryptDecryptDES(struct generated_test_suite_info * tsuite)
{
    unsigned int j;
    CK_BYTE original[BIG_REQUEST];
    CK_BYTE crypt[BIG_REQUEST + DES_BLOCK_SIZE];
    CK_BYTE decrypt[BIG_REQUEST + DES_BLOCK_SIZE];
    CK_ULONG original_len, crypt_len, decrypt_len;

    CK_SESSION_HANDLE session;
    CK_MECHANISM mech, mechkey;
    CK_OBJECT_HANDLE h_key;
    CK_FLAGS flags;
    CK_BYTE user_pin[PKCS11_MAX_PIN_LEN];
    CK_ULONG user_pin_len;
    CK_SLOT_ID slot_id = SLOT_ID;
    CK_RV rc;

    /** begin test **/
    testcase_begin("%s Encryption/Decryption with key generation "
                   "test.", tsuite->name);
    testcase_rw_session();
    testcase_user_login();

    /** skip test if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, tsuite->mech.mechanism)) {
        testcase_skip("Slot %u doesn't support %s (%u)",
                      (unsigned int) slot_id,
                      mech_to_str(tsuite->mech.mechanism),
                      (unsigned int) tsuite->mech.mechanism);
        goto testcase_cleanup;
    }

    /** clear buffers **/
    memset(original, 0, sizeof(original));
    memset(crypt, 0, sizeof(crypt));
    memset(decrypt, 0, sizeof(decrypt));

    /** generate test data **/
    original_len = sizeof(original);
    crypt_len = sizeof(crypt);
    decrypt_len = sizeof(decrypt);

    for (j = 0; j < original_len; j++)
        original[j] = j % 255;

    /** set mechanism for key generation **/
    mechkey = des_keygen;

    /** generate key **/
    rc = funcs->C_GenerateKey(session, &mechkey, NULL, 0, &h_key);
    if (rc != CKR_OK) {
        testcase_error("C_GenerateKey rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** set mech for crypto **/
    mech = tsuite->mech;

    /** initialize single encryption **/
    rc = funcs->C_EncryptInit(session, &mech, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_EncryptInit rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** do single encryption **/
    rc = funcs->C_Encrypt(session, original, original_len, crypt, &crypt_len);
    if (rc != CKR_OK) {
        testcase_error("C_Encrypt rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** initialize single decryption **/
    rc = funcs->C_DecryptInit(session, &mech, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_DecryptInit rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** do single decryption **/
    rc = funcs->C_Decrypt(session, crypt, crypt_len, decrypt, &decrypt_len);
    if (rc != CKR_OK) {
        testcase_error("C_Decrypt rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** compare encryption/decryption results with original data **/
    testcase_new_assertion();

    if (decrypt_len != original_len) {
        testcase_fail("decrypted data length does not match "
                      "original data length.\nexpected length=%ld, "
                      "but found length=%ld\n", original_len, decrypt_len);
    } else if (memcmp(decrypt, original, original_len)) {
        testcase_fail("decrypted data does not match original " "data");
    } else {
        testcase_pass("%s Encryption/Decryption with key "
                      "generation test passed.", tsuite->name);
    }

    /** clean up **/
    rc = funcs->C_DestroyObject(session, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
    }
    goto testcase_cleanup;

error:
    rc = funcs->C_DestroyObject(session, h_key);
    if (rc != CKR_OK)
        testcase_error("C_DestroyObject rc=%s.", p11_get_ckr(rc));
    goto testcase_cleanup;

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

    return rc;
}

/** Tests DES multipart encryption & decryption with generated keys. **/
CK_RV do_EncryptDecryptUpdateDES(struct generated_test_suite_info * tsuite)
{
    unsigned int i, j, k;
    CK_BYTE original[BIG_REQUEST];
    CK_BYTE crypt[BIG_REQUEST + DES_BLOCK_SIZE];
    CK_BYTE decrypt[BIG_REQUEST + DES_BLOCK_SIZE];
    CK_ULONG original_len, crypt_len, decrypt_len;
    CK_ULONG tmp;

    CK_SESSION_HANDLE session;
    CK_MECHANISM mech, mechkey;
    CK_OBJECT_HANDLE h_key;
    CK_FLAGS flags;
    CK_BYTE user_pin[PKCS11_MAX_PIN_LEN];
    CK_ULONG user_pin_len;
    CK_SLOT_ID slot_id = SLOT_ID;
    CK_RV rc;

    /** begin test **/
    testcase_begin("%s Multipart Encryption/Decryption with key "
                   "generation test.", tsuite->name);
    testcase_rw_session();
    testcase_user_login();

    /** skip test if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, tsuite->mech.mechanism)) {
        testcase_skip("Slot %u doesn't support %s (%u)",
                      (unsigned int) slot_id,
                      mech_to_str(tsuite->mech.mechanism),
                      (unsigned int) tsuite->mech.mechanism);
        goto testcase_cleanup;
    }

    /** clear buffers **/
    memset(original, 0, sizeof(original));
    memset(crypt, 0, sizeof(crypt));
    memset(decrypt, 0, sizeof(decrypt));

    /** generate test data **/
    original_len = sizeof(original);

    for (j = 0; j < original_len; j++)
        original[j] = j % 255;

    /** set mechanism for key gen **/
    mechkey = des_keygen;

    /** generate key **/
    rc = funcs->C_GenerateKey(session, &mechkey, NULL, 0, &h_key);
    if (rc != CKR_OK) {
        testcase_error("C_GenerateKey rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** set mech for crypto **/
    mech = tsuite->mech;

    /** initialize multipart encryption **/
    rc = funcs->C_EncryptInit(session, &mech, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_EncryptInit #1 rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** do multipart encryption **/
    i = k = 0;                  // i indexes source buffer
    // k indexes destination buffer
    crypt_len = sizeof(crypt);
    tmp = 0;

    while (i < original_len) {
        tmp = crypt_len - k;    // room left in mpcrypt
        rc = funcs->C_EncryptUpdate(session,
                                    &original[i],
                                    DES_BLOCK_SIZE, &crypt[k], &tmp);
        if (rc != CKR_OK) {
            testcase_error("C_EncryptUpdate rc=%s", p11_get_ckr(rc));
            goto error;
        }
        k += tmp;
        i += DES_BLOCK_SIZE;
    }

    /** finalize multipart encryption **/
    crypt_len = sizeof(crypt) - k;

    rc = funcs->C_EncryptFinal(session, &crypt[k], &crypt_len);
    if (rc != CKR_OK) {
        testcase_error("C_EncryptFinal rc=%s", p11_get_ckr(rc));
        goto error;
    }

    crypt_len += k;

    /** initialize multipart decryption **/
    rc = funcs->C_DecryptInit(session, &mech, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_DecryptInit rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** do multipart decryption **/
    i = k = 0;                  // i indexes source buffer
    // k indexes destination buffer
    decrypt_len = sizeof(decrypt);

    while (i < crypt_len) {
        tmp = decrypt_len - k;  // room left in mpdecrypt
        rc = funcs->C_DecryptUpdate(session,
                                    &crypt[i],
                                    DES_BLOCK_SIZE, &decrypt[k], &tmp);
        if (rc != CKR_OK) {
            testcase_error("C_DecryptUpdate rc=%s", p11_get_ckr(rc));
            goto error;
        }
        k += tmp;
        i += DES_BLOCK_SIZE;
    }

    decrypt_len = sizeof(decrypt) - k;

    /** finalize multipart decryption **/
    rc = funcs->C_DecryptFinal(session, &decrypt[k], &decrypt_len);
    if (rc != CKR_OK) {
        testcase_error("C_DecryptFinal rc=%s", p11_get_ckr(rc));
        return rc;
    }

    decrypt_len += k;

    /** compare encrypt/decrypt results with original data **/
    testcase_new_assertion();

    if (decrypt_len != original_len) {
        testcase_fail("decrypted multipart data length does not "
                      "match original data length.\nexpected length=%ld,"
                      " but found length=%ld\n", original_len, decrypt_len);
    } else if (memcmp(decrypt, original, original_len)) {
        testcase_fail("decrypted data does not match original " "data");
    } else {
        testcase_pass("%s Multipart Encryption/Decryption with key"
                      "generation test passed.", tsuite->name);
    }

    /** clean up **/
    rc = funcs->C_DestroyObject(session, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
    }

    goto testcase_cleanup;

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

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

    return rc;
}

/** Tests DES encryption & decryption with wrapped/unwrapped keys. **/
CK_RV do_WrapUnwrapDES(struct generated_test_suite_info * tsuite)
{
    unsigned int j;
    CK_BYTE expected[BIG_REQUEST + DES_BLOCK_SIZE];
    CK_BYTE actual[BIG_REQUEST + DES_BLOCK_SIZE];
    CK_ULONG expected_len, actual_len, cipher_len;

    CK_BYTE wrapped_data[BIG_REQUEST + DES_BLOCK_SIZE];
    CK_BYTE user_pin[PKCS11_MAX_PIN_LEN];
    CK_SESSION_HANDLE session;
    CK_MECHANISM mechkey, mech;
    CK_OBJECT_HANDLE h_key;
    CK_OBJECT_HANDLE w_key;
    CK_OBJECT_HANDLE uw_key;
    CK_ULONG wrapped_data_len;
    CK_ULONG user_pin_len;
    CK_ULONG tmpl_count = 2;
    CK_FLAGS flags;
    CK_RV rc;
    CK_OBJECT_CLASS key_class = CKO_SECRET_KEY;
    CK_KEY_TYPE key_type = CKK_DES;
    CK_SLOT_ID slot_id = SLOT_ID;

    CK_ATTRIBUTE template[] = {
        {CKA_CLASS, &key_class, sizeof(key_class)}
        ,
        {CKA_KEY_TYPE, &key_type, sizeof(key_type)}
    };

    /** begin test **/
    testcase_begin("%s Wrap/Unwrap key test.", tsuite->name);
    testcase_rw_session();
    testcase_user_login();

    /** skip test if the slot doesn't support this mechanism **/
    if (!mech_supported(slot_id, tsuite->mech.mechanism)) {
        testcase_skip("Slot %u doesn't support %s (%u)",
                      (unsigned int) slot_id,
                      mech_to_str(tsuite->mech.mechanism),
                      (unsigned int) tsuite->mech.mechanism);
        goto testcase_cleanup;
    }
    if (!wrap_supported(slot_id, tsuite->mech)) {
        testcase_skip("Slot %u doesn't support %s (%u)",
                      (unsigned int) slot_id,
                      mech_to_str(tsuite->mech.mechanism),
                      (unsigned int) tsuite->mech.mechanism);
        goto testcase_cleanup;
    }

    /** clear buffers **/
    memset(actual, 0, sizeof(actual));
    memset(expected, 0, sizeof(expected));

    /** generate data **/
    actual_len = expected_len = BIG_REQUEST;
    cipher_len = BIG_REQUEST + DES_BLOCK_SIZE;
    for (j = 0; j < actual_len; j++) {
        actual[j] = j % 255;
        expected[j] = j % 255;
    }

    /** set mechkey for key generation **/
    mechkey = des_keygen;

    /** generate a DES Key **/
    rc = funcs->C_GenerateKey(session, &mechkey, NULL, 0, &h_key);
    if (rc != CKR_OK) {
        testcase_error("C_GenerateKey rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** generate wrapping key **/
    rc = funcs->C_GenerateKey(session, &mechkey, NULL, 0, &w_key);
    if (rc != CKR_OK) {
        testcase_error("C_GenerateKey rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** set mech for crypto**/
    mech = tsuite->mech;

    /** initialize single encryption **/
    rc = funcs->C_EncryptInit(session, &mech, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_EncryptInit rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** do single encryption **/
    rc = funcs->C_Encrypt(session, actual, actual_len, actual, &cipher_len);
    if (rc != CKR_OK) {
        testcase_error("C_Encrypt rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** wrap key **/
    wrapped_data_len = sizeof(wrapped_data);
    rc = funcs->C_WrapKey(session,
                          &mech,
                          w_key,
                          h_key, (CK_BYTE *) & wrapped_data, &wrapped_data_len);
    if (rc != CKR_OK) {
        testcase_error("C_WrapKey rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** unwrap key **/
    rc = funcs->C_UnwrapKey(session, &mech, w_key, wrapped_data,
                            wrapped_data_len, template, tmpl_count, &uw_key);
    if (rc != CKR_OK) {
        testcase_error("C_UnwrapKey rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** decrypt data with unwrapped key **/
    rc = funcs->C_DecryptInit(session, &mech, uw_key);
    if (rc != CKR_OK) {
        testcase_error("C_DecryptInit rc=%s", p11_get_ckr(rc));
        goto error;
    }

    rc = funcs->C_Decrypt(session, actual, cipher_len, actual, &actual_len);
    if (rc != CKR_OK) {
        testcase_error("C_Decrypt rc=%s", p11_get_ckr(rc));
        goto error;
    }

    /** compare encrypted/decrypted data with original data **/
    testcase_new_assertion();

    if (actual_len != expected_len) {
        testcase_fail("expected length=%ld, but found length=%ld"
                      "\n", expected_len, actual_len);
        rc = CKR_GENERAL_ERROR;
    } else if (memcmp(actual, expected, actual_len)) {
        testcase_fail("decrypted data does not match plaintext " "data.");
        rc = CKR_GENERAL_ERROR;
    } else {
        testcase_pass("DES Wrap/UnWrap test for %s passed.", tsuite->name);
    }

    /** clean up **/
    rc = funcs->C_DestroyObject(session, h_key);
    if (rc != CKR_OK) {
        testcase_error("C_DestroyObject rc=%s", p11_get_ckr(rc));
    }
    goto testcase_cleanup;

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

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

    return rc;
}

CK_RV des_funcs()
{
    int i;
    CK_RV rv;

    /** published (known answer) tests **/
    for (i = 0; i < NUM_OF_PUBLISHED_TESTSUITES; i++) {
        rv = do_EncryptDES(&published_test_suites[i]);
        if (rv != CKR_OK && (!no_stop))
            break;

        rv = do_DecryptDES(&published_test_suites[i]);
        if (rv != CKR_OK && (!no_stop))
            break;

        rv = do_EncryptUpdateDES(&published_test_suites[i]);
        if (rv != CKR_OK && (!no_stop))
            break;

        rv = do_DecryptUpdateDES(&published_test_suites[i]);
        if (rv != CKR_OK && (!no_stop))
            break;
    }

    /** generated tests **/
    for (i = 0; i < NUM_OF_GENERATED_TESTSUITES; i++) {
        rv = do_WrapUnwrapDES(&generated_test_suites[i]);
        if (rv != CKR_OK && (!no_stop))
            break;
        rv = do_EncryptDecryptDES(&generated_test_suites[i]);
        if (rv != CKR_OK && (!no_stop))
            break;
        rv = do_EncryptDecryptUpdateDES(&generated_test_suites[i]);
        if (rv != CKR_OK && (!no_stop))
            break;
    }

    return rv;
}

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

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

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

    rc = do_GetFunctionList();
    if (!rc) {
        testcase_error("do_GetFunctionList(), rc=%s", p11_get_ckr(rc));
        return rc;
    }

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

    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;
        }
    }
    rc = 0;
    testcase_setup(0);          //TODO
    rc = des_funcs();
    testcase_print_result();

    funcs->C_Finalize(NULL);

    return rc;
}
