1 /*
2 * Copyright (c) 2019-2021, Texas Instruments Incorporated
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * * Neither the name of Texas Instruments Incorporated nor the names of
17 * its contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <stdint.h>
34 #include <stdbool.h>
35 #include <string.h>
36
37 #include <ti/drivers/dpl/DebugP.h>
38
39 #include <ti/drivers/cryptoutils/utils/CryptoUtils.h>
40
41 #if defined(__GNUC__) || defined(__clang__)
42 #define CRYPTOUTILS_NOINLINE __attribute__((noinline))
43 #else
44 #define CRYPTOUTILS_NOINLINE
45 #endif
46
47 #define CryptoUtils_LIMIT_MASK (0xFFFFFFFEu)
48
49 /*
50 * These constants take values at the very top of the memory map where it is unreasonable
51 * for an application to have stored a different number value.
52 */
53 #define CryptoUtils_LIMIT_ZERO 0xFFFFFFFEu
54 #define CryptoUtils_LIMIT_ONE 0xFFFFFFFFu
55
56 const uint8_t *CryptoUtils_limitZero = (uint8_t *)CryptoUtils_LIMIT_ZERO;
57 const uint8_t *CryptoUtils_limitOne = (uint8_t *)CryptoUtils_LIMIT_ONE;
58
59 /*
60 * ======== CryptoUtils_buffersMatch ========
61 */
62 #if defined(__IAR_SYSTEMS_ICC__)
63 #pragma inline = never
64 #elif defined(__TI_COMPILER_VERSION__) && !defined(__cplusplus)
65 #pragma FUNC_CANNOT_INLINE(CryptoUtils_buffersMatch)
66 #elif defined(__TI_COMPILER_VERSION__)
67 #pragma FUNC_CANNOT_INLINE
68 #endif
CryptoUtils_buffersMatch(const volatile void * volatile buffer0,const volatile void * volatile buffer1,size_t bufferByteLength)69 CRYPTOUTILS_NOINLINE bool CryptoUtils_buffersMatch(const volatile void *volatile buffer0,
70 const volatile void *volatile buffer1,
71 size_t bufferByteLength)
72 {
73 volatile uint8_t tempResult = 0;
74 uint8_t byte0;
75 uint8_t byte1;
76 size_t i;
77
78 /* XOR each byte of the buffer together and OR the results.
79 * If the OR'd result is non-zero, the buffers do not match.
80 * There is no branch based on the content of the buffers here to avoid
81 * timing attacks.
82 */
83 for (i = 0; i < bufferByteLength; i++)
84 {
85 byte0 = ((uint8_t *)buffer0)[i];
86 byte1 = ((uint8_t *)buffer1)[i];
87
88 tempResult |= byte0 ^ byte1;
89 }
90
91 return tempResult == 0;
92 }
93
94 /*
95 * ======== CryptoUtils_buffersMatchWordAligned ========
96 */
97 #if defined(__IAR_SYSTEMS_ICC__)
98 #pragma inline = never
99 #elif defined(__TI_COMPILER_VERSION__) && !defined(__cplusplus)
100 #pragma FUNC_CANNOT_INLINE(CryptoUtils_buffersMatchWordAligned)
101 #elif defined(__TI_COMPILER_VERSION__)
102 #pragma FUNC_CANNOT_INLINE
103 #endif
CryptoUtils_buffersMatchWordAligned(const volatile uint32_t * volatile buffer0,const volatile uint32_t * volatile buffer1,size_t bufferByteLength)104 CRYPTOUTILS_NOINLINE bool CryptoUtils_buffersMatchWordAligned(const volatile uint32_t *volatile buffer0,
105 const volatile uint32_t *volatile buffer1,
106 size_t bufferByteLength)
107 {
108 volatile uint32_t tempResult = 0;
109 uint32_t word0;
110 uint32_t word1;
111 size_t i;
112
113 /* We could skip the branch and just set tempResult equal to the
114 * statement below for the same effect but this is more explicit.
115 */
116 if (bufferByteLength % sizeof(uint32_t) != 0)
117 {
118 return false;
119 }
120
121 /* XOR each 32-bit word of the buffer together and OR the results.
122 * If the OR'd result is non-zero, the buffers do not match.
123 * There is no branch based on the content of the buffers here to avoid
124 * timing attacks.
125 */
126 for (i = 0; i < bufferByteLength / sizeof(uint32_t); i++)
127 {
128 word0 = buffer0[i];
129 word1 = buffer1[i];
130
131 tempResult |= word0 ^ word1;
132 }
133
134 return tempResult == 0;
135 }
136
137 /*
138 * ======== CryptoUtils_reverseBufferBytewise ========
139 */
CryptoUtils_reverseBufferBytewise(void * buffer,size_t bufferByteLength)140 void CryptoUtils_reverseBufferBytewise(void *buffer, size_t bufferByteLength)
141 {
142 uint8_t *bufferLow = buffer;
143 uint8_t *bufferHigh = bufferLow + bufferByteLength - 1;
144 uint8_t tmp;
145
146 while (bufferLow < bufferHigh)
147 {
148 tmp = *bufferLow;
149 *bufferLow = *bufferHigh;
150 *bufferHigh = tmp;
151 bufferLow++;
152 bufferHigh--;
153 }
154 }
155
156 /*
157 * ======== CryptoUtils_isBufferAllZeros ========
158 */
CryptoUtils_isBufferAllZeros(const void * buffer,size_t bufferByteLength)159 bool CryptoUtils_isBufferAllZeros(const void *buffer, size_t bufferByteLength)
160 {
161 uint32_t i;
162 uint8_t bufferBits = 0;
163
164 for (i = 0; i < bufferByteLength; i++)
165 {
166 bufferBits |= ((uint8_t *)buffer)[i];
167 }
168
169 return bufferBits == 0;
170 }
171
172 /*
173 * ======== CryptoUtils_memset ========
174 */
CryptoUtils_memset(void * dest,size_t destSize,uint8_t val,size_t count)175 void CryptoUtils_memset(void *dest, size_t destSize, uint8_t val, size_t count)
176 {
177 DebugP_assert(dest);
178 DebugP_assert(count <= destSize);
179
180 volatile uint8_t *volatile p = (volatile uint8_t *)dest;
181
182 while (destSize-- && count--)
183 {
184 *p++ = val;
185 }
186 }
187
188 /*
189 * ======== CryptoUtils_copyPad ========
190 */
CryptoUtils_copyPad(const void * source,uint32_t * destination,size_t sourceLength)191 void CryptoUtils_copyPad(const void *source, uint32_t *destination, size_t sourceLength)
192 {
193 uint32_t i;
194 uint8_t remainder;
195 uint32_t temp;
196 uint8_t *tempBytePointer;
197 const uint8_t *sourceBytePointer;
198
199 remainder = sourceLength % sizeof(uint32_t);
200 temp = 0;
201 tempBytePointer = (uint8_t *)&temp;
202 sourceBytePointer = (uint8_t *)source;
203
204 /* Copy source to destination starting at the end of source and the
205 * beginning of destination.
206 * We assemble each word in normal order and write one word at a
207 * time since the PKA_RAM requires word-aligned reads and writes.
208 */
209
210 for (i = 0; i < sourceLength / sizeof(uint32_t); i++)
211 {
212 uint32_t sourceOffset = sizeof(uint32_t) * i;
213
214 tempBytePointer[0] = sourceBytePointer[sourceOffset + 0];
215 tempBytePointer[1] = sourceBytePointer[sourceOffset + 1];
216 tempBytePointer[2] = sourceBytePointer[sourceOffset + 2];
217 tempBytePointer[3] = sourceBytePointer[sourceOffset + 3];
218
219 *(destination + i) = temp;
220 }
221
222 /* Reset to 0 so we do not have to zero-out individual bytes */
223 temp = 0;
224
225 /* If sourceLength is not a word-multiple, we need to copy over the
226 * remaining bytes and zero pad the word we are writing to PKA_RAM.
227 */
228 if (remainder == 1)
229 {
230
231 tempBytePointer[0] = sourceBytePointer[0];
232
233 /* i is reused from the loop above. This write zero-pads the
234 * destination buffer to word-length.
235 */
236 *(destination + i) = temp;
237 }
238 else if (remainder == 2)
239 {
240
241 tempBytePointer[0] = sourceBytePointer[0];
242 tempBytePointer[1] = sourceBytePointer[1];
243
244 *(destination + i) = temp;
245 }
246 else if (remainder == 3)
247 {
248
249 tempBytePointer[0] = sourceBytePointer[0];
250 tempBytePointer[1] = sourceBytePointer[1];
251 tempBytePointer[2] = sourceBytePointer[2];
252
253 *(destination + i) = temp;
254 }
255 }
256
257 /*
258 * ======== CryptoUtils_reverseCopyPad ========
259 */
CryptoUtils_reverseCopyPad(const void * source,uint32_t * destination,size_t sourceLength)260 void CryptoUtils_reverseCopyPad(const void *source, uint32_t *destination, size_t sourceLength)
261 {
262 uint32_t i;
263 uint8_t remainder;
264 uint32_t temp;
265 uint8_t *tempBytePointer;
266 const uint8_t *sourceBytePointer;
267
268 remainder = sourceLength % sizeof(uint32_t);
269 temp = 0;
270 tempBytePointer = (uint8_t *)&temp;
271 sourceBytePointer = (uint8_t *)source;
272
273 /* Copy source to destination starting at the end of source and the
274 * beginning of destination.
275 * We assemble each word in byte-reversed order and write one word at a
276 * time since the PKA_RAM requires word-aligned reads and writes.
277 */
278
279 for (i = 0; i < sourceLength / sizeof(uint32_t); i++)
280 {
281 uint32_t sourceOffset = sourceLength - 1 - sizeof(uint32_t) * i;
282
283 tempBytePointer[3] = sourceBytePointer[sourceOffset - 3];
284 tempBytePointer[2] = sourceBytePointer[sourceOffset - 2];
285 tempBytePointer[1] = sourceBytePointer[sourceOffset - 1];
286 tempBytePointer[0] = sourceBytePointer[sourceOffset - 0];
287
288 *(destination + i) = temp;
289 }
290
291 /* Reset to 0 so we do not have to zero-out individual bytes */
292 temp = 0;
293
294 /* If sourceLength is not a word-multiple, we need to copy over the
295 * remaining bytes and zero pad the word we are writing to PKA_RAM.
296 */
297 if (remainder == 1)
298 {
299
300 tempBytePointer[0] = sourceBytePointer[0];
301
302 /* i is reused from the loop above. This write zero-pads the
303 * destination buffer to word-length.
304 */
305 *(destination + i) = temp;
306 }
307 else if (remainder == 2)
308 {
309
310 tempBytePointer[0] = sourceBytePointer[1];
311 tempBytePointer[1] = sourceBytePointer[0];
312
313 *(destination + i) = temp;
314 }
315 else if (remainder == 3)
316 {
317
318 tempBytePointer[0] = sourceBytePointer[2];
319 tempBytePointer[1] = sourceBytePointer[1];
320 tempBytePointer[2] = sourceBytePointer[0];
321
322 *(destination + i) = temp;
323 }
324 }
325
326 /*
327 * ======== CryptoUtils_reverseCopy ========
328 */
CryptoUtils_reverseCopy(const void * source,void * destination,size_t sourceLength)329 void CryptoUtils_reverseCopy(const void *source, void *destination, size_t sourceLength)
330 {
331 /*
332 * If destination address is word-aligned and source length is a word-multiple,
333 * use CryptoUtils_reverseCopyPad() for better efficiency.
334 */
335 if ((((uint32_t)destination | sourceLength) & 0x3) == 0)
336 {
337 CryptoUtils_reverseCopyPad(source, (uint32_t *)destination, sourceLength);
338 }
339 else
340 {
341 const uint8_t *sourceBytePtr = (const uint8_t *)source;
342 uint8_t *dstBytePtr = (uint8_t *)destination + sourceLength - 1;
343
344 /*
345 * Copy source to destination starting at the end of source and the
346 * beginning of destination.
347 */
348 while (sourceLength--)
349 {
350 *dstBytePtr-- = *sourceBytePtr++;
351 }
352 }
353 }
354
355 /* limitValue must be either CryptoUtils_LIMIT_ZERO or CryptoUtils_LIMIT_ONE */
CryptoUtils_convertLimitValueToInt(const void * limitValue)356 static int16_t CryptoUtils_convertLimitValueToInt(const void *limitValue)
357 {
358 int16_t value = 0;
359
360 if (limitValue == CryptoUtils_limitOne)
361 {
362 value = 1;
363 }
364
365 return value;
366 }
367
368 /*
369 * Returns number1[offset] - number2[offset].
370 *
371 * Can handle one of number1 or number2 (but not both) being one of the special limit values of
372 * CryptoUtils_LIMIT_ZERO or CryptoUtils_LIMIT_ONE.
373 *
374 * All pointer parameters must be non-NULL.
375 */
CryptoUtils_diffAtOffset(const uint8_t number1[],const uint8_t number2[],size_t offset,size_t lsbOffset)376 static int16_t CryptoUtils_diffAtOffset(const uint8_t number1[],
377 const uint8_t number2[],
378 size_t offset,
379 size_t lsbOffset)
380 {
381
382 int16_t diff;
383
384 /* Look at number2 first, as it will be more common for number2 to be one of the limit values. */
385 if (number2 == CryptoUtils_limitZero)
386 {
387 diff = (int16_t)number1[offset];
388 }
389 else if (number2 == CryptoUtils_limitOne)
390 {
391 if (offset == lsbOffset)
392 {
393 diff = (int16_t)number1[offset] - 1;
394 }
395 else
396 {
397 diff = (int16_t)number1[offset];
398 }
399 }
400 else if (number1 == CryptoUtils_limitZero)
401 {
402 diff = 0 - (int16_t)number1[offset];
403 }
404 else if (number1 == CryptoUtils_limitOne)
405 {
406 if (offset == lsbOffset)
407 {
408 diff = 1 - (int16_t)number1[offset];
409 }
410 else
411 {
412 diff = 0 - (int16_t)number1[offset];
413 }
414 }
415 else
416 {
417 diff = (int16_t)number1[offset] - (int16_t)number2[offset];
418 }
419
420 return diff;
421 }
422
423 /* Uses a timing constant algorithm to return 0 if value is 0 and return 1 otherwise. */
CryptoUtils_valueNonZeroTimingConstantCheck(int16_t value)424 static uint16_t CryptoUtils_valueNonZeroTimingConstantCheck(int16_t value)
425 {
426 uint16_t valueNonZero;
427
428 /* Mask and shift bits such that if any bit in value is '1' then the
429 lsb of valueNonZero is 1 and otherwise valueNonZero is 0. */
430 valueNonZero = (((uint16_t)value & 0xFF00u) >> 8u) | ((uint8_t)value & 0xFFu);
431 valueNonZero = ((valueNonZero & 0xF0u) >> 4u) | (valueNonZero & 0x0Fu);
432 valueNonZero = ((valueNonZero & 0x0Cu) >> 2u) | (valueNonZero & 0x03u);
433 valueNonZero = ((valueNonZero & 0x02u) >> 1u) | (valueNonZero & 0x01u);
434
435 return valueNonZero;
436 }
437
438 /*
439 * Returns sign of number1 - number2:
440 * negative value if number2 is larger, positive value if number1 is larger and zero if numbers are equal.
441 *
442 * Note that the magnitude of the return value has no meaning.
443 *
444 * The comparison is performed with a time-constant algorithm with respect to either of the number
445 * arguments (number1, number2) when those inputs are not CryptoUtils_limitZero or CryptoUtils_limitOne.
446 *
447 * All pointer parameters must be non-NULL.
448 */
CryptoUtils_compareNumbers(const uint8_t number1[],const uint8_t number2[],size_t byteLength,CryptoUtils_Endianess endianess)449 static int16_t CryptoUtils_compareNumbers(const uint8_t number1[],
450 const uint8_t number2[],
451 size_t byteLength,
452 CryptoUtils_Endianess endianess)
453 {
454 int16_t result = 0x0;
455 int16_t diff;
456 uint16_t diffNonZero;
457 uint16_t diffResultMask;
458 uint16_t resultUnknown = 0xFFFFu;
459 uintptr_t number1Address;
460 uintptr_t number2Address;
461 size_t i;
462
463 number1Address = (uintptr_t)number1;
464 number2Address = (uintptr_t)number2;
465
466 /*
467 * Check if special RNG_limit values are being used for both values.
468 * This is not expected, but is handled for completeness.
469 */
470 if (((number1Address & CryptoUtils_LIMIT_MASK) == CryptoUtils_LIMIT_MASK) &&
471 ((number2Address & CryptoUtils_LIMIT_MASK) == CryptoUtils_LIMIT_MASK))
472 {
473
474 result = CryptoUtils_convertLimitValueToInt(number1) - CryptoUtils_convertLimitValueToInt(number2);
475 }
476 else if (number1 != number2)
477 {
478 if (endianess == CryptoUtils_ENDIANESS_BIG)
479 {
480 i = 0u;
481 while (i < byteLength)
482 {
483 diff = CryptoUtils_diffAtOffset(number1, number2, i, byteLength - 1);
484
485 /* Update result only if result was not known and is thus currently set to 0. */
486 result = (int16_t)((uint16_t)result | (resultUnknown & (uint16_t)diff));
487
488 /*
489 * Determine if result is now known and update resultUnknown
490 */
491
492 diffNonZero = CryptoUtils_valueNonZeroTimingConstantCheck(diff);
493
494 /* Create mask where mask value is 0 if bytes were equal, otherwise mask is all 1s. */
495 diffResultMask = diffNonZero - 1u;
496
497 /* Set resultUnknown to 0 (indicating result is known) if bytes were not equal. */
498 resultUnknown &= diffResultMask;
499
500 i++;
501 }
502 }
503 else
504 {
505 i = byteLength;
506 while (i > 0u)
507 {
508 i--;
509
510 diff = CryptoUtils_diffAtOffset(number1, number2, i, 0);
511
512 /* Update result only if result was not known and is thus currently set to 0. */
513 result = (int16_t)((uint16_t)result | (resultUnknown & (uint16_t)diff));
514
515 /*
516 * Determine if result is now known and update resultUnknown
517 */
518 diffNonZero = CryptoUtils_valueNonZeroTimingConstantCheck(diff);
519
520 /* Create mask where mask value is 0 if bytes were equal, otherwise mask is all 1s. */
521 diffResultMask = diffNonZero - 1u;
522
523 /* Set resultUnknown to 0 (indicating result is known) if bytes were not equal. */
524 resultUnknown &= diffResultMask;
525 }
526 }
527 }
528 else
529 {
530 result = 0;
531 }
532
533 return result;
534 }
535
536 /*
537 * ======== CryptoUtils_isNumberInRange ========
538 */
CryptoUtils_isNumberInRange(const void * number,size_t bitLength,CryptoUtils_Endianess endianess,const void * lowerLimit,const void * upperLimit)539 bool CryptoUtils_isNumberInRange(const void *number,
540 size_t bitLength,
541 CryptoUtils_Endianess endianess,
542 const void *lowerLimit,
543 const void *upperLimit)
544 {
545 int16_t upperResult;
546 int16_t lowerResult;
547 bool inUpperLimit = true;
548 bool inLowerLimit = true;
549 size_t byteLength;
550
551 byteLength = (bitLength + 7u) >> 3u;
552
553 if (upperLimit != NULL)
554 {
555 upperResult = CryptoUtils_compareNumbers(number, upperLimit, byteLength, endianess);
556 if (upperResult >= 0)
557 {
558 inUpperLimit = false;
559 }
560 }
561
562 if (lowerLimit != NULL)
563 {
564 lowerResult = CryptoUtils_compareNumbers(number, lowerLimit, byteLength, endianess);
565 if (lowerResult < 0)
566 {
567 inLowerLimit = false;
568 }
569 }
570
571 return (inUpperLimit && inLowerLimit);
572 }
573