summaryrefslogtreecommitdiff
path: root/board/cr50/dcrypto/compare.c
diff options
context:
space:
mode:
Diffstat (limited to 'board/cr50/dcrypto/compare.c')
-rw-r--r--board/cr50/dcrypto/compare.c70
1 files changed, 61 insertions, 9 deletions
diff --git a/board/cr50/dcrypto/compare.c b/board/cr50/dcrypto/compare.c
index db6193752b..09bbd36fd1 100644
--- a/board/cr50/dcrypto/compare.c
+++ b/board/cr50/dcrypto/compare.c
@@ -5,16 +5,68 @@
#include "dcrypto.h"
-/* Constant time comparator. */
-int DCRYPTO_equals(const void *a, const void *b, size_t len)
+/**
+ * CRYPTO_FAST_COMPARE = 1 will enable machine word reads if performance
+ * is important, but will increase code size by ~100 bytes.
+ */
+#define CRYPTO_FAST_COMPARE 0
+
+/* Constant time, hardened comparator. */
+enum dcrypto_result __attribute__((optimize("-O1"))) DCRYPTO_equals(
+ const void *a, const void *b, size_t len)
{
- size_t i;
- const uint8_t *pa = a;
- const uint8_t *pb = b;
- uint8_t diff = 0;
+ uintptr_t a_addr = (uintptr_t)a;
+ uintptr_t b_addr = (uintptr_t)b;
+ uintptr_t tail = a_addr + len;
+ uintptr_t tail_copy = value_barrier(tail);
+ uintptr_t diff = 0;
+
+#if CRYPTO_FAST_COMPARE
+ /* Set 'body' to the last word boundary. */
+ uintptr_t body = tail & ~WORD_MASK;
+
+ /* End of head is the tail if data is not aligned. */
+ uintptr_t head = tail;
+
+ /* Compute starting address if src and dest can be aligned. */
+ if (((a_addr & WORD_MASK) == (b_addr & WORD_MASK)) &&
+ (len >= sizeof(uintptr_t)))
+ /* Set 'head' to the first word boundary. */
+ head = ((a_addr + WORD_MASK) & ~WORD_MASK);
+
+ /* Process misaligned head. */
+ while (a_addr < head) {
+ diff |= *((volatile uint8_t *)a_addr) ^
+ *((volatile uint8_t *)b_addr);
+ a_addr++;
+ b_addr++;
+ }
- for (i = 0; i < len; i++)
- diff |= pa[i] ^ pb[i];
+ /* Process aligned body (if any). */
+ while (a_addr < body) {
+ diff |= *((volatile uintptr_t *)a_addr) ^
+ *((volatile uintptr_t *)b_addr);
+ a_addr += sizeof(uintptr_t);
+ b_addr += sizeof(uintptr_t);
+ }
+#endif
+ /* Process remaining part. Also serves as fault resistance. */
+ while (a_addr < tail) {
+ diff |= *((volatile uint8_t *)a_addr) ^
+ *((volatile uint8_t *)b_addr);
+ a_addr++;
+ b_addr++;
+ }
- return !diff;
+ /**
+ * b_addr = src2 + len
+ * tail, a_addr = src1 + len
+ * (src2 + len) - (src1 + len) + src1 - src2 = 0
+ * Any other result of expression will result in wrong value.
+ * Don't use 'src2_addr' as it is possible to make
+ * b_addr == a_addr
+ */
+ return dcrypto_ok_if_zero((value_barrier(b_addr) - tail_copy +
+ (uintptr_t)a - (uintptr_t)b) |
+ diff);
}