cmac.c
Go to the documentation of this file.
1 /**
2  * @file cmac.c
3  * @brief CMAC (Cipher-based Message Authentication Code)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneCRYPTO Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @section Description
28  *
29  * CMAC is a block cipher-based MAC algorithm specified in NIST SP 800-38B
30  *
31  * @author Oryx Embedded SARL (www.oryx-embedded.com)
32  * @version 2.4.4
33  **/
34 
35 //Switch to the appropriate trace level
36 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL
37 
38 //Dependencies
39 #include "core/crypto.h"
40 #include "mac/cmac.h"
41 
42 //Check crypto library configuration
43 #if (CMAC_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief Compute CMAC using the specified cipher algorithm
48  * @param[in] cipher Cipher algorithm used to compute CMAC
49  * @param[in] key Pointer to the secret key
50  * @param[in] keyLen Length of the secret key
51  * @param[in] data Pointer to the input message
52  * @param[in] dataLen Length of the input data
53  * @param[out] mac Calculated MAC value
54  * @param[in] macLen Expected length of the MAC
55  * @return Error code
56  **/
57 
58 error_t cmacCompute(const CipherAlgo *cipher, const void *key, size_t keyLen,
59  const void *data, size_t dataLen, uint8_t *mac, size_t macLen)
60 {
61  error_t error;
62 #if (CRYPTO_STATIC_MEM_SUPPORT == DISABLED)
63  CmacContext *context;
64 #else
65  CmacContext context[1];
66 #endif
67 
68 #if (CRYPTO_STATIC_MEM_SUPPORT == DISABLED)
69  //Allocate a memory buffer to hold the CMAC context
70  context = cryptoAllocMem(sizeof(CmacContext));
71  //Failed to allocate memory?
72  if(context == NULL)
73  return ERROR_OUT_OF_MEMORY;
74 #endif
75 
76  //Initialize the CMAC context
77  error = cmacInit(context, cipher, key, keyLen);
78 
79  //Check status code
80  if(!error)
81  {
82  //Digest the message
83  cmacUpdate(context, data, dataLen);
84  //Finalize the CMAC computation
85  error = cmacFinal(context, mac, macLen);
86  }
87 
88 #if (CRYPTO_STATIC_MEM_SUPPORT == DISABLED)
89  //Free previously allocated memory
90  cryptoFreeMem(context);
91 #endif
92 
93  //Return status code
94  return error;
95 }
96 
97 
98 /**
99  * @brief Initialize CMAC calculation
100  * @param[in] context Pointer to the CMAC context to initialize
101  * @param[in] cipher Cipher algorithm used to compute CMAC
102  * @param[in] key Pointer to the secret key
103  * @param[in] keyLen Length of the secret key
104  * @return Error code
105  **/
106 
107 error_t cmacInit(CmacContext *context, const CipherAlgo *cipher,
108  const void *key, size_t keyLen)
109 {
110  error_t error;
111  uint8_t rb;
112 
113  //Check parameters
114  if(context == NULL || cipher == NULL)
116 
117  //CMAC supports only block ciphers whose block size is 64 or 128 bits
118  if(cipher->type != CIPHER_ALGO_TYPE_BLOCK)
120 
121  //Rb is completely determined by the number of bits in a block
122  if(cipher->blockSize == 8)
123  {
124  //If b = 64, then Rb = 11011
125  rb = 0x1B;
126  }
127  else if(cipher->blockSize == 16)
128  {
129  //If b = 128, then Rb = 10000111
130  rb = 0x87;
131  }
132  else
133  {
134  //Invalid block size
136  }
137 
138  //Cipher algorithm used to compute CMAC
139  context->cipher = cipher;
140 
141  //Initialize cipher context
142  error = cipher->init(&context->cipherContext, key, keyLen);
143  //Any error to report?
144  if(error)
145  return error;
146 
147  //Let L = 0
148  osMemset(context->buffer, 0, cipher->blockSize);
149 
150  //Compute L = CIPH(L)
151  cipher->encryptBlock(&context->cipherContext, context->buffer,
152  context->buffer);
153 
154  //The subkey K1 is obtained by multiplying L by x in GF(2^b)
155  cmacMul(context->k1, context->buffer, cipher->blockSize, rb);
156  //The subkey K2 is obtained by multiplying L by x^2 in GF(2^b)
157  cmacMul(context->k2, context->k1, cipher->blockSize, rb);
158 
159  //Reset CMAC context
160  cmacReset(context);
161 
162  //Successful initialization
163  return NO_ERROR;
164 }
165 
166 
167 /**
168  * @brief Reset CMAC context
169  * @param[in] context Pointer to the CMAC context
170  **/
171 
172 void cmacReset(CmacContext *context)
173 {
174  //Clear input buffer
175  osMemset(context->buffer, 0, context->cipher->blockSize);
176  //Number of bytes in the buffer
177  context->bufferLength = 0;
178 
179  //Initialize MAC value
180  osMemset(context->mac, 0, context->cipher->blockSize);
181 }
182 
183 
184 /**
185  * @brief Update the CMAC context with a portion of the message being hashed
186  * @param[in] context Pointer to the CMAC context
187  * @param[in] data Pointer to the input data
188  * @param[in] dataLen Length of the buffer
189  **/
190 
191 void cmacUpdate(CmacContext *context, const void *data, size_t dataLen)
192 {
193  size_t n;
194 
195  //Process the incoming data
196  while(dataLen > 0)
197  {
198  //Process message block by block
199  if(context->bufferLength == context->cipher->blockSize)
200  {
201  //XOR M(i) with C(i-1)
202  cmacXorBlock(context->buffer, context->buffer, context->mac,
203  context->cipher->blockSize);
204 
205  //Compute C(i) = CIPH(M(i) ^ C(i-1))
206  context->cipher->encryptBlock(&context->cipherContext, context->buffer,
207  context->mac);
208 
209  //Empty the buffer
210  context->bufferLength = 0;
211  }
212 
213  //The message is partitioned into complete blocks
214  n = MIN(dataLen, context->cipher->blockSize - context->bufferLength);
215 
216  //Copy the data to the buffer
217  osMemcpy(context->buffer + context->bufferLength, data, n);
218  //Update the length of the buffer
219  context->bufferLength += n;
220 
221  //Advance the data pointer
222  data = (uint8_t *) data + n;
223  //Remaining bytes to process
224  dataLen -= n;
225  }
226 }
227 
228 
229 /**
230  * @brief Finish the CMAC calculation
231  * @param[in] context Pointer to the CMAC context
232  * @param[out] mac Calculated MAC value (optional parameter)
233  * @param[in] macLen Expected length of the MAC
234  * @return Error code
235  **/
236 
237 error_t cmacFinal(CmacContext *context, uint8_t *mac, size_t macLen)
238 {
239  //Make sure the CMAC context is valid
240  if(context == NULL)
242 
243  //Check the length of the MAC
244  if(macLen < 1 || macLen > context->cipher->blockSize)
246 
247  //Check whether the last block Mn is complete
248  if(context->bufferLength >= context->cipher->blockSize)
249  {
250  //The final block M(n) is XOR-ed with the first subkey K1
251  cmacXorBlock(context->buffer, context->buffer, context->k1,
252  context->cipher->blockSize);
253  }
254  else
255  {
256  //Append padding string
257  context->buffer[context->bufferLength++] = 0x80;
258 
259  //Append the minimum number of zeroes to form a complete block
260  while(context->bufferLength < context->cipher->blockSize)
261  {
262  context->buffer[context->bufferLength++] = 0x00;
263  }
264 
265  //The final block M(n) is XOR-ed with the second subkey K2
266  cmacXorBlock(context->buffer, context->buffer, context->k2,
267  context->cipher->blockSize);
268  }
269 
270  //XOR M(n) with C(n-1)
271  cmacXorBlock(context->buffer, context->buffer, context->mac,
272  context->cipher->blockSize);
273 
274  //Compute T = CIPH(M(n) ^ C(n-1))
275  context->cipher->encryptBlock(&context->cipherContext, context->buffer,
276  context->mac);
277 
278  //Copy the resulting MAC value
279  if(mac != NULL)
280  {
281  //It is possible to truncate the MAC. The result of the truncation
282  //should be taken in most significant bits first order
283  osMemcpy(mac, context->mac, macLen);
284  }
285 
286  //Successful processing
287  return NO_ERROR;
288 }
289 
290 
291 /**
292  * @brief Release CMAC context
293  * @param[in] context Pointer to the CMAC context
294  **/
295 
296 void cmacDeinit(CmacContext *context)
297 {
298  //Make sure the CMAC context is valid
299  if(context != NULL)
300  {
301  //Release cipher context
302  context->cipher->deinit(&context->cipherContext);
303 
304  //Clear CMAC context
305  osMemset(context, 0, sizeof(CmacContext));
306  }
307 }
308 
309 
310 /**
311  * @brief Multiplication by x in GF(2^128)
312  * @param[out] x Pointer to the output block
313  * @param[out] a Pointer to the input block
314  * @param[in] n Size of the block, in bytes
315  * @param[in] rb Representation of the irreducible binary polynomial
316  **/
317 
318 void cmacMul(uint8_t *x, const uint8_t *a, size_t n, uint8_t rb)
319 {
320  size_t i;
321  uint8_t c;
322 
323  //Save the value of the most significant bit
324  c = a[0] >> 7;
325 
326  //The multiplication of a polynomial by x in GF(2^128) corresponds to a
327  //shift of indices
328  for(i = 0; i < (n - 1); i++)
329  {
330  x[i] = (a[i] << 1) | (a[i + 1] >> 7);
331  }
332 
333  //Shift the last byte of the block to the left
334  x[i] = a[i] << 1;
335 
336  //If the highest term of the result is equal to one, then perform reduction
337  x[i] ^= rb & ~(c - 1);
338 }
339 
340 
341 /**
342  * @brief XOR operation
343  * @param[out] x Block resulting from the XOR operation
344  * @param[in] a First input block
345  * @param[in] b Second input block
346  * @param[in] n Size of the block, in bytes
347  **/
348 
349 void cmacXorBlock(uint8_t *x, const uint8_t *a, const uint8_t *b, size_t n)
350 {
351  size_t i;
352 
353  //Perform XOR operation
354  for(i = 0; i < n; i++)
355  {
356  x[i] = a[i] ^ b[i];
357  }
358 }
359 
360 #endif
size_t bufferLength
Definition: cmac.h:60
uint8_t b
Definition: nbns_common.h:104
uint8_t a
Definition: ndp.h:411
uint8_t x
Definition: lldp_ext_med.h:211
@ CIPHER_ALGO_TYPE_BLOCK
Definition: crypto.h:953
uint8_t data[]
Definition: ethernet.h:222
size_t blockSize
Definition: crypto.h:1072
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
const CipherAlgo * cipher
Definition: cmac.h:55
uint8_t k2[MAX_CIPHER_BLOCK_SIZE]
Definition: cmac.h:58
void cmacDeinit(CmacContext *context)
Release CMAC context.
Definition: cmac.c:296
uint8_t buffer[MAX_CIPHER_BLOCK_SIZE]
Definition: cmac.h:59
CipherAlgoEncryptBlock encryptBlock
Definition: crypto.h:1076
void cmacMul(uint8_t *x, const uint8_t *a, size_t n, uint8_t rb)
Multiplication by x in GF(2^128)
Definition: cmac.c:318
CipherAlgoInit init
Definition: crypto.h:1073
uint8_t mac[MAX_CIPHER_BLOCK_SIZE]
Definition: cmac.h:61
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
error_t
Error codes.
Definition: error.h:43
CipherAlgoType type
Definition: crypto.h:1071
General definitions for cryptographic algorithms.
#define MIN(a, b)
Definition: os_port.h:63
uint32_t dataLen
Definition: sftp_common.h:229
CMAC algorithm context.
Definition: cmac.h:54
void cmacXorBlock(uint8_t *x, const uint8_t *a, const uint8_t *b, size_t n)
XOR operation.
Definition: cmac.c:349
void cmacReset(CmacContext *context)
Reset CMAC context.
Definition: cmac.c:172
uint8_t n
#define cryptoFreeMem(p)
Definition: crypto.h:791
error_t cmacCompute(const CipherAlgo *cipher, const void *key, size_t keyLen, const void *data, size_t dataLen, uint8_t *mac, size_t macLen)
Compute CMAC using the specified cipher algorithm.
Definition: cmac.c:58
CipherAlgoDeinit deinit
Definition: crypto.h:1078
Common interface for encryption algorithms.
Definition: crypto.h:1068
error_t cmacInit(CmacContext *context, const CipherAlgo *cipher, const void *key, size_t keyLen)
Initialize CMAC calculation.
Definition: cmac.c:107
#define cryptoAllocMem(size)
Definition: crypto.h:786
void cmacUpdate(CmacContext *context, const void *data, size_t dataLen)
Update the CMAC context with a portion of the message being hashed.
Definition: cmac.c:191
uint8_t k1[MAX_CIPHER_BLOCK_SIZE]
Definition: cmac.h:57
CMAC (Cipher-based Message Authentication Code)
#define osMemset(p, value, length)
Definition: os_port.h:135
error_t cmacFinal(CmacContext *context, uint8_t *mac, size_t macLen)
Finish the CMAC calculation.
Definition: cmac.c:237
CipherContext cipherContext
Definition: cmac.h:56
@ NO_ERROR
Success.
Definition: error.h:44
uint8_t c
Definition: ndp.h:514