Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ccm_security.c Source File

ccm_security.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2014-2015, 2017-2018, Arm Limited and affiliates.
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 /**
00019  *
00020  * \file ccm_security.c
00021  * \brief CCM Library API.
00022  *
00023  * \section ccm-api CCM Library API:
00024  *  - ccm_sec_init(), A function to init CCM library.
00025  *  - ccm_process_run(), A function to run configured CCM process
00026  *
00027  *  \section ccm-inctuction CCM process sequency:
00028  *  1. Init CCM library by , ccm key, ccm_sec_init()
00029  *      - security level
00030  *      - 128-bit ccm key
00031  *      - mode: AES_CCM_ENCRYPT or AES_CCM_DECRYPT
00032  *      - CCM L parameter: 2 or 3 depends nonce legth (802.15.4 use 2 and TLS security use 3)
00033  *  2. Define ADATA pointer and length, if returned global structure mic_len field is > 0
00034  *  3. Set Data pointer and length
00035  *  4. Do configured CCM process ccm_process_run()
00036  *  5. Check Return value:
00037  *      -If 0 Process ok
00038  *      -< 0 MIC fail or parameter fail
00039  */
00040 
00041 #include <stdint.h>
00042 #include <string.h>
00043 #include "ccmLIB.h"
00044 #include "platform/arm_hal_aes.h"
00045 
00046 static void ccm_generate_A0(uint8_t *ptr, ccm_globals_t *ccm_pramters);
00047 static void ccm_auth_generate_B0(uint8_t *ptr, ccm_globals_t *ccm_params);
00048 static void ccm_auth_calc_Xi(void *aes_context, uint8_t X[static 16], uint8_t Blen, const uint8_t B[static Blen]);
00049 static uint8_t ccm_mic_len_calc(uint8_t sec_level);
00050 static void ccm_encode(ccm_globals_t *ccm_params);
00051 static int8_t ccm_calc_auth_MIC(ccm_globals_t *ccm_params);
00052 
00053 /**
00054  * \brief A function to init CCM library.
00055  * \param sec_level Used CCM security level (0-7).
00056  * \param ccm_key pointer to 128-key.
00057  * \param mode AES_CCM_ENCRYPT or AES_CCM_DECRYPT
00058  * \param ccm_l cuold be 2 or 3. 2 when NONCE length is 13 and 3 when length is 12. (NONCE Len= (15-ccm_l))
00059  *
00060  * \return Pointer to Global CCM paramameter buffer.
00061  * \return 0 When parameter fail or CCM is Busy.
00062  */
00063 bool ccm_sec_init(ccm_globals_t *ccm_context, uint8_t sec_level, const uint8_t *ccm_key, uint8_t mode, uint8_t ccm_l)
00064 {
00065     memset(ccm_context, 0, sizeof(ccm_globals_t));
00066 
00067     if ((ccm_l == 2 || ccm_l == 3) && (sec_level < 8)) {
00068         void *aes_context = arm_aes_start(ccm_key);
00069         if (!aes_context) {
00070             return false;
00071         }
00072         ccm_context->aes_context = aes_context;
00073         ccm_context->ccm_encode_mode = mode;
00074         ccm_context->ccm_sec_level = sec_level;
00075         ccm_context->ccm_l_param = ccm_l;
00076         ccm_context->key_ptr = ccm_key;
00077         ccm_context->mic_len = ccm_mic_len_calc(sec_level);
00078         return true;
00079     }
00080     return false;
00081 }
00082 
00083 /**
00084  * \brief A function to init CCM library.
00085  * \param sec_level Used CCM security level (0-7).
00086  * \param ccm_key pointer to 128-key.
00087  * \param mode AES_CCM_ENCRYPT or AES_CCM_DECRYPT
00088  * \param ccm_l cuold be 2 or 3. 2 when NONCE length is 13 and 3 when length is 12. (NONCE Len= (15-ccm_l))
00089  *
00090  * \return 0 CCM process OK and when AES_CCM_DECRYPT mode was selectected also MIC was correct.
00091  * \return -1 Init have not called or data or adata pointers or lengths are zero.
00092  * \return -2 Null pointer given to function
00093  */
00094 int8_t ccm_process_run(ccm_globals_t *ccm_params)
00095 {
00096     int8_t ret_val = -1;
00097     if (ccm_params == NULL) {
00098         ret_val = -2;
00099         goto END;
00100 
00101     }
00102 
00103     if (ccm_params->mic_len) {
00104         /* data length >= 0xff00 would require different encoding */
00105         if (ccm_params->adata_len == 0 || ccm_params->adata_len >= 0xff00 || ccm_params->adata_ptr == NULL) {
00106             goto END;
00107         } else if (ccm_params->mic == NULL) {
00108             ret_val = -2;
00109             goto END;
00110         }
00111     }
00112 
00113     if (ccm_params->data_len != 0 && ccm_params->data_ptr == NULL) {
00114         ret_val = -2;
00115         goto END;
00116     }
00117 
00118     if (ccm_params->ccm_encode_mode == AES_CCM_ENCRYPT) {
00119         if (ccm_params->mic_len) {
00120             //Calc
00121             if (ccm_calc_auth_MIC(ccm_params)) {
00122                 goto END;
00123             }
00124         }
00125         if (ccm_params->data_len) {
00126             ccm_encode(ccm_params);
00127         }
00128         ret_val = 0;
00129     } else {
00130         if (ccm_params->data_len) {
00131             ccm_encode(ccm_params);
00132         }
00133         if (ccm_params->mic_len) {
00134             if (ccm_calc_auth_MIC(ccm_params) == 0) {
00135                 ret_val = 0;
00136             }
00137         } else {
00138             ret_val = 0;
00139         }
00140     }
00141 
00142 END:
00143     ccm_free(ccm_params);
00144     return ret_val;
00145 }
00146 
00147 void ccm_free(ccm_globals_t *ccm_params)
00148 {
00149     if (ccm_params && ccm_params->aes_context) {
00150         arm_aes_finish(ccm_params->aes_context);
00151     }
00152 }
00153 
00154 
00155 /* Counter-mode encryption/decryption
00156  *  Ci := E(Key, Ai) ^ Mi
00157  */
00158 static void ccm_encode(ccm_globals_t *ccm_params)
00159 {
00160     if (!ccm_params->key_ptr || ccm_params->ccm_sec_level < AES_SECURITY_LEVEL_ENC) {
00161         return;
00162     }
00163     uint16_t len = ccm_params->data_len;
00164     uint8_t *ptr = ccm_params->data_ptr;
00165     uint8_t Ai[16], Si[16];
00166 
00167     //first, generate A0
00168     ccm_generate_A0(Ai, ccm_params);
00169 
00170     while (len) {
00171         //increment counter in Ai - 16-bit increment enough; len is 16-bit
00172         if (++Ai[15] == 0) {
00173             ++Ai[14];
00174         }
00175 
00176         // Si := E(Key, Ai)
00177         arm_aes_encrypt(ccm_params->aes_context, Ai, Si);
00178 
00179         // output := Si ^ input
00180         for (int_fast8_t i = 0; i < 16 && len; i++, len--) {
00181             *ptr++ ^= Si[i];
00182         }
00183     }
00184 }
00185 
00186 
00187 static int8_t ccm_calc_auth_MIC(ccm_globals_t *ccm_params)
00188 {
00189     const uint8_t *data_ptr = ccm_params->data_ptr;
00190     uint16_t data_len = ccm_params->data_len;
00191     const uint8_t *adata_ptr = ccm_params->adata_ptr;
00192     uint16_t adata_len = ccm_params->adata_len;
00193     uint8_t Xi[16];
00194 
00195     // As a convenience, treat "data" as "adata", reflecting that "Private
00196     // Payload" is part of "a data" not "m data" for unencrypted modes.
00197     // The distinction matters because there's an "align to block" between
00198     // "a" and "m", which we don't do when it's all in "a".
00199     if (ccm_params->ccm_sec_level < AES_SECURITY_LEVEL_ENC && data_len != 0) {
00200         // This trick only works if data follows adata
00201         if (data_ptr == adata_ptr + adata_len) {
00202             adata_len += data_len;
00203             data_len = 0;
00204         } else {
00205             return -1;
00206         }
00207     }
00208 
00209     ccm_auth_generate_B0(Xi, ccm_params);  //Set B0
00210 
00211     // Calculate X1: E(key, B0)
00212     // [Could use ccm_auth_calc_Xi - it's formally X1 := E(key, B0 ^ X0), where X0 = 0]
00213     arm_aes_encrypt(ccm_params->aes_context, Xi, Xi);
00214 
00215     //First authentication block has 2-byte length field concatenated
00216     if (adata_len) {
00217         uint8_t B1[16];
00218         uint_fast8_t t_len = adata_len > 14 ? 14 : adata_len;
00219 
00220         B1[0] = adata_len >> 8;
00221         B1[1] = adata_len;
00222         memcpy(&B1[2], adata_ptr, t_len);
00223 
00224         ccm_auth_calc_Xi(ccm_params->aes_context, Xi, 2 + t_len, B1);
00225         adata_ptr += t_len;
00226         adata_len -= t_len;
00227     }
00228 
00229     while (adata_len) {
00230         uint_fast8_t t_len = adata_len > 16 ? 16 : adata_len;
00231 
00232         ccm_auth_calc_Xi(ccm_params->aes_context, Xi, t_len, adata_ptr);
00233         adata_ptr += t_len;
00234         adata_len -= t_len;
00235     }
00236 
00237     while (data_len) {
00238         uint_fast8_t t_len = data_len > 16 ? 16 : data_len;
00239 
00240         ccm_auth_calc_Xi(ccm_params->aes_context, Xi, t_len, data_ptr);
00241         data_ptr += t_len;
00242         data_len -= t_len;
00243     }
00244 
00245     // Authentication tag T is leftmost M octets of X[t+1]
00246 
00247     // Encryption block S0 is E(Key, A0)
00248     uint8_t S0[16];
00249     ccm_generate_A0(S0, ccm_params);
00250     arm_aes_encrypt(ccm_params->aes_context, S0, S0);
00251 
00252     // Encrypted authentication tag U is S0^T (leftmost M octets)
00253     if (ccm_params->ccm_encode_mode == AES_CCM_ENCRYPT) {
00254         for (uint_fast8_t i = 0; i < ccm_params->mic_len; i++) {
00255             ccm_params->mic[i] = Xi[i] ^ S0[i];
00256         }
00257     } else {
00258         for (uint_fast8_t i = 0; i < ccm_params->mic_len; i++)
00259             if (ccm_params->mic[i] != (Xi[i] ^ S0[i])) {
00260                 return -1;
00261             }
00262     }
00263     return 0;
00264 }
00265 
00266 /**
00267  * \brief This function is used to create A0 which is actual init vector to be used to encrypt full rf message.
00268  * Ai = 8-bit FLAGS | nonce | 16/24-bit counter.
00269  * Si = E[key,Ai]
00270  *
00271  * \return none.
00272  */
00273 static void ccm_generate_A0(uint8_t *ptr, ccm_globals_t *ccm_pramters)
00274 {
00275     uint8_t n_len, flags;
00276     flags = ccm_pramters->ccm_l_param - 1;
00277     n_len = 15 - ccm_pramters->ccm_l_param;
00278 
00279     //FLAGS = L' = L - 1;
00280     *ptr++ = flags;
00281     memcpy(ptr, ccm_pramters->exp_nonce, n_len);
00282     ptr += n_len;
00283     memset(ptr, 0, ccm_pramters->ccm_l_param);
00284 }
00285 
00286 /* Calculate X[i+1]: X[i+1] := E(Key, X[i] ^ B[i]) */
00287 /* Blen is <= 16; this handles zero-padding B when it is < 16 */
00288 static void ccm_auth_calc_Xi(void *aes_context, uint8_t X[static 16], uint8_t Blen, const uint8_t B[static Blen])
00289 {
00290     for (uint_fast8_t i = 0; i < Blen; i++) {
00291         X[i] ^= B[i];
00292     }
00293     arm_aes_encrypt(aes_context, X, X);
00294 }
00295 
00296 /* flags = reserved(1) || Adata(1) || M (3) || L (3)
00297  *         where M = 0 or (ccm_mic_len-2)/2
00298  *               L = CCM_L_PARAM - 1
00299  */
00300 /* B0 := flags(1)|| Nonce(15-L) || length of message(L) */
00301 static void ccm_auth_generate_B0(uint8_t *ptr, ccm_globals_t *ccm_params)
00302 {
00303     uint8_t flags = 0;
00304     uint8_t n_len;
00305 
00306     n_len = 15 - ccm_params->ccm_l_param;
00307 
00308     if (ccm_params->mic_len) {
00309         flags = ccm_params->mic_len - 2;
00310         flags <<= 2;
00311     }
00312     flags |= 0x40;
00313     flags |= (ccm_params->ccm_l_param - 1);
00314     *ptr++ = flags;
00315     memcpy(ptr, ccm_params->exp_nonce, n_len);
00316     ptr += n_len;
00317     if (ccm_params->ccm_l_param == 3) {
00318         *ptr++ = 0;
00319     }
00320     *ptr++ = ccm_params->data_len >> 8;
00321     *ptr = ccm_params->data_len;
00322 }
00323 
00324 
00325 static uint8_t ccm_mic_len_calc(uint8_t sec_level)
00326 {
00327     switch (sec_level) {
00328         case AES_SECURITY_LEVEL_ENC_MIC32:
00329         case AES_SECURITY_LEVEL_MIC32:
00330             return 4;
00331         case AES_SECURITY_LEVEL_ENC_MIC64:
00332         case AES_SECURITY_LEVEL_MIC64:
00333             return 8;
00334         case AES_SECURITY_LEVEL_ENC_MIC128:
00335         case AES_SECURITY_LEVEL_MIC128:
00336             return 16;
00337         default:
00338             return 0;
00339 
00340     }
00341 }
00342