/* * 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 #include #include "mcu/mcu.h" #include "nimble/ble.h" #include "controller/ble_hw.h" #include "CMAC.h" #include "cmac_driver/cmac_shared.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(); } }