/****************************************************************************** * * Copyright 2008-2012 Broadcom Corporation * * Licensed 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. * ******************************************************************************/ /****************************************************************************** * * This file contains the implementation of the AES128 CMAC algorithm. * ******************************************************************************/ #include "bt_target.h" #include <stdio.h> #include <string.h> #include "btm_ble_api.h" #include "hcimsgs.h" #include "smp_int.h" typedef struct { uint8_t* text; uint16_t len; uint16_t round; } tCMAC_CB; tCMAC_CB cmac_cb; /* Rb for AES-128 as block cipher, LSB as [0] */ BT_OCTET16 const_Rb = {0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; void print128(BT_OCTET16 x, const uint8_t* key_name) { #if (SMP_DEBUG == TRUE && SMP_DEBUG_VERBOSE == TRUE) uint8_t* p = (uint8_t*)x; uint8_t i; SMP_TRACE_WARNING("%s(MSB ~ LSB) = ", key_name); for (i = 0; i < 4; i++) { SMP_TRACE_WARNING("%02x %02x %02x %02x", p[BT_OCTET16_LEN - i * 4 - 1], p[BT_OCTET16_LEN - i * 4 - 2], p[BT_OCTET16_LEN - i * 4 - 3], p[BT_OCTET16_LEN - i * 4 - 4]); } #endif } /******************************************************************************* * * Function padding * * Description utility function to padding the given text to be a 128 bits * data. The parameter dest is input and output parameter, it * must point to a BT_OCTET16_LEN memory space; where include * length bytes valid data. * * Returns void * ******************************************************************************/ static void padding(BT_OCTET16 dest, uint8_t length) { uint8_t i, *p = dest; /* original last block */ for (i = length; i < BT_OCTET16_LEN; i++) p[BT_OCTET16_LEN - i - 1] = (i == length) ? 0x80 : 0; } /******************************************************************************* * * Function leftshift_onebit * * Description utility function to left shift one bit for a 128 bits value. * * Returns void * ******************************************************************************/ static void leftshift_onebit(uint8_t* input, uint8_t* output) { uint8_t i, overflow = 0, next_overflow = 0; SMP_TRACE_EVENT("leftshift_onebit "); /* input[0] is LSB */ for (i = 0; i < BT_OCTET16_LEN; i++) { next_overflow = (input[i] & 0x80) ? 1 : 0; output[i] = (input[i] << 1) | overflow; overflow = next_overflow; } return; } /******************************************************************************* * * Function cmac_aes_cleanup * * Description clean up function for AES_CMAC algorithm. * * Returns void * ******************************************************************************/ static void cmac_aes_cleanup(void) { osi_free(cmac_cb.text); memset(&cmac_cb, 0, sizeof(tCMAC_CB)); } /******************************************************************************* * * Function cmac_aes_k_calculate * * Description This function is the calculation of block cipher using * AES-128. * * Returns void * ******************************************************************************/ static bool cmac_aes_k_calculate(BT_OCTET16 key, uint8_t* p_signature, uint16_t tlen) { tSMP_ENC output; uint8_t i = 1, err = 0; uint8_t x[16] = {0}; uint8_t* p_mac; SMP_TRACE_EVENT("cmac_aes_k_calculate "); while (i <= cmac_cb.round) { smp_xor_128(&cmac_cb.text[(cmac_cb.round - i) * BT_OCTET16_LEN], x); /* Mi' := Mi (+) X */ if (!SMP_Encrypt(key, BT_OCTET16_LEN, &cmac_cb.text[(cmac_cb.round - i) * BT_OCTET16_LEN], BT_OCTET16_LEN, &output)) { err = 1; break; } memcpy(x, output.param_buf, BT_OCTET16_LEN); i++; } if (!err) { p_mac = output.param_buf + (BT_OCTET16_LEN - tlen); memcpy(p_signature, p_mac, tlen); SMP_TRACE_DEBUG("tlen = %d p_mac = %d", tlen, p_mac); SMP_TRACE_DEBUG( "p_mac[0] = 0x%02x p_mac[1] = 0x%02x p_mac[2] = 0x%02x p_mac[3] = " "0x%02x", *p_mac, *(p_mac + 1), *(p_mac + 2), *(p_mac + 3)); SMP_TRACE_DEBUG( "p_mac[4] = 0x%02x p_mac[5] = 0x%02x p_mac[6] = 0x%02x p_mac[7] = " "0x%02x", *(p_mac + 4), *(p_mac + 5), *(p_mac + 6), *(p_mac + 7)); return true; } else return false; } /******************************************************************************* * * Function cmac_prepare_last_block * * Description This function proceeed to prepare the last block of message * Mn depending on the size of the message. * * Returns void * ******************************************************************************/ static void cmac_prepare_last_block(BT_OCTET16 k1, BT_OCTET16 k2) { // uint8_t x[16] = {0}; bool flag; SMP_TRACE_EVENT("cmac_prepare_last_block "); /* last block is a complete block set flag to 1 */ flag = ((cmac_cb.len % BT_OCTET16_LEN) == 0 && cmac_cb.len != 0) ? true : false; SMP_TRACE_WARNING("flag = %d round = %d", flag, cmac_cb.round); if (flag) { /* last block is complete block */ smp_xor_128(&cmac_cb.text[0], k1); } else /* padding then xor with k2 */ { padding(&cmac_cb.text[0], (uint8_t)(cmac_cb.len % 16)); smp_xor_128(&cmac_cb.text[0], k2); } } /******************************************************************************* * * Function cmac_subkey_cont * * Description This is the callback function when CIPHk(0[128]) is * completed. * * Returns void * ******************************************************************************/ static void cmac_subkey_cont(tSMP_ENC* p) { uint8_t k1[BT_OCTET16_LEN], k2[BT_OCTET16_LEN]; uint8_t* pp = p->param_buf; SMP_TRACE_EVENT("cmac_subkey_cont "); print128(pp, (const uint8_t*)"K1 before shift"); /* If MSB(L) = 0, then K1 = L << 1 */ if ((pp[BT_OCTET16_LEN - 1] & 0x80) != 0) { /* Else K1 = ( L << 1 ) (+) Rb */ leftshift_onebit(pp, k1); smp_xor_128(k1, const_Rb); } else { leftshift_onebit(pp, k1); } if ((k1[BT_OCTET16_LEN - 1] & 0x80) != 0) { /* K2 = (K1 << 1) (+) Rb */ leftshift_onebit(k1, k2); smp_xor_128(k2, const_Rb); } else { /* If MSB(K1) = 0, then K2 = K1 << 1 */ leftshift_onebit(k1, k2); } print128(k1, (const uint8_t*)"K1"); print128(k2, (const uint8_t*)"K2"); cmac_prepare_last_block(k1, k2); } /******************************************************************************* * * Function cmac_generate_subkey * * Description This is the function to generate the two subkeys. * * Parameters key - CMAC key, expect SRK when used by SMP. * * Returns void * ******************************************************************************/ static bool cmac_generate_subkey(BT_OCTET16 key) { BT_OCTET16 z = {0}; bool ret = true; tSMP_ENC output; SMP_TRACE_EVENT(" cmac_generate_subkey"); if (SMP_Encrypt(key, BT_OCTET16_LEN, z, BT_OCTET16_LEN, &output)) { cmac_subkey_cont(&output); ; } else ret = false; return ret; } /******************************************************************************* * * Function aes_cipher_msg_auth_code * * Description This is the AES-CMAC Generation Function with tlen * implemented. * * Parameters key - CMAC key in little endian order, expect SRK when used * by SMP. * input - text to be signed in little endian byte order. * length - length of the input in byte. * tlen - lenth of mac desired * p_signature - data pointer to where signed data to be * stored, tlen long. * * Returns false if out of resources, true in other cases. * ******************************************************************************/ bool aes_cipher_msg_auth_code(BT_OCTET16 key, uint8_t* input, uint16_t length, uint16_t tlen, uint8_t* p_signature) { uint16_t len, diff; uint16_t n = (length + BT_OCTET16_LEN - 1) / BT_OCTET16_LEN; /* n is number of rounds */ bool ret = false; SMP_TRACE_EVENT("%s", __func__); if (n == 0) n = 1; len = n * BT_OCTET16_LEN; SMP_TRACE_WARNING("AES128_CMAC started, allocate buffer size = %d", len); /* allocate a memory space of multiple of 16 bytes to hold text */ cmac_cb.text = (uint8_t*)osi_calloc(len); cmac_cb.round = n; diff = len - length; if (input != NULL && length > 0) { memcpy(&cmac_cb.text[diff], input, (int)length); cmac_cb.len = length; } else { cmac_cb.len = 0; } /* prepare calculation for subkey s and last block of data */ if (cmac_generate_subkey(key)) { /* start calculation */ ret = cmac_aes_k_calculate(key, p_signature, tlen); } /* clean up */ cmac_aes_cleanup(); return ret; }