--- /n/sources/plan9/sys/include/libsec.h Sat Jun 14 00:00:00 2025 +++ /sys/include/libsec.h Tue Jun 23 00:00:00 2026 @@ -466,3 +466,5 @@ int hkdfExpand(uchar*, DigestState*(*)(uchar*, ulong, uchar*, DigestState*), int, uchar*, int, uchar*, int, int); int hkdfKey(uchar*, DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, uchar*, int, uchar*, int, uchar*, int, int); +/* timing safe memcmp() */ +int tsmemcmp(void*, void*, ulong); --- /dev/null Thu Jan 1 00:00:00 1970 +++ /sys/src/libsec/port/tsmemcmp.c Tue Jun 23 00:00:00 2026 @@ -0,0 +1,26 @@ +#include +#include +#include + +/* + * timing safe memcmp() + */ +int +tsmemcmp(void *a1, void *a2, ulong n) +{ + int lt, gt, c1, c2, r, m; + uchar *s1, *s2; + + r = m = 0; + s1 = a1; + s2 = a2; + while(n--){ + c1 = *s1++; + c2 = *s2++; + lt = (c1 - c2) >> 8; + gt = (c2 - c1) >> 8; + r |= (lt - gt) & ~m; + m |= lt | gt; + } + return r; +} --- /n/sources/plan9/sys/src/libsec/port/mkfile Sat Jun 14 00:00:00 2025 +++ /sys/src/libsec/port/mkfile Tue Jun 23 00:00:00 2026 @@ -20,6 +20,7 @@ tlshand.c thumb.c readcert.c \ pbkdf2.c\ hkdf.c\ + tsmemcmp.c\ ALLOFILES=${CFILES:%.c=%.$O} --- /n/sources/plan9/sys/man/2/memory Sat Jun 14 00:00:00 2025 +++ /sys/man/2/memory Tue Jun 23 00:00:00 2026 @@ -1,6 +1,6 @@ .TH MEMORY 2 .SH NAME -memccpy, memchr, memcmp, memcpy, memmove, memset \- memory operations +memccpy, memchr, memcmp, memcpy, memmove, memset, tsmemcmp \- memory operations .SH SYNOPSIS .B #include .br @@ -17,6 +17,9 @@ int memcmp(void *s1, void *s2, ulong n) .PP .B +int tsmemcmp(void *s1, void *s2, ulong n) +.PP +.B void* memcpy(void *s1, void *s2, ulong n) .PP .B @@ -124,3 +127,12 @@ and .I memmove are handed a negative count, they abort. +.PP +.I Tsmemcmp +is a variant of +.I memcmp +that is safe against timing attacks: its runtime depends only on +.I n +and not on where the inputs first differ. +.I Memcmp +should not be used to compare sensitive data such as authenticators.