blob: 195dcd35ddc14c0b4da01e5d509e85fea4043fea [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include <stdint.h>
#include "mcu/mcu.h"
#include "nimble/ble.h"
#include "controller/ble_hw.h"
#include "CMAC.h"
#include <ipc_cmac/rand.h>
#include "tinycrypt/aes.h"
static struct tc_aes_key_sched_struct g_ctx;
int
ble_hw_rng_init(ble_rng_isr_cb_t cb, int bias)
{
cmac_rand_set_isr_cb(cb);
return 0;
}
int
ble_hw_rng_start(void)
{
/* Chime the M33 in case we need random numbers generated */
cmac_rand_start();
CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV1C_CMAC2SYS_IRQ_SET_Msk;
return 0;
}
int
ble_hw_rng_stop(void)
{
cmac_rand_stop();
return 0;
}
#define BLE_HW_RESOLV_LIST_SIZE (MYNEWT_VAL(BLE_LL_RESOLV_LIST_SIZE))
struct ble_hw_resolv_irk {
uint32_t key[4];
};
struct ble_hw_resolv_list {
uint8_t count;
struct ble_hw_resolv_irk irk[BLE_HW_RESOLV_LIST_SIZE];
};
struct ble_hw_resolv_proc {
uint32_t hash;
uint8_t f_configured;
uint8_t f_active;
uint8_t f_match;
uint8_t f_done;
struct ble_hw_resolv_irk *irk;
struct ble_hw_resolv_irk *irk_end;
uint32_t crypto_prand_in[4];
uint32_t crypto_e_out[4];
};
static struct ble_hw_resolv_list g_ble_hw_resolv_list;
static struct ble_hw_resolv_proc g_ble_hw_resolv_proc;
int
ble_hw_get_public_addr(ble_addr_t *addr)
{
return -1;
}
int
ble_hw_get_static_addr(ble_addr_t *addr)
{
return -1;
}
void
ble_hw_whitelist_clear(void)
{
}
int
ble_hw_whitelist_add(const uint8_t *addr, uint8_t addr_type)
{
return 0;
}
void
ble_hw_whitelist_rmv(const uint8_t *addr, uint8_t addr_type)
{
}
uint8_t
ble_hw_whitelist_size(void)
{
return 0;
}
void
ble_hw_whitelist_enable(void)
{
}
void
ble_hw_whitelist_disable(void)
{
}
int
ble_hw_whitelist_match(void)
{
return 0;
}
int
ble_hw_encrypt_block(struct ble_encryption_block *ecb)
{
uint32_t in_addr;
uint32_t out_addr;
/*
* The following code bears some explanation. This function is called by
* the LL task to encrypt blocks and calculate session keys. Address
* resolution also calls this function. Furthermore, during connections,
* the M0 crypto accelerator is used but this function is not called when
* using it. During the entire connection event, the M0 crypto block cannot
* be used as the crypto state (some of it) needs to remain un-changed.
* Note that this is also true when address resolution is enabled: the
* HW crypto block is set up and cannot be modified.
*
* Rather than attempt to share the M0 crypto block between the various
* controller features which require it, we decided to use software to
* perform the encryption task for anything being done at the link-layer
* (outside of an ISR). If this function is called inside an ISR, and it
* is when resolving addresses, the crypto accelerator is not being used
* by a connection event. Thus, we check to see if we are inside of an ISR.
* If so, we use the M0 crypto block. If outside of an ISR, we use the M33
*/
if (!os_arch_in_isr()) {
tc_aes128_set_encrypt_key(&g_ctx, ecb->key);
tc_aes_encrypt(ecb->cipher_text, ecb->plain_text, &g_ctx);
return 0;
}
/* Need to retain state of in/out pointers */
in_addr = CMAC->CM_CRYPTO_IN_ADR2_REG;
out_addr = CMAC->CM_CRYPTO_OUT_ADR_REG;
while (CMAC->CM_CRYPTO_STAT_REG & CMAC_CM_CRYPTO_STAT_REG_CM_CRYPTO_BUSY_Msk);
/* RECB, memory in/out, encryption */
CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ECB_ENC_EN_Msk |
CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_IN_SEL_Msk |
CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_OUT_SEL_Msk |
CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ENC_DECN_Msk;
CMAC->CM_CRYPTO_KEY_31_0_REG = get_le32(&ecb->key[0]);
CMAC->CM_CRYPTO_KEY_63_32_REG = get_le32(&ecb->key[4]);
CMAC->CM_CRYPTO_KEY_95_64_REG = get_le32(&ecb->key[8]);
CMAC->CM_CRYPTO_KEY_127_96_REG = get_le32(&ecb->key[12]);
CMAC->CM_CRYPTO_IN_ADR2_REG = (uint32_t)ecb->plain_text;
CMAC->CM_CRYPTO_OUT_ADR_REG = (uint32_t)ecb->cipher_text;
CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk;
CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV_CRYPTO_START_Msk;
while (!(CMAC->CM_EXC_STAT_REG & CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk));
CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk;
CMAC->CM_CRYPTO_IN_ADR2_REG = in_addr;
CMAC->CM_CRYPTO_OUT_ADR_REG = out_addr;
return 0;
}
void
ble_hw_resolv_list_clear(void)
{
g_ble_hw_resolv_list.count = 0;
}
int
ble_hw_resolv_list_add(uint8_t *irk)
{
struct ble_hw_resolv_irk *e;
if (g_ble_hw_resolv_list.count == BLE_HW_RESOLV_LIST_SIZE) {
return BLE_ERR_MEM_CAPACITY;
}
e = &g_ble_hw_resolv_list.irk[g_ble_hw_resolv_list.count];
/* Prepare key here so we do not need to do it during resolving */
e->key[0] = get_le32(&irk[0]);
e->key[1] = get_le32(&irk[4]);
e->key[2] = get_le32(&irk[8]);
e->key[3] = get_le32(&irk[12]);
g_ble_hw_resolv_list.count++;
return BLE_ERR_SUCCESS;
}
void
ble_hw_resolv_list_rmv(int index)
{
struct ble_hw_resolv_irk *e;
if (index < g_ble_hw_resolv_list.count) {
g_ble_hw_resolv_list.count--;
e = &g_ble_hw_resolv_list.irk[index];
memmove(e, e + 1, (g_ble_hw_resolv_list.count - index) * sizeof(e->key));
}
}
uint8_t
ble_hw_resolv_list_size(void)
{
return BLE_HW_RESOLV_LIST_SIZE;
}
int
ble_hw_resolv_list_match(void)
{
return g_ble_hw_resolv_proc.f_match ?
g_ble_hw_resolv_proc.irk - g_ble_hw_resolv_list.irk : -1;
}
static void
ble_hw_resolv_proc_next(void)
{
void *src = &g_ble_hw_resolv_proc.irk->key;
if (g_ble_hw_resolv_proc.irk == g_ble_hw_resolv_proc.irk_end) {
g_ble_hw_resolv_proc.f_done = 1;
g_ble_hw_resolv_proc.f_active = 0;
} else {
__asm__ volatile (".syntax unified \n"
" ldm %[ptr]!, {r1, r2, r3, r4} \n"
" ldr %[ptr], =%[reg] \n"
" stm %[ptr]!, {r1, r2, r3, r4} \n"
: [ptr] "+l" (src)
: [reg] "i" (&CMAC->CM_CRYPTO_KEY_31_0_REG)
: "r1", "r2", "r3", "r4", "memory");
CMAC->CM_EV_SET_REG = CMAC_CM_EV_SET_REG_EV_CRYPTO_START_Msk;
}
}
void
ble_hw_resolv_proc_enable(void)
{
assert(!g_ble_hw_resolv_proc.f_active);
CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_SW_REQ_ABORT_Msk;
CMAC->CM_CRYPTO_CTRL_REG = CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ECB_ENC_EN_Msk |
CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_IN_SEL_Msk |
CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_OUT_SEL_Msk |
CMAC_CM_CRYPTO_CTRL_REG_CM_CRYPTO_ENC_DECN_Msk;
CMAC->CM_CRYPTO_IN_ADR2_REG = (uint32_t)g_ble_hw_resolv_proc.crypto_prand_in;
CMAC->CM_CRYPTO_OUT_ADR_REG = (uint32_t)g_ble_hw_resolv_proc.crypto_e_out;
g_ble_hw_resolv_proc.irk = g_ble_hw_resolv_list.irk;
g_ble_hw_resolv_proc.irk_end = g_ble_hw_resolv_list.irk +
g_ble_hw_resolv_list.count;
g_ble_hw_resolv_proc.f_configured = 1;
g_ble_hw_resolv_proc.f_active = 0;
/*
* It would be better to enable IRQ in ble_hw_resolv_proc_start, but this
* would introduce a bit of latency when starting resolving procedure and
* we need to save every us possible there in order to be able to resolve
* RPA on time.
*/
NVIC_ClearPendingIRQ(CRYPTO_IRQn);
NVIC_EnableIRQ(CRYPTO_IRQn);
}
void
ble_hw_resolv_proc_disable(void)
{
g_ble_hw_resolv_proc.f_configured = 0;
g_ble_hw_resolv_proc.f_active = 0;
g_ble_hw_resolv_proc.f_match = 0;
g_ble_hw_resolv_proc.f_done = 1;
NVIC_DisableIRQ(CRYPTO_IRQn);
}
void
ble_hw_resolv_proc_reset(const uint8_t *addr)
{
g_ble_hw_resolv_proc.f_match = 0;
}
void
ble_hw_resolv_proc_start(const uint8_t *addr)
{
assert(g_ble_hw_resolv_proc.f_configured);
/* crypto_prand_in is already zeroed so prand is properly padded */
g_ble_hw_resolv_proc.crypto_prand_in[3] = get_be24(&addr[3]) << 8;
g_ble_hw_resolv_proc.hash = get_be24(&addr[0]);
g_ble_hw_resolv_proc.f_match = 0;
g_ble_hw_resolv_proc.f_done = 0;
g_ble_hw_resolv_proc.f_active = 1;
ble_hw_resolv_proc_next();
}
void
CRYPTO_IRQHandler(void)
{
uint32_t hash;
CMAC->CM_EXC_STAT_REG = CMAC_CM_EXC_STAT_REG_EXC_CRYPTO_Msk;
hash = g_ble_hw_resolv_proc.crypto_e_out[3] >> 8;
if (g_ble_hw_resolv_proc.hash == hash) {
g_ble_hw_resolv_proc.f_active = 0;
g_ble_hw_resolv_proc.f_match = 1;
g_ble_hw_resolv_proc.f_done = 1;
} else {
g_ble_hw_resolv_proc.irk++;
ble_hw_resolv_proc_next();
}
}