samd51_crypto_hash.c
Go to the documentation of this file.
1 /**
2  * @file samd51_crypto_hash.c
3  * @brief SAMD51 hash hardware accelerator
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2025 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  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.5.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL CRYPTO_TRACE_LEVEL
33 
34 //Dependencies
35 #include "sam.h"
36 #include "core/crypto.h"
39 #include "hash/hash_algorithms.h"
40 #include "debug.h"
41 
42 //Check crypto library configuration
43 #if (SAMD51_CRYPTO_HASH_SUPPORT == ENABLED)
44 
45 //IAR EWARM compiler?
46 #if defined(__ICCARM__)
47 
48 //ICM region descriptor
49 #pragma data_alignment = 64
50 static Samd51IcmDesc icmDesc;
51 //ICM hash area
52 #pragma data_alignment = 128
53 static uint32_t icmHash[8];
54 
55 //Keil MDK-ARM or GCC compiler?
56 #else
57 
58 //ICM region descriptor
59 static Samd51IcmDesc icmDesc __attribute__((aligned(64)));
60 //ICM hash area
61 static uint32_t icmHash[8] __attribute__((aligned(128)));
62 
63 #endif
64 
65 //Padding string
66 static const uint8_t padding[64] =
67 {
68  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
69  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
71  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
72 };
73 
74 
75 /**
76  * @brief Update hash value
77  * @param[in] algo Hash algorithm
78  * @param[in] data Pointer to the input buffer
79  * @param[in] length Length of the input buffer
80  * @param[in,out] h Hash value
81  **/
82 
83 void hashProcessData(uint32_t algo, const uint8_t *data, size_t length,
84  uint32_t *h)
85 {
86  //Acquire exclusive access to the ICM module
88 
89  //Perform software reset
90  ICM_REGS->ICM_CTRL = ICM_CTRL_SWRST_Msk;
91 
92  //Set ICM region descriptor
93  icmDesc.raddr = (uint32_t) data;
94  icmDesc.rcfg = ICM_RCFG_ALGO(algo) | ICM_RCFG_EOM_Msk;
95  icmDesc.rctrl = (length / 64) - 1;
96  icmDesc.rnext = 0;
97 
98  //Data memory barrier
99  __DMB();
100 
101  //Set configuration register
102  ICM_REGS->ICM_CFG = ICM_CFG_UALGO(algo) | ICM_CFG_UIHASH_Msk | ICM_CFG_SLBDIS_Msk;
103  //The start address is a multiple of 64 bytes
104  ICM_REGS->ICM_DSCR = (uint32_t) &icmDesc;
105  //The hash memory location must be a multiple of 128 bytes
106  ICM_REGS->ICM_HASH = (uint32_t) &icmHash;
107 
108  //Set initial hash value
109  ICM_REGS->ICM_UIHVAL[0] = h[0];
110  ICM_REGS->ICM_UIHVAL[1] = h[1];
111  ICM_REGS->ICM_UIHVAL[2] = h[2];
112  ICM_REGS->ICM_UIHVAL[3] = h[3];
113  ICM_REGS->ICM_UIHVAL[4] = h[4];
114 
115  //SHA-224 or SHA-256 algorithm?
116  if(algo == ICM_CFG_UALGO_SHA224_Val || algo == ICM_CFG_UALGO_SHA256_Val)
117  {
118  ICM_REGS->ICM_UIHVAL[5] = h[5];
119  ICM_REGS->ICM_UIHVAL[6] = h[6];
120  ICM_REGS->ICM_UIHVAL[7] = h[7];
121  }
122 
123  //Enable RHC interrupt (Region Hash Completed)
124  ICM_REGS->ICM_IER = ICM_IER_RHC(1);
125  //Enable ICM module
126  ICM_REGS->ICM_CTRL = ICM_CTRL_ENABLE_Msk;
127 
128  //The RHC status flag is set when the ICM has completed the region
129  while((ICM_REGS->ICM_ISR & ICM_ISR_RHC_Msk) == 0)
130  {
131  }
132 
133  //Disable ICM module
134  ICM_REGS->ICM_CTRL = ICM_CTRL_DISABLE_Msk;
135 
136  //Data memory barrier
137  __DMB();
138 
139  //Read resulting hash value
140  h[0] = icmHash[0];
141  h[1] = icmHash[1];
142  h[2] = icmHash[2];
143  h[3] = icmHash[3];
144  h[4] = icmHash[4];
145 
146  //SHA-224 or SHA-256 algorithm?
147  if(algo == ICM_CFG_UALGO_SHA224_Val || algo == ICM_CFG_UALGO_SHA256_Val)
148  {
149  h[5] = icmHash[5];
150  h[6] = icmHash[6];
151  h[7] = icmHash[7];
152  }
153 
154  //Release exclusive access to the ICM module
156 }
157 
158 
159 #if (SHA1_SUPPORT == ENABLED)
160 
161 /**
162  * @brief Initialize SHA-1 message digest context
163  * @param[in] context Pointer to the SHA-1 context to initialize
164  **/
165 
166 void sha1Init(Sha1Context *context)
167 {
168  //Set initial hash value
169  context->h[0] = BETOH32(0x67452301);
170  context->h[1] = BETOH32(0xEFCDAB89);
171  context->h[2] = BETOH32(0x98BADCFE);
172  context->h[3] = BETOH32(0x10325476);
173  context->h[4] = BETOH32(0xC3D2E1F0);
174 
175  //Number of bytes in the buffer
176  context->size = 0;
177  //Total length of the message
178  context->totalSize = 0;
179 }
180 
181 
182 /**
183  * @brief Update the SHA-1 context with a portion of the message being hashed
184  * @param[in] context Pointer to the SHA-1 context
185  * @param[in] data Pointer to the buffer being hashed
186  * @param[in] length Length of the buffer
187  **/
188 
189 void sha1Update(Sha1Context *context, const void *data, size_t length)
190 {
191  size_t n;
192 
193  //Process the incoming data
194  while(length > 0)
195  {
196  //Check whether some data is pending in the buffer
197  if(context->size == 0 && length >= 64)
198  {
199  //The length must be a multiple of 64 bytes
200  n = length - (length % 64);
201 
202  //Update hash value
203  hashProcessData(ICM_CFG_UALGO_SHA1_Val, data, n, context->h);
204 
205  //Update the SHA-1 context
206  context->totalSize += n;
207  //Advance the data pointer
208  data = (uint8_t *) data + n;
209  //Remaining bytes to process
210  length -= n;
211  }
212  else
213  {
214  //The buffer can hold at most 64 bytes
215  n = MIN(length, 64 - context->size);
216 
217  //Copy the data to the buffer
218  osMemcpy(context->buffer + context->size, data, n);
219 
220  //Update the SHA-1 context
221  context->size += n;
222  context->totalSize += n;
223  //Advance the data pointer
224  data = (uint8_t *) data + n;
225  //Remaining bytes to process
226  length -= n;
227 
228  //Check whether the buffer is full
229  if(context->size == 64)
230  {
231  //Update hash value
232  hashProcessData(ICM_CFG_UALGO_SHA1_Val, context->buffer,
233  context->size, context->h);
234 
235  //Empty the buffer
236  context->size = 0;
237  }
238  }
239  }
240 }
241 
242 
243 /**
244  * @brief Finish the SHA-1 message digest
245  * @param[in] context Pointer to the SHA-1 context
246  * @param[out] digest Calculated digest
247  **/
248 
249 void sha1Final(Sha1Context *context, uint8_t *digest)
250 {
251  uint_t i;
252  size_t paddingSize;
253  uint64_t totalSize;
254 
255  //Length of the original message (before padding)
256  totalSize = context->totalSize * 8;
257 
258  //Pad the message so that its length is congruent to 56 modulo 64
259  if(context->size < 56)
260  {
261  paddingSize = 56 - context->size;
262  }
263  else
264  {
265  paddingSize = 64 + 56 - context->size;
266  }
267 
268  //Append padding
269  sha1Update(context, padding, paddingSize);
270 
271  //Append the length of the original message
272  for(i = 0; i < 8; i++)
273  {
274  context->buffer[63 - i] = totalSize & 0xFF;
275  totalSize >>= 8;
276  }
277 
278  //Calculate the message digest
279  hashProcessData(ICM_CFG_UALGO_SHA1_Val, context->buffer, 64, context->h);
280 
281  //Copy the resulting digest
282  for(i = 0; i < (SHA1_DIGEST_SIZE / 4); i++)
283  {
284  STORE32LE(context->h[i], digest + i * 4);
285  }
286 }
287 
288 
289 /**
290  * @brief Finish the SHA-1 message digest (no padding added)
291  * @param[in] context Pointer to the SHA-1 context
292  * @param[out] digest Calculated digest
293  **/
294 
295 void sha1FinalRaw(Sha1Context *context, uint8_t *digest)
296 {
297  uint_t i;
298 
299  //Copy the resulting digest
300  for(i = 0; i < (SHA1_DIGEST_SIZE / 4); i++)
301  {
302  STORE32LE(context->h[i], digest + i * 4);
303  }
304 }
305 
306 #endif
307 #if (SHA224_SUPPORT == ENABLED)
308 
309 /**
310  * @brief Initialize SHA-224 message digest context
311  * @param[in] context Pointer to the SHA-224 context to initialize
312  **/
313 
314 void sha224Init(Sha224Context *context)
315 {
316  //Set initial hash value
317  context->h[0] = BETOH32(0xC1059ED8);
318  context->h[1] = BETOH32(0x367CD507);
319  context->h[2] = BETOH32(0x3070DD17);
320  context->h[3] = BETOH32(0xF70E5939);
321  context->h[4] = BETOH32(0xFFC00B31);
322  context->h[5] = BETOH32(0x68581511);
323  context->h[6] = BETOH32(0x64F98FA7);
324  context->h[7] = BETOH32(0xBEFA4FA4);
325 
326  //Number of bytes in the buffer
327  context->size = 0;
328  //Total length of the message
329  context->totalSize = 0;
330 }
331 
332 #endif
333 #if (SHA256_SUPPORT == ENABLED)
334 
335 /**
336  * @brief Initialize SHA-256 message digest context
337  * @param[in] context Pointer to the SHA-256 context to initialize
338  **/
339 
340 void sha256Init(Sha256Context *context)
341 {
342  //Set initial hash value
343  context->h[0] = BETOH32(0x6A09E667);
344  context->h[1] = BETOH32(0xBB67AE85);
345  context->h[2] = BETOH32(0x3C6EF372);
346  context->h[3] = BETOH32(0xA54FF53A);
347  context->h[4] = BETOH32(0x510E527F);
348  context->h[5] = BETOH32(0x9B05688C);
349  context->h[6] = BETOH32(0x1F83D9AB);
350  context->h[7] = BETOH32(0x5BE0CD19);
351 
352  //Number of bytes in the buffer
353  context->size = 0;
354  //Total length of the message
355  context->totalSize = 0;
356 }
357 
358 
359 /**
360  * @brief Update the SHA-256 context with a portion of the message being hashed
361  * @param[in] context Pointer to the SHA-256 context
362  * @param[in] data Pointer to the buffer being hashed
363  * @param[in] length Length of the buffer
364  **/
365 
366 void sha256Update(Sha256Context *context, const void *data, size_t length)
367 {
368  size_t n;
369 
370  //Process the incoming data
371  while(length > 0)
372  {
373  //Check whether some data is pending in the buffer
374  if(context->size == 0 && length >= 64)
375  {
376  //The length must be a multiple of 64 bytes
377  n = length - (length % 64);
378 
379  //Update hash value
380  hashProcessData(ICM_CFG_UALGO_SHA256_Val, data, n, context->h);
381 
382  //Update the SHA-256 context
383  context->totalSize += n;
384  //Advance the data pointer
385  data = (uint8_t *) data + n;
386  //Remaining bytes to process
387  length -= n;
388  }
389  else
390  {
391  //The buffer can hold at most 64 bytes
392  n = MIN(length, 64 - context->size);
393 
394  //Copy the data to the buffer
395  osMemcpy(context->buffer + context->size, data, n);
396 
397  //Update the SHA-256 context
398  context->size += n;
399  context->totalSize += n;
400  //Advance the data pointer
401  data = (uint8_t *) data + n;
402  //Remaining bytes to process
403  length -= n;
404 
405  //Check whether the buffer is full
406  if(context->size == 64)
407  {
408  //Update hash value
409  hashProcessData(ICM_CFG_UALGO_SHA256_Val, context->buffer,
410  context->size, context->h);
411 
412  //Empty the buffer
413  context->size = 0;
414  }
415  }
416  }
417 }
418 
419 
420 /**
421  * @brief Finish the SHA-256 message digest
422  * @param[in] context Pointer to the SHA-256 context
423  * @param[out] digest Calculated digest
424  **/
425 
426 void sha256Final(Sha256Context *context, uint8_t *digest)
427 {
428  uint_t i;
429  size_t paddingSize;
430  uint64_t totalSize;
431 
432  //Length of the original message (before padding)
433  totalSize = context->totalSize * 8;
434 
435  //Pad the message so that its length is congruent to 56 modulo 64
436  if(context->size < 56)
437  {
438  paddingSize = 56 - context->size;
439  }
440  else
441  {
442  paddingSize = 64 + 56 - context->size;
443  }
444 
445  //Append padding
446  sha256Update(context, padding, paddingSize);
447 
448  //Append the length of the original message
449  for(i = 0; i < 8; i++)
450  {
451  context->buffer[63 - i] = totalSize & 0xFF;
452  totalSize >>= 8;
453  }
454 
455  //Calculate the message digest
456  hashProcessData(ICM_CFG_UALGO_SHA256_Val, context->buffer, 64, context->h);
457 
458  //Copy the resulting digest
459  for(i = 0; i < (SHA256_DIGEST_SIZE / 4); i++)
460  {
461  STORE32LE(context->h[i], digest + i * 4);
462  }
463 }
464 
465 
466 /**
467  * @brief Finish the SHA-256 message digest (no padding added)
468  * @param[in] context Pointer to the SHA-256 context
469  * @param[out] digest Calculated digest
470  **/
471 
472 void sha256FinalRaw(Sha256Context *context, uint8_t *digest)
473 {
474  uint_t i;
475 
476  //Copy the resulting digest
477  for(i = 0; i < (SHA256_DIGEST_SIZE / 4); i++)
478  {
479  STORE32LE(context->h[i], digest + i * 4);
480  }
481 }
482 
483 #endif
484 #endif
SHA-256 algorithm context.
Definition: sha256.h:62
ICM region descriptor.
uint32_t rnext
ICM region next address.
void sha224Init(Sha224Context *context)
Initialize SHA-224 message digest context.
uint8_t data[]
Definition: ethernet.h:222
#define STORE32LE(a, p)
Definition: cpu_endian.h:279
size_t size
Definition: sha256.h:69
void sha256Update(Sha256Context *context, const void *data, size_t length)
Update the SHA-256 context with a portion of the message being hashed.
uint32_t h[8]
Definition: sha256.h:63
#define BETOH32(value)
Definition: cpu_endian.h:451
SAMD51 hash hardware accelerator.
void sha1FinalRaw(Sha1Context *context, uint8_t *digest)
Finish the SHA-1 message digest (no padding added)
uint64_t totalSize
Definition: sha1.h:70
void sha1Final(Sha1Context *context, uint8_t *digest)
Finish the SHA-1 message digest.
void sha1Init(Sha1Context *context)
Initialize SHA-1 message digest context.
uint8_t h
Definition: ndp.h:302
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
uint32_t rcfg
ICM region configuration.
OsMutex samd51CryptoMutex
Definition: samd51_crypto.c:45
uint32_t h[5]
Definition: sha1.h:63
General definitions for cryptographic algorithms.
uint32_t rctrl
ICM region control.
uint8_t length
Definition: tcp.h:375
uint8_t buffer[64]
Definition: sha256.h:67
#define ICM_RCFG_ALGO
#define MIN(a, b)
Definition: os_port.h:63
Collection of hash algorithms.
#define SHA1_DIGEST_SIZE
Definition: sha1.h:45
uint8_t n
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
uint32_t raddr
ICM region start address.
size_t size
Definition: sha1.h:69
void sha256Final(Sha256Context *context, uint8_t *digest)
Finish the SHA-256 message digest.
SHA-1 algorithm context.
Definition: sha1.h:62
uint8_t buffer[64]
Definition: sha1.h:67
uint64_t totalSize
Definition: sha256.h:70
unsigned int uint_t
Definition: compiler_port.h:57
#define SHA256_DIGEST_SIZE
Definition: sha256.h:45
void sha256Init(Sha256Context *context)
Initialize SHA-256 message digest context.
void sha256FinalRaw(Sha256Context *context, uint8_t *digest)
Finish the SHA-256 message digest (no padding added)
SAMD51 hardware cryptographic accelerator.
__attribute__((naked))
AVR32 Ethernet MAC interrupt wrapper.
Debugging facilities.
void hashProcessData(uint32_t algo, const uint8_t *data, size_t length, uint32_t *h)
Update hash value.
void sha1Update(Sha1Context *context, const void *data, size_t length)
Update the SHA-1 context with a portion of the message being hashed.