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-2020 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 1.9.8
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  CmacContext *context;
63 
64  //Allocate a memory buffer to hold the CMAC context
65  context = cryptoAllocMem(sizeof(CmacContext));
66 
67  //Successful memory allocation?
68  if(context != NULL)
69  {
70  //Initialize the CMAC context
71  error = cmacInit(context, cipher, key, keyLen);
72 
73  //Check status code
74  if(!error)
75  {
76  //Digest the message
77  cmacUpdate(context, data, dataLen);
78  //Finalize the CMAC computation
79  error = cmacFinal(context, mac, macLen);
80  }
81 
82  //Free previously allocated memory
83  cryptoFreeMem(context);
84  }
85  else
86  {
87  //Failed to allocate memory
88  error = ERROR_OUT_OF_MEMORY;
89  }
90 
91  //Return status code
92  return error;
93 }
94 
95 
96 /**
97  * @brief Initialize CMAC calculation
98  * @param[in] context Pointer to the CMAC context to initialize
99  * @param[in] cipher Cipher algorithm used to compute CMAC
100  * @param[in] key Pointer to the secret key
101  * @param[in] keyLen Length of the secret key
102  * @return Error code
103  **/
104 
105 error_t cmacInit(CmacContext *context, const CipherAlgo *cipher,
106  const void *key, size_t keyLen)
107 {
108  error_t error;
109  uint8_t rb;
110 
111  //CMAC supports only block ciphers whose block size is 64 or 128 bits
112  if(cipher->type != CIPHER_ALGO_TYPE_BLOCK)
114 
115  //Rb is completely determined by the number of bits in a block
116  if(cipher->blockSize == 8)
117  {
118  //If b = 64, then Rb = 11011
119  rb = 0x1B;
120  }
121  else if(cipher->blockSize == 16)
122  {
123  //If b = 128, then Rb = 10000111
124  rb = 0x87;
125  }
126  else
127  {
128  //Invalid block size
130  }
131 
132  //Cipher algorithm used to compute CMAC
133  context->cipher = cipher;
134 
135  //Initialize cipher context
136  error = cipher->init(context->cipherContext, key, keyLen);
137  //Any error to report?
138  if(error)
139  return error;
140 
141  //Let L = 0
142  osMemset(context->buffer, 0, cipher->blockSize);
143  //Compute L = CIPH(L)
144  cipher->encryptBlock(context->cipherContext, context->buffer, context->buffer);
145 
146  //The subkey K1 is obtained by multiplying L by x in GF(2^b)
147  cmacMul(context->k1, context->buffer, cipher->blockSize, rb);
148  //The subkey K2 is obtained by multiplying L by x^2 in GF(2^b)
149  cmacMul(context->k2, context->k1, cipher->blockSize, rb);
150 
151  //Reset CMAC context
152  cmacReset(context);
153 
154  //Successful initialization
155  return NO_ERROR;
156 }
157 
158 
159 /**
160  * @brief Reset CMAC context
161  * @param[in] context Pointer to the CMAC context
162  **/
163 
164 void cmacReset(CmacContext *context)
165 {
166  //Clear input buffer
167  osMemset(context->buffer, 0, context->cipher->blockSize);
168  //Number of bytes in the buffer
169  context->bufferLength = 0;
170 
171  //Initialize MAC value
172  osMemset(context->mac, 0, context->cipher->blockSize);
173 }
174 
175 
176 /**
177  * @brief Update the CMAC context with a portion of the message being hashed
178  * @param[in] context Pointer to the CMAC context
179  * @param[in] data Pointer to the input data
180  * @param[in] dataLen Length of the buffer
181  **/
182 
183 void cmacUpdate(CmacContext *context, const void *data, size_t dataLen)
184 {
185  size_t n;
186 
187  //Process the incoming data
188  while(dataLen > 0)
189  {
190  //Process message block by block
191  if(context->bufferLength == context->cipher->blockSize)
192  {
193  //XOR M(i) with C(i-1)
194  cmacXorBlock(context->buffer, context->buffer, context->mac,
195  context->cipher->blockSize);
196 
197  //Compute C(i) = CIPH(M(i) ^ C(i-1))
198  context->cipher->encryptBlock(context->cipherContext, context->buffer,
199  context->mac);
200 
201  //Empty the buffer
202  context->bufferLength = 0;
203  }
204 
205  //The message is partitioned into complete blocks
206  n = MIN(dataLen, context->cipher->blockSize - context->bufferLength);
207 
208  //Copy the data to the buffer
209  osMemcpy(context->buffer + context->bufferLength, data, n);
210  //Update the length of the buffer
211  context->bufferLength += n;
212 
213  //Advance the data pointer
214  data = (uint8_t *) data + n;
215  //Remaining bytes to process
216  dataLen -= n;
217  }
218 }
219 
220 
221 /**
222  * @brief Finish the CMAC calculation
223  * @param[in] context Pointer to the CMAC context
224  * @param[out] mac Calculated MAC value
225  * @param[in] macLen Expected length of the MAC
226  * @return Error code
227  **/
228 
229 error_t cmacFinal(CmacContext *context, uint8_t *mac, size_t macLen)
230 {
231  //Check the length of the MAC
232  if(macLen < 1 || macLen > context->cipher->blockSize)
234 
235  //Check whether the last block Mn is complete
236  if(context->bufferLength >= context->cipher->blockSize)
237  {
238  //The final block M(n) is XOR-ed with the first subkey K1
239  cmacXorBlock(context->buffer, context->buffer, context->k1,
240  context->cipher->blockSize);
241  }
242  else
243  {
244  //Append padding string
245  context->buffer[context->bufferLength++] = 0x80;
246 
247  //Append the minimum number of zeroes to form a complete block
248  while(context->bufferLength < context->cipher->blockSize)
249  {
250  context->buffer[context->bufferLength++] = 0x00;
251  }
252 
253  //The final block M(n) is XOR-ed with the second subkey K2
254  cmacXorBlock(context->buffer, context->buffer, context->k2,
255  context->cipher->blockSize);
256  }
257 
258  //XOR M(n) with C(n-1)
259  cmacXorBlock(context->buffer, context->buffer, context->mac,
260  context->cipher->blockSize);
261 
262  //Compute T = CIPH(M(n) ^ C(n-1))
263  context->cipher->encryptBlock(context->cipherContext, context->buffer,
264  context->mac);
265 
266  //Copy the resulting MAC value
267  if(mac != NULL)
268  {
269  //It is possible to truncate the MAC. The result of the truncation
270  //should be taken in most significant bits first order
271  osMemcpy(mac, context->mac, macLen);
272  }
273 
274  //Successful processing
275  return NO_ERROR;
276 }
277 
278 
279 /**
280  * @brief Multiplication by x in GF(2^128)
281  * @param[out] x Pointer to the output block
282  * @param[out] a Pointer to the input block
283  * @param[in] n Size of the block, in bytes
284  * @param[in] rb Representation of the irreducible binary polynomial
285  **/
286 
287 void cmacMul(uint8_t *x, const uint8_t *a, size_t n, uint8_t rb)
288 {
289  size_t i;
290  uint8_t c;
291 
292  //Save the value of the most significant bit
293  c = a[0] >> 7;
294 
295  //The multiplication of a polynomial by x in GF(2^128) corresponds to a
296  //shift of indices
297  for(i = 0; i < (n - 1); i++)
298  {
299  x[i] = (a[i] << 1) | (a[i + 1] >> 7);
300  }
301 
302  //Shift the last byte of the block to the left
303  x[i] = a[i] << 1;
304 
305  //If the highest term of the result is equal to one, then perform reduction
306  x[i] ^= rb & ~(c - 1);
307 }
308 
309 
310 /**
311  * @brief XOR operation
312  * @param[out] x Block resulting from the XOR operation
313  * @param[in] a First input block
314  * @param[in] b Second input block
315  * @param[in] n Size of the block, in bytes
316  **/
317 
318 void cmacXorBlock(uint8_t *x, const uint8_t *a, const uint8_t *b, size_t n)
319 {
320  size_t i;
321 
322  //Perform XOR operation
323  for(i = 0; i < n; i++)
324  {
325  x[i] = a[i] ^ b[i];
326  }
327 }
328 
329 #endif
size_t bufferLength
Definition: cmac.h:109
uint8_t a
Definition: ndp.h:410
uint8_t data[]
Definition: ethernet.h:209
size_t blockSize
Definition: crypto.h:1089
const CipherAlgo * cipher
Definition: cmac.h:104
uint8_t k2[MAX_CIPHER_BLOCK_SIZE]
Definition: cmac.h:107
uint8_t buffer[MAX_CIPHER_BLOCK_SIZE]
Definition: cmac.h:108
CipherAlgoEncryptBlock encryptBlock
Definition: crypto.h:1093
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:287
CipherAlgoInit init
Definition: crypto.h:1090
uint8_t mac[MAX_CIPHER_BLOCK_SIZE]
Definition: cmac.h:110
Invalid parameter.
Definition: error.h:47
#define osMemcpy(dest, src, length)
Definition: os_port.h:134
error_t
Error codes.
Definition: error.h:42
CipherAlgoType type
Definition: crypto.h:1088
General definitions for cryptographic algorithms.
#define MIN(a, b)
Definition: os_port.h:62
CMAC algorithm context.
Definition: cmac.h:102
void cmacXorBlock(uint8_t *x, const uint8_t *a, const uint8_t *b, size_t n)
XOR operation.
Definition: cmac.c:318
void cmacReset(CmacContext *context)
Reset CMAC context.
Definition: cmac.c:164
uint8_t b[6]
Definition: ethernet.h:179
uint8_t cipherContext[MAX_CIPHER_CONTEXT_SIZE]
Definition: cmac.h:105
uint8_t n
#define cryptoFreeMem(p)
Definition: crypto.h:630
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
Common interface for encryption algorithms.
Definition: crypto.h:1084
error_t cmacInit(CmacContext *context, const CipherAlgo *cipher, const void *key, size_t keyLen)
Initialize CMAC calculation.
Definition: cmac.c:105
#define cryptoAllocMem(size)
Definition: crypto.h:625
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:183
uint8_t k1[MAX_CIPHER_BLOCK_SIZE]
Definition: cmac.h:106
CMAC (Cipher-based Message Authentication Code)
#define osMemset(p, value, length)
Definition: os_port.h:128
error_t cmacFinal(CmacContext *context, uint8_t *mac, size_t macLen)
Finish the CMAC calculation.
Definition: cmac.c:229
Success.
Definition: error.h:44
uint8_t c
Definition: ndp.h:513