#include "pkcs11_init.h"
#include "pkcs11_digest.h"
#include "pkcs11_object.h"
#include "pkcs11_session.h"
#include "pkcs11_util.h"
#include "cryptoauthlib.h"

#if !PKCS11_HARDWARE_SHA256
#include "crypto/atca_crypto_sw_sha2.h"
static atcac_sha2_256_ctx pkcs11_digest_ctx;
static bool pkcs11_digest_initalized;
#endif

/**
 * \brief Initializes a message-digesting operation using the specified mechanism in the specified session
 */
CK_RV pkcs11_digest_init(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism)
{
    pkcs11_session_ctx_ptr pSession;
    CK_RV rv;

    rv = pkcs11_init_check(NULL_PTR, FALSE);
    if (rv)
    {
        return rv;
    }

    if (!pMechanism)
    {
        return CKR_ARGUMENTS_BAD;
    }

    if (CKM_SHA256 != pMechanism->mechanism)
    {
        return CKR_MECHANISM_INVALID;
    }

    rv = pkcs11_session_check(&pSession, hSession);
    if (rv)
    {
        return rv;
    }
#if PKCS11_HARDWARE_SHA256
    return CKR_FUNCTION_NOT_SUPPORTED;
#else
    rv = atcac_sw_sha2_256_init(&pkcs11_digest_ctx);

    if (ATCA_SUCCESS == rv)
    {
        pkcs11_digest_initalized = TRUE;
    }

    return pkcs11_util_convert_rv(rv);
#endif
}

/**
 * \brief Digest the specified data in a one-pass operation and return the resulting digest
 */
CK_RV pkcs11_digest(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen)
{
    pkcs11_session_ctx_ptr pSession;
    CK_RV rv;

    rv = pkcs11_init_check(NULL_PTR, FALSE);
    if (rv)
    {
        return rv;
    }

    if (!pData || !ulDataLen || !pulDigestLen)
    {
        return CKR_ARGUMENTS_BAD;
    }

    if (!pDigest)
    {
        *pulDigestLen = ATCA_SHA2_256_DIGEST_SIZE;
        return CKR_OK;
    }
    else if (ATCA_SHA2_256_DIGEST_SIZE > *pulDigestLen)
    {
        return CKR_BUFFER_TOO_SMALL;
    }

    rv = pkcs11_session_check(&pSession, hSession);
    if (rv)
    {
        return rv;
    }

#if PKCS11_HARDWARE_SHA256
    ((void)hSession);
    ((void)pData);
    ((void)ulDataLen);
    ((void)pDigest);
    ((void)pulDigestLen);

    return CKR_FUNCTION_NOT_SUPPORTED;
#else
    if (!pkcs11_digest_initalized)
    {
        return CKR_OPERATION_NOT_INITIALIZED;
    }

    rv = atcac_sw_sha2_256(pData, ulDataLen, pDigest);

    pkcs11_digest_initalized = FALSE;

    return pkcs11_util_convert_rv(rv);
#endif
}

/**
 * \brief Continues a multiple-part digesting operation
 */
CK_RV pkcs11_digest_update(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen)
{
    pkcs11_session_ctx_ptr pSession;
    CK_RV rv;

    rv = pkcs11_init_check(NULL_PTR, FALSE);
    if (rv)
    {
        return rv;
    }

    if (!pPart || !ulPartLen)
    {
        return CKR_ARGUMENTS_BAD;
    }

    rv = pkcs11_session_check(&pSession, hSession);
    if (rv)
    {
        return rv;
    }

#if PKCS11_HARDWARE_SHA256
    ((void)hSession);
    ((void)pPart);
    ((void)ulPartLen);

    return CKR_FUNCTION_NOT_SUPPORTED;
#else
    if (!pkcs11_digest_initalized)
    {
        return CKR_OPERATION_NOT_INITIALIZED;
    }

    rv = atcac_sw_sha2_256_update(&pkcs11_digest_ctx, pPart, ulPartLen);

    return pkcs11_util_convert_rv(rv);
#endif

}

/**
 * \brief Finishes a multiple-part digesting operation
 */
CK_RV pkcs11_digest_final(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen)
{
    pkcs11_session_ctx_ptr pSession;
    CK_RV rv;

    rv = pkcs11_init_check(NULL_PTR, FALSE);
    if (rv)
    {
        return rv;
    }

    if (!pulDigestLen)
    {
        return CKR_ARGUMENTS_BAD;
    }

    if (!pDigest)
    {
        *pulDigestLen = ATCA_SHA2_256_DIGEST_SIZE;
        return CKR_OK;
    }
    else if (ATCA_SHA2_256_DIGEST_SIZE > *pulDigestLen)
    {
        return CKR_BUFFER_TOO_SMALL;
    }

    rv = pkcs11_session_check(&pSession, hSession);
    if (rv)
    {
        return rv;
    }

#if PKCS11_HARDWARE_SHA256
    ((void)hSession);
    ((void)pDigest);
    ((void)pulDigestLen);

    return CKR_FUNCTION_NOT_SUPPORTED;
#else
    if (!pkcs11_digest_initalized)
    {
        return CKR_OPERATION_NOT_INITIALIZED;
    }

    rv = atcac_sw_sha2_256_finish(&pkcs11_digest_ctx, pDigest);

    pkcs11_digest_initalized = FALSE;

    return pkcs11_util_convert_rv(rv);
#endif
}
