blob: 479c54cf76ac345da6bd51fac05e06730de82a5b [file] [edit]
/*
* 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 <openssl/crypto.h>
#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;
}