/* * Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy * in the file LICENSE in the source distribution or at * https://www.openssl.org/source/license.html */ /* * Functional and constant-time tests for CRYPTO_memcmp(). * * CRYPTO_memcmp() must compare its two operands without any control-flow * branch or memory access that depends on the operand *contents* (only the * length is public). * * When built with enable-ct-validation (OPENSSL_CONSTANT_TIME_VALIDATION), * CONSTTIME_SECRET marks the operands as "undefined" for Valgrind's memcheck; * any branch or memory index derived from them then makes Valgrind exit * non-zero. Because the taint is injected here at the call site, this test * verifies whichever CRYPTO_memcmp implementation is actually linked -- the * per-arch assembler version where one exists (x86_64, aarch64, ...), or the * C fallback in crypto/cpuid.c otherwise. Outside a CT build the macros are * no-ops and this is an ordinary functional test. * * The accumulated comparison result is the function's intended *public* * output, so it is declassified before being asserted on; the operand buffers * are declassified too so the (stack) memory does not stay tainted. */ #include #include "internal/nelem.h" #include "internal/constant_time.h" #include "testutil.h" #define MAX_LEN 64 /* * len == 16 exercises the dedicated fast path in several assembler versions * (e.g. crypto/x86_64cpuid.pl); the other lengths exercise the byte loop and * the empty-input early return. */ static const struct { /* byte length of buffers to compare; must be <= MAX_LEN */ size_t len; /* index at which the second buffer differs, or -1 for equal buffers */ int diff_pos; } memcmp_cases[] = { /* empty: always equal */ { 0, -1 }, { 1, -1 }, { 1, 0 }, /* asm fast path (length = 16) */ { 16, -1 }, { 16, 0 }, { 16, 8 }, { 16, 15 }, /* byte loop */ { 64, -1 }, { 64, 0 }, { 64, 31 }, { 64, 63 }, }; static int test_crypto_memcmp(int idx) { size_t i; size_t len = memcmp_cases[idx].len; int diff_pos = memcmp_cases[idx].diff_pos; /* nonzero result iff buffers differ */ int expected = diff_pos >= 0; unsigned char a[MAX_LEN], b[MAX_LEN]; int result; for (i = 0; i < len; i++) a[i] = b[i] = (unsigned char)(i * 7 + 1); if (diff_pos >= 0) b[diff_pos] ^= 0xff; CONSTTIME_SECRET(a, len); CONSTTIME_SECRET(b, len); result = CRYPTO_memcmp(a, b, len); CONSTTIME_DECLASSIFY(&result, sizeof(result)); CONSTTIME_DECLASSIFY(a, len); CONSTTIME_DECLASSIFY(b, len); if (!TEST_int_eq(result != 0, expected)) return 0; else return 1; } int setup_tests(void) { ADD_ALL_TESTS(test_crypto_memcmp, OSSL_NELEM(memcmp_cases)); return 1; }