1
0
mirror of https://github.com/wolfSSL/wolfssl.git synced 2026-10-24 04:09:07 +03:00
Files
wolfssl/tests/unit-mcdc/test_poly1305_whitebox.c

410 lines
16 KiB
C

/* test_poly1305_whitebox.c
*
* Copyright (C) 2006-2026 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* White-box supplement for wolfcrypt/src/poly1305.c.
*
* On an x86-64 USE_INTEL_POLY1305_SPEEDUP build (USE_INTEL_SPEEDUP +
* WOLFSSL_X86_64_BUILD, poly1305.h) wc_Poly1305SetKey()/wc_Poly1305Update()/
* wc_Poly1305Final() dispatch through a file-static cpuid mask (intel_flags),
* set once in SetKey and reused by Update/Final:
*
* if (IS_INTEL_AVX2(intel_flags)) poly1305_*_avx2(...);
* else poly1305_*_avx(...);
*
* Each of these is a single-condition branch (not a compound MC/DC decision:
* poly1305.c's own the module registry-measured MC/DC total is unaffected by
* which of these paths a given build takes), so this white-box does not
* change the covered/total counts. It is kept anyway, matching
* the intel-dispatch technique used by the aes/sha3 white-boxes and this
* suite's chacha sibling, for FEATURE/branch-coverage evidence that the
* AVX2-false (AVX1-only) side is reachable and correct: on an AVX2-capable
* CI host, cpuid_get_flags_ex()'s real detection always takes the AVX2
* branch through the public API, so tests/api alone never demonstrates the
* AVX1-only side.
*
* cpuid_get_flags_ex() is idempotent (wolfssl/wolfcrypt/cpuid.h): it only
* re-queries the hardware when the flags word still holds
* WC_CPUID_INITIALIZER. Forcing intel_flags to a real (non-initializer)
* value before calling wc_Poly1305SetKey() makes it trust our forced value
* instead of re-detecting. Crash-safety: this host has real AVX1 hardware
* (see the module registry poly1305 notes), so forcing intel_flags to "AVX1
* only" and letting the dispatch call the real poly1305_*_avx asm is always
* safe: we never claim a capability the CPU lacks, only hide one it has.
*
* ---------------------------------------------------------------------------
* AVX-512 / IFMA / fused-flag dispatch (wb_poly1305_avx512_dispatch)
* ---------------------------------------------------------------------------
* The AVX-512 Poly1305 work turned the single-condition AVX2 test above into
* six compound decisions -- three in wc_Poly1305Update, three in
* wc_Poly1305Final -- with the same shape in both:
*
* if (!POLY1305_FORCE_AVX2(ctx) && !POLY1305_FORCE_SCALAR(ctx) &&
* poly1305_use_ifma(intel_flags)) <- 8-way IFMA, radix 2^44
* if (!POLY1305_FORCE_AVX2(ctx) && !POLY1305_FORCE_SCALAR(ctx) &&
* poly1305_use_avx512(intel_flags)) <- 8-way vpmuludq
* if (!POLY1305_FORCE_SCALAR(ctx) && IS_INTEL_AVX2(intel_flags))
*
* plus the two predicates they call:
*
* poly1305_use_avx512: IS_INTEL_AVX512(f) && IS_INTEL_VAES(f)
* poly1305_use_ifma: IS_INTEL_AVX512_IFMA(f) && IS_CPU_INTEL(f)
*
* Three inputs decide all six: the two ctx flags (set only by the fused
* ChaCha20-Poly1305 kernels in chacha20_poly1305.c, so no poly1305-only caller
* ever sets them) and intel_flags. Any one host reaches exactly one
* combination -- on an AMD host IS_CPU_INTEL is false while AVX-512 and VAES
* are present, so every call takes the 8-way vpmuludq path and the other five
* decisions are never evaluated with a differing operand.
* wb_poly1305_dispatch_case() drives one (intel_flags, forceAvx2, forceScalar)
* triple through SetKey/Update/Final; the table in
* wb_poly1305_avx512_dispatch() is the full combination sweep, every row of it
* in this one binary so that each condition's independence pair is derivable
* here (llvm-cov computes MC/DC per binary).
*
* Crash-safety rule, unchanged from above: a forced mask may only CLEAR
* capability bits the CPU has, never claim one it lacks. Each row's mask is
* built as (wanted & real cpuid_get_flags()), so on a host without AVX-512 the
* AVX-512 rows collapse onto the rows below them and are simply not gained --
* never executed as an unsupported instruction. The one bit forced ON,
* CPUID_INTEL, is a vendor tag rather than a capability: poly1305_use_ifma()'s
* own comment states the gate is a throughput preference between two paths any
* IFMA-capable CPU can run (AMD Zen 4/5 implement IFMA), and the row that lets
* the IFMA kernel actually execute is additionally skipped unless the host
* really reports AVX-512 IFMA.
*
* The ctx-flag rows reproduce, on a poly1305-only context, exactly the state
* the fused kernels set up before calling Update/Final: forceAvx2 -> zero
* ctx->hh, started = 0 (chacha20_poly1305_encrypt_fused); forceScalar -> zero
* ctx->h, finished = 1, leftover = 0, started = 0
* (chacha20_poly1305_encrypt_fused_ifma, wc_ChaCha20Poly1305_UpdateData).
* No MAC value is checked: which kernel computes the right tag is the KAT
* suite's job, this file only drives the dispatch decisions.
*/
#include <wolfcrypt/src/poly1305.c>
#include <stdio.h>
static int wb_fail = 0;
#define WB_NOTE(msg) do { printf(" [wb] %s\n", (msg)); } while (0)
#if defined(HAVE_POLY1305) && defined(USE_INTEL_POLY1305_SPEEDUP)
static void wb_poly1305_dispatch(void)
{
Poly1305 ctx;
static const byte key[32] = {
0x85,0xd6,0xbe,0x78,0x57,0x55,0x6d,0x33,
0x7f,0x44,0x52,0xfe,0x42,0xd5,0x06,0xa8,
0x01,0x03,0x80,0x8a,0xfb,0x0d,0xb2,0xfd,
0x4a,0xbf,0xf6,0xaf,0x41,0x49,0xf5,0x1b
};
/* Multiple blocks so poly1305_blocks_avx() actually runs (not just
* poly1305_block_avx()'s single-block path). */
byte msg[3 * POLY1305_BLOCK_SIZE + 5];
byte tag[WC_POLY1305_MAC_SZ];
cpuid_flags_t saved_flags = intel_flags;
size_t i;
for (i = 0; i < sizeof(msg); i++) {
msg[i] = (byte)i;
}
/* AVX2-false, AVX1-true: forces poly1305_setkey_avx/blocks_avx/
* final_avx. Real hardware capability, so safe to actually execute. */
intel_flags = CPUID_AVX1;
if (wc_Poly1305SetKey(&ctx, key, sizeof(key)) == 0) {
if (wc_Poly1305Update(&ctx, msg, (word32)sizeof(msg)) != 0 ||
wc_Poly1305Final(&ctx, tag) != 0) {
WB_NOTE("Update/Final (AVX1-only) failed");
wb_fail = 1;
}
}
else {
WB_NOTE("wc_Poly1305SetKey failed (AVX1-only case skipped)");
wb_fail = 1;
}
intel_flags = saved_flags;
WB_NOTE("poly1305 intel dispatch AVX1-only side exercised");
}
#else
static void wb_poly1305_dispatch(void)
{
WB_NOTE("USE_INTEL_POLY1305_SPEEDUP not compiled in this variant; "
"skipped");
}
#endif
/* -------------------------------------------------------------------------
* AVX-512 / IFMA / fused-flag dispatch sweep. See the file header.
* ---------------------------------------------------------------------- */
#if defined(HAVE_POLY1305) && defined(USE_INTEL_POLY1305_SPEEDUP)
/* Capability bits are only ever removed from what the CPU really reports, so a
* forced mask can never make the dispatch enter a path the host cannot run. */
static cpuid_flags_t wb_hw(cpuid_flags_t wanted)
{
return wanted & cpuid_get_flags();
}
/* Reproduce the context state the fused ChaCha20-Poly1305 kernels leave behind
* when they pin Update/Final to one path (chacha20_poly1305.c). */
static void wb_poly1305_force(Poly1305* ctx, int forceAvx2, int forceScalar)
{
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED
if (forceAvx2) {
ctx->forceAvx2 = 1;
XMEMSET(ctx->hh, 0, sizeof(ctx->hh));
ctx->started = 0;
ctx->leftover = 0;
}
#else
(void)forceAvx2;
#endif
#ifdef WOLFSSL_CHACHA20_POLY1305_FUSED_IFMA
if (forceScalar) {
ctx->forceScalar = 1;
XMEMSET(ctx->h, 0, sizeof(ctx->h));
ctx->finished = 1;
ctx->started = 0;
ctx->leftover = 0;
}
#else
(void)forceScalar;
#endif
(void)ctx;
}
/* One (intel_flags, forceAvx2, forceScalar) row: key, hash more than the
* 8-block buffer so the block kernels run, and finalise. */
static void wb_poly1305_dispatch_case(cpuid_flags_t flags, int forceAvx2,
int forceScalar, const char* what)
{
static const byte key[32] = {
0x85,0xd6,0xbe,0x78,0x57,0x55,0x6d,0x33,
0x7f,0x44,0x52,0xfe,0x42,0xd5,0x06,0xa8,
0x01,0x03,0x80,0x8a,0xfb,0x0d,0xb2,0xfd,
0x4a,0xbf,0xf6,0xaf,0x41,0x49,0xf5,0x1b
};
Poly1305 ctx;
byte msg[17 * POLY1305_BLOCK_SIZE + 5];
byte tag[WC_POLY1305_MAC_SZ];
size_t i;
for (i = 0; i < sizeof(msg); i++) {
msg[i] = (byte)i;
}
XMEMSET(&ctx, 0, sizeof(ctx));
/* Non-initializer value: cpuid_get_flags_ex() in SetKey keeps it. */
intel_flags = flags;
if (wc_Poly1305SetKey(&ctx, key, (word32)sizeof(key)) != 0) {
printf(" [wb] wc_Poly1305SetKey failed (%s skipped)\n", what);
wb_fail = 1;
return;
}
wb_poly1305_force(&ctx, forceAvx2, forceScalar);
if (wc_Poly1305Update(&ctx, msg, (word32)sizeof(msg)) != 0 ||
wc_Poly1305Final(&ctx, tag) != 0) {
printf(" [wb] Update/Final failed (%s)\n", what);
wb_fail = 1;
}
}
static void wb_poly1305_avx512_dispatch(void)
{
cpuid_flags_t saved_flags = intel_flags;
cpuid_flags_t real = cpuid_get_flags();
/* Common base: the scalar and 4-way kernels this host really has. */
const cpuid_flags_t avx1 = wb_hw(CPUID_AVX1);
const cpuid_flags_t avx2 = wb_hw(CPUID_AVX1 | CPUID_AVX2);
const cpuid_flags_t f512 = wb_hw(CPUID_AVX1 | CPUID_AVX2 | CPUID_AVX512);
const cpuid_flags_t fvaes = wb_hw(CPUID_AVX1 | CPUID_AVX2 | CPUID_AVX512 |
CPUID_VAES | CPUID_AVX512_IFMA);
/* AVX2 absent: IS_INTEL_AVX2 false, every AVX-512 predicate false. */
wb_poly1305_dispatch_case(avx1, 0, 0, "scalar (AVX1 only)");
/* AVX2 present, no AVX-512: the 4-way path. */
wb_poly1305_dispatch_case(avx2, 0, 0, "4-way AVX2");
/* AVX-512 without VAES: poly1305_use_avx512() second operand false. */
wb_poly1305_dispatch_case(f512, 0, 0, "AVX-512 without VAES");
/* AVX-512 + VAES + IFMA, vendor bit absent: use_avx512 true (8-way
* vpmuludq), use_ifma's IS_CPU_INTEL operand false. */
wb_poly1305_dispatch_case(fvaes, 0, 0, "8-way AVX-512, non-Intel vendor");
/* Same, plus the Intel vendor tag: the IFMA path. Only when the host
* really implements AVX-512 IFMA -- the kernel is vpmadd52 asm. */
if (IS_INTEL_AVX512(real) && IS_INTEL_AVX512_IFMA(real)) {
wb_poly1305_dispatch_case(fvaes | CPUID_INTEL, 0, 0,
"8-way AVX-512 IFMA");
}
else {
WB_NOTE("host reports no AVX-512 IFMA; IFMA dispatch row skipped");
}
/* Fused-kernel context flags: each pins the dispatch to one path. */
wb_poly1305_dispatch_case(fvaes, 1, 0, "forceAvx2 (fused 4-way)");
wb_poly1305_dispatch_case(fvaes, 0, 1, "forceScalar (fused IFMA stitch)");
intel_flags = saved_flags;
WB_NOTE("poly1305 AVX-512/IFMA/fused-flag dispatch sweep exercised");
}
#ifdef HAVE_INTEL_AVX512
/* poly1305_use_avx512()/poly1305_use_ifma() are pure predicates over a flags
* word: no state, no instruction from the feature they test. Evaluating them
* directly over the four operand combinations pairs both conditions of each
* without depending on what this host implements, so the two predicates close
* on any machine; the dispatch sweep above then shows the same rows arising
* from real calls wherever the hardware allows. */
static void wb_poly1305_predicates(void)
{
static volatile cpuid_flags_t rows[4];
volatile int sink = 0;
int i;
rows[0] = 0;
rows[1] = CPUID_AVX512 | CPUID_AVX512_IFMA;
rows[2] = CPUID_VAES | CPUID_INTEL;
rows[3] = CPUID_AVX512 | CPUID_AVX512_IFMA | CPUID_VAES | CPUID_INTEL;
for (i = 0; i < 4; i++) {
sink += poly1305_use_avx512(rows[i]);
sink += poly1305_use_ifma(rows[i]);
}
(void)sink;
WB_NOTE("poly1305_use_avx512/use_ifma operand combinations exercised");
}
#else
static void wb_poly1305_predicates(void)
{
WB_NOTE("HAVE_INTEL_AVX512 off; use_avx512/use_ifma predicates skipped");
}
#endif
#else
static void wb_poly1305_avx512_dispatch(void)
{
WB_NOTE("USE_INTEL_POLY1305_SPEEDUP not compiled in this variant; "
"AVX-512 dispatch sweep skipped");
}
static void wb_poly1305_predicates(void)
{
WB_NOTE("USE_INTEL_POLY1305_SPEEDUP not compiled in this variant; "
"predicate sweep skipped");
}
#endif
/* ---- wc_Poly1305SetKey() argument guard (~line 850) ---------------------- *
*
* if ((ctx == NULL) || (key == NULL) || (keySz != 32))
*
* MEASURED RESULT: idx1 (key == NULL) is UNSATISFIABLE, and this function is
* the evidence. wc_Poly1305SetKey() opens with a separate, earlier
*
* if (key == NULL) return BAD_FUNC_ARG;
*
* (~line 836), so by the time the compound at ~850 is evaluated key is
* non-NULL by construction: idx1 is a redundant re-check that can only ever
* be observed FALSE, and no independence pair for it exists through any entry
* point. Calling wc_Poly1305SetKey(ctx, NULL, 32) here returns from the
* earlier guard and never reaches line 850 -- confirmed by the white-box
* binary's own MC/DC record, which shows idx0/idx2 covered and idx1 not.
* The three satisfiable vectors plus the all-false baseline are kept as
* same-binary regression evidence for idx0/idx2.
*
* Also NOT chased (structurally unsatisfiable): wc_Poly1305_Pad()'s
* "(paddingLen > 0) && (paddingLen < WC_POLY1305_PAD_SZ)" idx1. paddingLen is
* computed as "(-(int)lenToPad) & (WC_POLY1305_PAD_SZ - 1)", i.e. masked into
* [0, WC_POLY1305_PAD_SZ-1], so whenever idx1 is evaluated it is TRUE by
* construction -- no call shape can make it false, so no independence pair
* exists.
* ------------------------------------------------------------------------ */
#ifdef HAVE_POLY1305
static void wb_poly1305_setkey_guard(void)
{
Poly1305 ctx;
static const byte key[32] = {
0x85,0xd6,0xbe,0x78,0x57,0x55,0x6d,0x33,
0x7f,0x44,0x52,0xfe,0x42,0xd5,0x06,0xa8,
0x01,0x03,0x80,0x8a,0xfb,0x0d,0xb2,0xfd,
0x4a,0xbf,0xf6,0xaf,0x41,0x49,0xf5,0x1b
};
byte tag[WC_POLY1305_MAC_SZ];
XMEMSET(&ctx, 0, sizeof(ctx));
/* idx0 true: ctx == NULL. */
if (wc_Poly1305SetKey(NULL, key, (word32)sizeof(key)) !=
WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_Poly1305SetKey(ctx==NULL) not rejected");
wb_fail = 1;
}
/* idx0 false, idx1 TRUE: valid ctx, absent key. */
if (wc_Poly1305SetKey(&ctx, NULL, (word32)sizeof(key)) !=
WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_Poly1305SetKey(key==NULL) not rejected");
wb_fail = 1;
}
/* idx0/idx1 false, idx2 true: wrong key size. */
if (wc_Poly1305SetKey(&ctx, key, 16) != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
WB_NOTE("wc_Poly1305SetKey(keySz!=32) not rejected");
wb_fail = 1;
}
/* All-false baseline in the same binary, run to completion. */
if (wc_Poly1305SetKey(&ctx, key, (word32)sizeof(key)) != 0 ||
wc_Poly1305Update(&ctx, key, (word32)sizeof(key)) != 0 ||
wc_Poly1305Final(&ctx, tag) != 0) {
WB_NOTE("wc_Poly1305SetKey valid sequence failed");
wb_fail = 1;
}
WB_NOTE("wc_Poly1305SetKey ctx/key/keySz guard pairs exercised");
}
#else
static void wb_poly1305_setkey_guard(void)
{ WB_NOTE("HAVE_POLY1305 off; wc_Poly1305SetKey guard skipped"); }
#endif
int main(void)
{
setvbuf(stdout, NULL, _IONBF, 0);
printf("poly1305.c white-box supplement\n");
#ifndef HAVE_POLY1305
printf(" HAVE_POLY1305 not defined; nothing to exercise\n");
return 0;
#else
wb_poly1305_dispatch();
wb_poly1305_predicates();
wb_poly1305_avx512_dispatch();
wb_poly1305_setkey_guard();
printf("done (%s)\n", wb_fail ? "with skips" : "ok");
/* Setup failures are surfaced as skips, not test failures: the harness
* treats a nonzero exit as a failed variant and discards its coverage. */
return 0;
#endif
}