Nicolas Borla / Mbed OS BBR_1Ebene
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, 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 #ifndef CCM_USE_MUTEX
00047 #define arm_ccm_mutex_lock()
00048 #define arm_ccm_mutex_unlock()
00049 #endif
00050 
00051 static ccm_globals_t ccm_globals;
00052 
00053 /* CCM Library Parameters */
00054 static uint8_t CCM_L_PARAM = 2;
00055 static uint8_t ccm_sec_level;
00056 static uint8_t CCM_ENCODE_MODE;
00057 static const uint8_t *ccm_key_ptr;
00058 
00059 static void ccm_generate_A0(uint8_t *ptr);
00060 static void ccm_auth_generate_B0(uint8_t *ptr, uint16_t len);
00061 static void ccm_auth_calc_Xi(uint8_t X[static 16], uint8_t Blen, const uint8_t B[static Blen]);
00062 static uint8_t ccm_mic_len_calc(uint8_t sec_level);
00063 static void ccm_encode(uint16_t len , uint8_t *ptr);
00064 static int8_t ccm_calc_auth_MIC(const uint8_t *data_ptr, uint16_t data_len, const uint8_t *adata_ptr, uint16_t adata_len);
00065 
00066 /**
00067  * \brief A function to init CCM library.
00068  * \param sec_level Used CCM security level (0-7).
00069  * \param ccm_key pointer to 128-key.
00070  * \param mode AES_CCM_ENCRYPT or AES_CCM_DECRYPT
00071  * \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))
00072  *
00073  * \return Pointer to Global CCM paramameter buffer.
00074  * \return 0 When parameter fail or CCM is Busy.
00075  */
00076 ccm_globals_t   *ccm_sec_init(uint8_t sec_level, const uint8_t *ccm_key, uint8_t mode, uint8_t ccm_l)
00077 {
00078     ccm_globals_t *ret_val = 0;
00079     if ((ccm_l == 2 || ccm_l == 3) && (sec_level < 8)) {
00080         arm_ccm_mutex_lock();
00081         memset(&ccm_globals, 0, sizeof(ccm_globals_t));
00082         CCM_ENCODE_MODE = mode;
00083         ccm_sec_level = sec_level;
00084         CCM_L_PARAM = ccm_l;
00085         ccm_key_ptr = ccm_key;
00086         arm_aes_start(ccm_key);
00087         ccm_globals.mic_len = ccm_mic_len_calc(ccm_sec_level);
00088         ccm_globals.mic = 0;
00089         ret_val = &ccm_globals;
00090     } else {
00091         ccm_key_ptr = 0;
00092     }
00093     return ret_val;
00094 }
00095 
00096 /**
00097  * \brief A function to init CCM library.
00098  * \param sec_level Used CCM security level (0-7).
00099  * \param ccm_key pointer to 128-key.
00100  * \param mode AES_CCM_ENCRYPT or AES_CCM_DECRYPT
00101  * \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))
00102  *
00103  * \return 0 CCM process OK and when AES_CCM_DECRYPT mode was selectected also MIC was correct.
00104  * \return -1 Init have not called or data or adata pointers or lengths are zero.
00105  * \return -2 Null pointer given to function
00106  */
00107 int8_t ccm_process_run(ccm_globals_t *ccm_params)
00108 {
00109     int8_t ret_val = -1;
00110     if (ccm_params == NULL) {
00111         ret_val = -2;
00112         goto END;
00113 
00114     }
00115 
00116     if (ccm_params->mic_len) {
00117         /* data length >= 0xff00 would require different encoding */
00118         if (ccm_params->adata_len == 0 || ccm_params->adata_len >= 0xff00 || ccm_params->adata_ptr == NULL) {
00119             goto END;
00120         } else if (ccm_params->mic == NULL) {
00121             ret_val = -2;
00122             goto END;
00123         }
00124     }
00125 
00126     if (ccm_params->data_len != 0 && ccm_params->data_ptr == NULL) {
00127         ret_val = -2;
00128         goto END;
00129     }
00130 
00131     if (CCM_ENCODE_MODE == AES_CCM_ENCRYPT) {
00132         if (ccm_params->mic_len) {
00133             //Calc
00134             if (ccm_calc_auth_MIC(ccm_params->data_ptr, ccm_params->data_len, ccm_params->adata_ptr, ccm_params->adata_len)) {
00135                 goto END;
00136             }
00137         }
00138         if (ccm_params->data_len) {
00139             ccm_encode(ccm_params->data_len, ccm_params->data_ptr);
00140         }
00141         ret_val = 0;
00142     } else {
00143         if (ccm_params->data_len) {
00144             ccm_encode(ccm_params->data_len, ccm_params->data_ptr);
00145         }
00146         if (ccm_params->mic_len) {
00147             if (ccm_calc_auth_MIC(ccm_params->data_ptr, ccm_params->data_len, ccm_params->adata_ptr, ccm_params->adata_len) == 0) {
00148                 ret_val = 0;
00149             }
00150         } else {
00151             ret_val = 0;
00152         }
00153     }
00154 
00155 END:
00156     ccm_key_ptr = 0;
00157     arm_aes_finish();
00158     arm_ccm_mutex_unlock();
00159     return ret_val;
00160 }
00161 
00162 
00163 /* Counter-mode encryption/decryption
00164  *  Ci := E(Key, Ai) ^ Mi
00165  */
00166 static void ccm_encode(uint16_t len , uint8_t *ptr)
00167 {
00168     if (!ccm_key_ptr || ccm_sec_level < AES_SECURITY_LEVEL_ENC) {
00169         return;
00170     }
00171 
00172     uint8_t Ai[16], Si[16];
00173 
00174     //first, generate A0
00175     ccm_generate_A0(Ai);
00176 
00177     while (len) {
00178         //increment counter in Ai - 16-bit increment enough; len is 16-bit
00179         if (++Ai[15] == 0) {
00180             ++Ai[14];
00181         }
00182 
00183         // Si := E(Key, Ai)
00184         arm_aes_encrypt(Ai, Si);
00185 
00186         // output := Si ^ input
00187         for (int_fast8_t i = 0; i < 16 && len; i++, len--) {
00188             *ptr++ ^= Si[i];
00189         }
00190     }
00191 }
00192 
00193 
00194 static int8_t ccm_calc_auth_MIC(const uint8_t *data_ptr, uint16_t data_len, const uint8_t *adata_ptr, uint16_t adata_len)
00195 {
00196     uint8_t Xi[16];
00197 
00198     // As a convenience, treat "data" as "adata", reflecting that "Private
00199     // Payload" is part of "a data" not "m data" for unencrypted modes.
00200     // The distinction matters because there's an "align to block" between
00201     // "a" and "m", which we don't do when it's all in "a".
00202     if (ccm_sec_level < AES_SECURITY_LEVEL_ENC && data_len != 0) {
00203         // This trick only works if data follows adata
00204         if (data_ptr == adata_ptr + adata_len) {
00205             adata_len += data_len;
00206             data_len = 0;
00207         } else {
00208             return -1;
00209         }
00210     }
00211 
00212     ccm_auth_generate_B0(Xi, data_len);  //Set B0
00213 
00214     // Calculate X1: E(key, B0)
00215     // [Could use ccm_auth_calc_Xi - it's formally X1 := E(key, B0 ^ X0), where X0 = 0]
00216     arm_aes_encrypt(Xi, Xi);
00217 
00218     //First authentication block has 2-byte length field concatenated
00219     if (adata_len) {
00220         uint8_t B1[16];
00221         uint_fast8_t t_len = adata_len > 14 ? 14 : adata_len;
00222 
00223         B1[0] = adata_len >> 8;
00224         B1[1] = adata_len;
00225         memcpy(&B1[2], adata_ptr, t_len);
00226 
00227         ccm_auth_calc_Xi(Xi, 2 + t_len, B1);
00228         adata_ptr += t_len;
00229         adata_len -= t_len;
00230     }
00231 
00232     while (adata_len) {
00233         uint_fast8_t t_len = adata_len > 16 ? 16 : adata_len;
00234 
00235         ccm_auth_calc_Xi(Xi, t_len, adata_ptr);
00236         adata_ptr += t_len;
00237         adata_len -= t_len;
00238     }
00239 
00240     while (data_len) {
00241         uint_fast8_t t_len = data_len > 16 ? 16 : data_len;
00242 
00243         ccm_auth_calc_Xi(Xi, t_len, data_ptr);
00244         data_ptr += t_len;
00245         data_len -= t_len;
00246     }
00247 
00248     // Authentication tag T is leftmost M octets of X[t+1]
00249 
00250     // Encryption block S0 is E(Key, A0)
00251     uint8_t S0[16];
00252     ccm_generate_A0(S0);
00253     arm_aes_encrypt(S0, S0);
00254 
00255     // Encrypted authentication tag U is S0^T (leftmost M octets)
00256     if (CCM_ENCODE_MODE == AES_CCM_ENCRYPT) {
00257         for (uint_fast8_t i = 0; i < ccm_globals.mic_len; i++) {
00258             ccm_globals.mic[i] = Xi[i] ^ S0[i];
00259         }
00260     } else {
00261         for (uint_fast8_t i = 0; i < ccm_globals.mic_len; i++)
00262             if (ccm_globals.mic[i] != (Xi[i] ^ S0[i])) {
00263                 return -1;
00264             }
00265     }
00266     return 0;
00267 }
00268 
00269 /**
00270  * \brief This function is used to create A0 which is actual init vector to be used to encrypt full rf message.
00271  * Ai = 8-bit FLAGS | nonce | 16/24-bit counter.
00272  * Si = E[key,Ai]
00273  *
00274  * \return none.
00275  */
00276 static void ccm_generate_A0(uint8_t *ptr)
00277 {
00278     uint8_t n_len, flags;
00279     flags = CCM_L_PARAM - 1;
00280     n_len = 15 - CCM_L_PARAM;
00281 
00282     //FLAGS = L' = L - 1;
00283     *ptr++ = flags;
00284     memcpy(ptr, ccm_globals.exp_nonce, n_len);
00285     ptr += n_len;
00286     memset(ptr, 0, CCM_L_PARAM);
00287 }
00288 
00289 /* Calculate X[i+1]: X[i+1] := E(Key, X[i] ^ B[i]) */
00290 /* Blen is <= 16; this handles zero-padding B when it is < 16 */
00291 static void ccm_auth_calc_Xi(uint8_t X[static 16], uint8_t Blen, const uint8_t B[static Blen])
00292 {
00293     for (uint_fast8_t i = 0; i < Blen; i++) {
00294         X[i] ^= B[i];
00295     }
00296     arm_aes_encrypt(X, X);
00297 }
00298 
00299 /* flags = reserved(1) || Adata(1) || M (3) || L (3)
00300  *         where M = 0 or (ccm_mic_len-2)/2
00301  *               L = CCM_L_PARAM - 1
00302  */
00303 /* B0 := flags(1)|| Nonce(15-L) || length of message(L) */
00304 static void ccm_auth_generate_B0(uint8_t *ptr, uint16_t len)
00305 {
00306     uint8_t flags = 0;
00307     uint8_t n_len;
00308 
00309     n_len = 15 - CCM_L_PARAM;
00310 
00311     if (ccm_globals.mic_len) {
00312         flags = ccm_globals.mic_len - 2;
00313         flags <<= 2;
00314     }
00315     flags |= 0x40;
00316     flags |= (CCM_L_PARAM - 1);
00317     *ptr++ = flags;
00318     memcpy(ptr, ccm_globals.exp_nonce, n_len);
00319     ptr += n_len;
00320     if (CCM_L_PARAM == 3) {
00321         *ptr++ = 0;
00322     }
00323     *ptr++ = len >> 8;
00324     *ptr = len;
00325 }
00326 
00327 
00328 static uint8_t ccm_mic_len_calc(uint8_t sec_level)
00329 {
00330     switch (sec_level) {
00331         case AES_SECURITY_LEVEL_ENC_MIC32:
00332         case AES_SECURITY_LEVEL_MIC32:
00333             return 4;
00334         case AES_SECURITY_LEVEL_ENC_MIC64:
00335         case AES_SECURITY_LEVEL_MIC64:
00336             return 8;
00337         case AES_SECURITY_LEVEL_ENC_MIC128:
00338         case AES_SECURITY_LEVEL_MIC128:
00339             return 16;
00340         default:
00341             return 0;
00342 
00343     }
00344 }
00345