/*
 * Copyright (c) 2017 Nordic Semiconductor ASA
 * Copyright (c) 2015-2016 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <string.h>
#include <errno.h>

#include <zephyr.h>
#include <misc/byteorder.h>

#include <bluetooth.h>
#include <hci_host.h>
#include <conn.h>

#include <constants.h>
#include <hmac_prng.h>
#include <aes.h>
#include <utils.h>

#define BT_DBG_ENABLED  IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
#define LOG_MODULE_NAME bt_crypto
#include "log.h"

#include "hci_core.h"

static struct tc_hmac_prng_struct prng;

static int prng_reseed(struct tc_hmac_prng_struct *h)
{
    u8_t seed[32];
    s64_t extra;
    int ret, i;

    for (i = 0; i < (sizeof(seed) / 8); i++) {
        struct bt_hci_rp_le_rand *rp;
        struct net_buf *rsp;

        ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_RAND, NULL, &rsp);
        if (ret) {
            return ret;
        }

        rp = (void *)rsp->data;
        memcpy(&seed[i * 8], rp->rand, 8);

        net_buf_unref(rsp);
    }

    extra = k_uptime_get();

    ret = tc_hmac_prng_reseed(h, seed, sizeof(seed), (u8_t *)&extra,
                              sizeof(extra));
    if (ret == TC_CRYPTO_FAIL) {
        BT_ERR("Failed to re-seed PRNG");
        return -EIO;
    }

    return 0;
}

int prng_init(void)
{
    struct bt_hci_rp_le_rand *rp;
    struct net_buf *rsp;
    int ret;

    /* Check first that HCI_LE_Rand is supported */
    if (!BT_CMD_TEST(bt_dev.supported_commands, 27, 7)) {
        return -ENOTSUP;
    }

    ret = bt_hci_cmd_send_sync(BT_HCI_OP_LE_RAND, NULL, &rsp);
    if (ret) {
        return ret;
    }

    rp = (void *)rsp->data;

    ret = tc_hmac_prng_init(&prng, rp->rand, sizeof(rp->rand));

    net_buf_unref(rsp);

    if (ret == TC_CRYPTO_FAIL) {
        BT_ERR("Failed to initialize PRNG");
        return -EIO;
    }

    /* re-seed is needed after init */
    return prng_reseed(&prng);
}

int bt_rand(void *buf, size_t len)
{
#if !defined(CONFIG_BT_GEN_RANDOM_BY_SW)
    k_get_random_byte_array(buf, len);
    return 0;
#else
    int ret;
    ret = tc_hmac_prng_generate(buf, len, &prng);
    if (ret == TC_HMAC_PRNG_RESEED_REQ) {
        ret = prng_reseed(&prng);
        if (ret) {
            return ret;
        }

        ret = tc_hmac_prng_generate(buf, len, &prng);
    }

    if (ret == TC_CRYPTO_SUCCESS) {
        return 0;
    }

    return -EIO;
#endif
}

int bt_encrypt_le(const u8_t key[16], const u8_t plaintext[16],
                  u8_t enc_data[16])
{
    struct tc_aes_key_sched_struct s;
    u8_t tmp[16];

    BT_DBG("key %s", bt_hex(key, 16));
    BT_DBG("plaintext %s", bt_hex(plaintext, 16));

    sys_memcpy_swap(tmp, key, 16);

    if (tc_aes128_set_encrypt_key(&s, tmp) == TC_CRYPTO_FAIL) {
        return -EINVAL;
    }

    sys_memcpy_swap(tmp, plaintext, 16);

    if (tc_aes_encrypt(enc_data, tmp, &s) == TC_CRYPTO_FAIL) {
        return -EINVAL;
    }

    sys_mem_swap(enc_data, 16);

    BT_DBG("enc_data %s", bt_hex(enc_data, 16));

    return 0;
}

int bt_encrypt_be(const u8_t key[16], const u8_t plaintext[16],
                  u8_t enc_data[16])
{
    struct tc_aes_key_sched_struct s;

    BT_DBG("key %s", bt_hex(key, 16));
    BT_DBG("plaintext %s", bt_hex(plaintext, 16));

    if (tc_aes128_set_encrypt_key(&s, key) == TC_CRYPTO_FAIL) {
        return -EINVAL;
    }

    if (tc_aes_encrypt(enc_data, plaintext, &s) == TC_CRYPTO_FAIL) {
        return -EINVAL;
    }

    BT_DBG("enc_data %s", bt_hex(enc_data, 16));

    return 0;
}
