/*
 * tee_hmac_sha256_digest_TA.c
 *
 * HMAC-SHA256 digest example
 *
 * NOTE: Test data for HMAC-SHA256 digest was obtained from BoringSSL project:
 *       boringssl/src/crypto/hmac_extra/hmac_tests.txt
 *       As this data is initially presented in form of hex-string the additional
 *       hex-string to byte-array conversion is used.
 *
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <tee_internal_api.h>
#include "crypto_samples_functions.h"

/*************************************************************************************************/

/* Input message */
static const char Input[] = "Sample message for keylen=blocklen";
/* Secret key */
static const char Key[] = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"
                          "202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F";
/* Pre-computed message HMAC */
static const char Output[] = "8BB9A1DB9806F20DF7F77B82138C7914D174D59E13DC4D0169C9057B133E1D62";

/* HMAC-SHA256 parameters */
#define SAMPLE_HMAC_OBJECT       TEE_TYPE_HMAC_SHA256
#define SAMPLE_HMAC_ALGO         TEE_ALG_HMAC_SHA256
#define SAMPLE_HMAC_SIZE_BIT     256
#define SAMPLE_HMAC_KEY_SIZE     ((sizeof(Key) - 1) / 2)
#define SAMPLE_HMAC_MAX_SIZE_BIT ((SAMPLE_HMAC_SIZE_BIT > (SAMPLE_HMAC_KEY_SIZE * 8)) ?\
                                   SAMPLE_HMAC_SIZE_BIT : (SAMPLE_HMAC_KEY_SIZE * 8))
#define SAMPLE_HMAC_DIGEST_SIZE   (SAMPLE_HMAC_SIZE_BIT / 8)

/* Defines message path length for MAC update example */
#define SAMPLE_MSG_INITIAL_LEN   5

/*************************************************************************************************/

/*
 * Computes message authentication code (aka message tag/digest) using HMAC SHA256
 */
TEE_Result hmac_sha256_digest()
{
    TEE_Result rc;
    TEE_Attribute attrs[1];
    TEE_OperationHandle hndl = NULL;
    TEE_ObjectHandle obj_hndl = NULL;
    /* Print buffer */
    char pbuf[1024] = "\0";
    uint8_t key[SAMPLE_HMAC_KEY_SIZE];
    uint8_t mac[SAMPLE_HMAC_DIGEST_SIZE];
    uint32_t mac_len = SAMPLE_HMAC_DIGEST_SIZE;
    uint8_t mac_precomputed[SAMPLE_HMAC_DIGEST_SIZE];

    printf("HMAC sample\n");

    /* Prepare data */
    if (hex_string2bytes(Key, key, SAMPLE_HMAC_KEY_SIZE)) {
        printf("Error: wrong format of secret key\n");
        return TEE_ERROR_GENERIC;
    }

    TEE_InitRefAttribute(&attrs[0], TEE_ATTR_SECRET_VALUE, key, SAMPLE_HMAC_KEY_SIZE);

    if ((rc = TEE_AllocateTransientObject(SAMPLE_HMAC_OBJECT, SAMPLE_HMAC_MAX_SIZE_BIT,
                                          &obj_hndl))
     || (rc = TEE_PopulateTransientObject(obj_hndl, attrs, 1))
     || (rc = TEE_AllocateOperation(&hndl, SAMPLE_HMAC_ALGO, TEE_MODE_MAC,
                                    SAMPLE_HMAC_MAX_SIZE_BIT))
     || (rc = TEE_SetOperationKey(hndl, obj_hndl))) {
        printf("Error: preparation to HMAC computing failed, rc = 0x%08x\n", rc);
        goto L_end;
    }

    printf("HMAC computing\n");
    TEE_MACInit(hndl, NULL, 0);
    /* TEE_MACUpdate function is used here in vitro / just for example. If final message
     * is prepared, it's reasonable to use TEE_MACComputeFinal for full message.
     */
    TEE_MACUpdate(hndl, Input, SAMPLE_MSG_INITIAL_LEN);
    if ((rc = TEE_MACComputeFinal(hndl, Input + SAMPLE_MSG_INITIAL_LEN,
                                  sizeof(Input) - 1 - SAMPLE_MSG_INITIAL_LEN, mac, &mac_len))) {
        printf("Error: HMAC final failed, rc = 0x%08x\n", rc);
        goto L_end;
    }

    bytes2hex_string(pbuf, mac, mac_len);
    printf("HMAC: %s\n", pbuf);

    printf("HMAC message authenticity checking\n");
    TEE_MACInit(hndl, NULL, 0);
    if ((rc = TEE_MACCompareFinal(hndl, Input, sizeof(Input) - 1, mac, mac_len))) {
        printf("Error: HMAC authentication failed, rc = 0x%08x\n", rc);
        goto L_end;
    }
    printf("HMAC is valid\n");

    printf("Comparing with pre-computed HMAC\n");
    if (hex_string2bytes(Output, mac_precomputed, SAMPLE_HMAC_DIGEST_SIZE)) {
        printf("Error: wrong format of pre-computed HMAC\n");
        rc = TEE_ERROR_GENERIC;
        goto L_end;
    }
    if ((rc = TEE_MemCompare(mac, mac_precomputed, SAMPLE_HMAC_DIGEST_SIZE))) {
        bytes2hex_string(pbuf, mac_precomputed, SAMPLE_HMAC_DIGEST_SIZE);
        printf("Pre-computed HMAC differs, rc = 0x%08x\n", rc);
        printf("HMAC: %s\n", pbuf);
        goto L_end;
    }
    printf("Pre-computed HMAC is the same\n");

L_end:

    TEE_CloseObject(obj_hndl);
    if (hndl) {
        TEE_FreeOperation(hndl);
    }

    if (rc) {
        printf("HMAC failed\n");
    } else {
        printf("HMAC passed\n");
    }

    return rc;
}
