TI C2000 example: init the software AES context, honour ENTROPY_NUM_SRC, report per-window raw entropy

pull/617/head
David Garske 2026-09-01 15:12:03 -07:00
parent 104cab75a0
commit def00ad02d
7 changed files with 282 additions and 55 deletions

View File

@ -0,0 +1,30 @@
/* entropy_probe.h
*
* 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
*/
#ifndef ENTROPY_PROBE_H
#define ENTROPY_PROBE_H
/* Raw entropy-source characterization image (make ENTROPY_PROBE=1).
* Dumps unconditioned samples over SCI; never returns. */
void entropy_probe_run(void);
#endif /* ENTROPY_PROBE_H */

View File

@ -310,6 +310,13 @@ endif
# can estimate min-entropy. Measurement only - no crypto runs.
ENTROPY_PROBE ?= 0
ifeq ($(ENTROPY_PROBE),1)
ifneq ($(SIGN)$(MLDSA)$(BENCH),000)
$(error ENTROPY_PROBE=1 is a standalone measurement image - build it on its own)
endif
# Nothing after entropy_probe_run() executes, so linking wolfcrypt_test and
# benchmark would only bloat the image.
CFLAGS += --define=NO_CRYPT_TEST --define=NO_CRYPT_BENCHMARK
HARNESS_SRCS :=
CFLAGS += --define=WOLF_ENTROPY_PROBE --define=NO_CRYPT_TEST \
--define=NO_CRYPT_BENCHMARK
HARNESS_EXTRA += $(CURDIR)/Source/entropy_probe.c

View File

@ -117,7 +117,7 @@ ML-DSA-87 (asymmetric, @150 MHz): verify ~225 ms/op; keygen and signing also run
The F28P550SJ has an on-chip AES accelerator ("AESA", a TI EIP-120t at `0x00042000`) that C2000Ware exposes through `driverlib/f28p55x/driverlib/aes.h`. `HWAES=1` offloads AES-ECB/CBC/CTR to it via the wolfCrypt crypto-callback framework (`wolfcrypt/src/port/ti/ti-c2000-aes.c` in the wolfSSL tree, gated on `WOLFSSL_C2000_AES`). `driverlib.lib` is already linked by this example, so no extra build plumbing is needed.
Software AES stays compiled in. A context opts into hardware with `wc_AesInit(&aes, NULL, WOLFSSL_C2000_DEVID)`; one initialised with `INVALID_DEVID` runs pure software. `wolf_aes_hw_test()` uses both and compares them, which is the point: on a 16-bit-byte target the octet marshalling into the accelerator's 32-bit registers is the highest-risk part of the port, and a mismatch is exactly what you want to see. The harness prints 13 lines covering ECB/CBC/CTR at 128/192/256 bits, multi-block, split calls, in-place decrypt and a non-block-aligned CTR split, each checked against software and (for the first block of each mode) against the published NIST SP800-38A vector.
Software AES stays compiled in. A context opts into hardware with `wc_AesInit(&aes, NULL, WOLFSSL_C2000_DEVID)`; one initialised with `INVALID_DEVID` runs pure software. `wolf_aes_hw_test()` uses both and compares them, which is the point: on a 16-bit-byte target the octet marshalling into the accelerator's 32-bit registers is the highest-risk part of the port, and a mismatch is exactly what you want to see. The harness prints 18 lines covering ECB/CBC/CTR at 128/192/256 bits, multi-block, split calls, in-place decrypt and a non-block-aligned CTR split, each checked against software and (for the first block of each mode) against the published NIST SP800-38A vector.
`HWAES=1` also defines `WC_USE_DEVID=0x2000` so `wolfcrypt_test` and `benchmark` exercise the device too -- without it they init every context with `INVALID_DEVID` and silently measure software.

View File

@ -48,6 +48,13 @@
/* Counter1 counts down from 0xFFFFF, capping the window near 34,900 INTOSC
* cycles at 300 MHz PLL / 10 MHz INTOSC; keep well under that. */
#define PROBE_CNT1_SEED 0xFFFFFUL
/* A sample taken after a DCC ERROR flag or a guard-loop timeout is not noise,
* it is a misconfigured clock mux or a stalled counter. Count both so a
* capture with any nonzero total can be rejected rather than analysed. */
static uint32_t probeDccErrors;
static uint32_t probeDccTimeouts;
static uint32_t probeAdcTimeouts;
#define PROBE_SAMPLES 1024
/* printf over SCI dominates; keep the ADC set smaller. */
#define PROBE_ADC_SAMPLES 1024
@ -87,12 +94,19 @@ static uint32_t probe_dccSample(uint32_t base, DCC_Count0ClockSource src0,
DCC_enableModule(base);
/* Bounded wait, scaled to the window, so a bad mux cannot hang. */
/* Bounded wait, scaled to the window, so a bad mux cannot hang. Record
* why we stopped: only a clean done-signal yields a usable sample. */
for (guard = 0; guard < (window * 256UL) + 100000UL; guard++) {
if (DCC_getSingleShotStatus(base) || DCC_getErrorStatus(base)) {
break;
}
}
if (DCC_getErrorStatus(base)) {
probeDccErrors++;
}
else if (!DCC_getSingleShotStatus(base)) {
probeDccTimeouts++;
}
return (PROBE_CNT1_SEED - (DCC_getCounter1Value(base) & PROBE_CNT1_SEED));
}
@ -140,6 +154,9 @@ static uint16_t probe_adcSample(void)
break;
}
}
if (guard >= 1000000UL) {
probeAdcTimeouts++;
}
return ADC_readResult(ADCCRESULT_BASE, ADC_SOC_NUMBER0);
}
@ -191,7 +208,7 @@ static void probe_dumpPackedDcc(const char* tag, uint32_t base,
}
static void probe_dumpPackedAdc(uint32_t nbytes)
static void probe_dumpPackedAdc(const char* tag, uint32_t nbytes)
{
uint32_t i;
int b;
@ -199,7 +216,7 @@ static void probe_dumpPackedAdc(uint32_t nbytes)
for (i = 0; i < nbytes; i++) {
if ((i % 32U) == 0U) {
printf("\r\nE6 0 ");
printf("\r\n%s 0 ", tag);
}
acc = 0U;
for (b = 0; b < 8; b++) {
@ -218,8 +235,10 @@ void entropy_probe_run(void)
uint32_t i;
printf("\r\n=== ENTROPY PROBE ===\r\n");
printf("SYSCLK %lu Hz, samples/config %d\r\n",
(unsigned long)DEVICE_SYSCLK_FREQ, (int)PROBE_SAMPLES);
/* %lu, not %d: int is 16 bits here and PROBE_PACKED_BYTES is 32768. */
printf("SYSCLK %lu Hz, samples/config %lu, packed stream %lu octets\r\n",
(unsigned long)DEVICE_SYSCLK_FREQ, (unsigned long)PROBE_SAMPLES,
(unsigned long)PROBE_PACKED_BYTES);
probe_dccInit();
probe_adcInit();
@ -244,8 +263,11 @@ void entropy_probe_run(void)
DCC_COUNT1SRC_PLL, 256UL, PROBE_PACKED_BYTES);
probe_dumpPackedDcc("E5", DCC0_BASE, DCC_COUNT0SRC_INTOSC2,
DCC_COUNT1SRC_PLL, 256UL, PROBE_PACKED_BYTES);
probe_dumpPackedAdc(PROBE_PACKED_BYTES);
probe_dumpPackedAdc("E6", PROBE_PACKED_BYTES);
printf("\r\nDCC errors %lu, DCC timeouts %lu, ADC timeouts %lu\r\n",
(unsigned long)probeDccErrors, (unsigned long)probeDccTimeouts,
(unsigned long)probeAdcTimeouts);
printf("\r\nPROBE DONE\r\n");
}

View File

@ -60,7 +60,9 @@
#endif
#ifdef WOLF_ENTROPY
#include <wolfssl/wolfcrypt/port/ti/ti-c2000-entropy.h>
#include <wolfssl/wolfcrypt/random.h>
#endif
#ifdef WOLF_ENTROPY_PROBE
#include "entropy_probe.h"
#endif
#ifdef WOLF_25519
#include <wolfssl/wolfcrypt/curve25519.h>
@ -978,7 +980,7 @@ static void wolf_mldsa87_verify_test(void)
* signature that arrive PACKED verify once wc_UnpackOctets() expands them.
* One key struct is shared - WOLFSSL_MLDSA_VERIFY_NO_MALLOC pins the verify
* workspace inside it, far too big for the 16 KW C28x stack. */
/* Static: an ML-DSA-65 signature is 3309 cells = 6618 octets of C28x RAM. */
/* Static: an ML-DSA-65 signature is 3309 octets = 3309 cells = 6618 bytes. */
static byte mo_pub[WC_MLDSA_65_PUB_KEY_SIZE];
static byte mo_sig[WC_MLDSA_65_SIG_SIZE];
static wc_MlDsaKey mo_key;
@ -1030,12 +1032,23 @@ static void mo_pack_roundtrip(void)
src[i] = (byte)((i * 7 + 1) & 0xFF);
}
ret = wc_PackOctets(packed, (word32)sizeof(packed), src,
(word32)sizeof(src));
(word32)sizeof(src), (word32)sizeof(src));
if (ret == 0) {
ret = wc_UnpackOctets(back, (word32)sizeof(back), packed,
(word32)sizeof(src));
(word32)sizeof(packed), (word32)sizeof(src));
}
/* Round-trip alone would also pass for an identity implementation, so
* check the packed layout itself: cell 0 must carry the first
* WC_OCTETS_PER_BYTE octets, low octet first. */
ok = (ret == 0) && (XMEMCMP(src, back, sizeof(src)) == 0);
if (ok) {
word32 expect = 0;
word32 e;
for (e = 0; e < WC_OCTETS_PER_BYTE; e++) {
expect |= (word32)src[e] << (8 * e);
}
ok = ((word32)packed[0] == expect);
}
printf("wc_Pack/UnpackOctets round-trip: %s (ret=%d)\r\n",
ok ? "PASS" : "FAIL", ret);
}
@ -1047,10 +1060,16 @@ static void mo_packed_verify(void)
int ret;
ret = wc_UnpackOctets(mo_pub, (word32)sizeof(mo_pub),
(const byte*)kat_mldsa65_pub_packed, (word32)sizeof(mo_pub));
(const byte*)kat_mldsa65_pub_packed,
(word32)(sizeof(kat_mldsa65_pub_packed) /
sizeof(kat_mldsa65_pub_packed[0])),
(word32)sizeof(mo_pub));
if (ret == 0) {
ret = wc_UnpackOctets(mo_sig, (word32)sizeof(mo_sig),
(const byte*)kat_mldsa65_sig_packed, (word32)sizeof(mo_sig));
(const byte*)kat_mldsa65_sig_packed,
(word32)(sizeof(kat_mldsa65_sig_packed) /
sizeof(kat_mldsa65_sig_packed[0])),
(word32)sizeof(mo_sig));
}
/* Expanded buffers must match the plain arrays octet for octet. */
if (ret == 0) {
@ -1098,8 +1117,9 @@ static void wolf_mldsa_octet_test(void)
return;
}
printf("octet model: CHAR_BIT=%d, %lu octet(s) per byte cell; "
"ML-DSA-65 sig = %lu octets = %lu cells = %lu bytes of RAM\r\n",
printf("octet model: CHAR_BIT=%d, one octet per byte cell, %lu octet(s) "
"per cell when packed; ML-DSA-65 sig = %lu octets = %lu cells = "
"%lu bytes of RAM\r\n",
(int)CHAR_BIT, (unsigned long)WC_OCTETS_PER_BYTE,
(unsigned long)WC_MLDSA_65_SIG_SIZE,
(unsigned long)WC_MLDSA_65_SIG_SIZE,
@ -1134,6 +1154,36 @@ static void wolf_mldsa_octet_test(void)
kat_mldsa87_sig_ph512, (word32)sizeof(kat_mldsa87_sig_ph512),
WC_HASH_TYPE_SHA512, sha512, (word32)sizeof(sha512));
/* Negative case: every check above is positive, so a verify that returned
* success unconditionally - or a w1 encoder that collapsed distinct
* commitments - would pass them all. Flip one octet of the level-44
* signature and require a clean rejection. */
{
static byte bad[WC_MLDSA_44_SIG_SIZE];
int bres = 1;
int bret;
XMEMCPY(bad, kat_mldsa44_sig, sizeof(bad));
bad[sizeof(bad) / 2] ^= 0x01;
bret = wc_MlDsaKey_Init(&mo_key, NULL, INVALID_DEVID);
if (bret == 0) {
bret = wc_MlDsaKey_SetParams(&mo_key, WC_ML_DSA_44);
}
if (bret == 0) {
bret = wc_MlDsaKey_ImportPubRaw(&mo_key, kat_mldsa44_pub,
(word32)sizeof(kat_mldsa44_pub));
}
if (bret == 0) {
bret = wc_MlDsaKey_VerifyCtx(&mo_key, bad, (word32)sizeof(bad),
NULL, 0, mo_msg, (word32)sizeof(mo_msg), &bres);
}
/* A corrupt signature must be rejected, not error out. */
printf("ML-DSA-44 corrupted-signature reject: %s (ret=%d res=%d)\r\n",
((bret == 0) && (bres == 0)) ? "PASS" : "FAIL", bret, bres);
wc_MlDsaKey_Free(&mo_key);
}
mo_packed_verify();
}
#endif /* WOLF_MLDSA_OCTETS */
@ -1305,9 +1355,18 @@ static void wolf_entropy_test(void)
printf("Entropy liveness self-test (raw): %s (ret=%d)\r\n",
(ret == 0) ? "PASS" : "FAIL", ret);
/* Raw noise sanity per source: population count should sit near half. */
for (src = 0; src < 2; src++) {
/* Raw noise sanity per source: population count should sit near half.
* Source 1 is optional - a build that needs DCC0 elsewhere sets
* WOLFSSL_C2000_ENTROPY_NUM_SRC to 1. */
for (src = 0; src < WOLFSSL_C2000_ENTROPY_NUM_SRC; src++) {
ret = wc_c2000_Entropy_GetRaw(raw, (word32)sizeof(raw), src);
if (ret != 0) {
/* raw[] holds stale data on failure, so counting it would report a
* meaningless balance. Report the read error instead. */
printf("Entropy raw src%d bit balance: FAIL (read error %d)\r\n",
src, ret);
continue;
}
ones = 0;
for (i = 0; i < (word32)sizeof(raw); i++) {
for (b = 0; b < 8; b++) {
@ -1319,7 +1378,7 @@ static void wolf_entropy_test(void)
/* 2048 bits; accept 40%..60% ones, i.e. counts 820..1228. */
printf("Entropy raw src%d bit balance: %s (%lu/2048 ones)\r\n",
src,
(ret == 0 && ones > 819UL && ones < 1229UL) ? "PASS" : "FAIL",
(ones > 819UL && ones < 1229UL) ? "PASS" : "FAIL",
(unsigned long)ones);
}
@ -1378,6 +1437,17 @@ static void wolf_aes_test(void)
static byte o[16], o2[16], tag[16];
int r;
/* Must be initialised, and explicitly with INVALID_DEVID: a static Aes
* zero-fills devId to 0, which is a valid device id, so with WOLF_CRYPTO_CB
* built in (HWAES=1) every aes.c hook would attempt callback dispatch
* instead of skipping. This is the software reference for the HW-vs-SW
* cross-checks, so it must stay unambiguously software. */
r = wc_AesInit(&aes, NULL, INVALID_DEVID);
if (r != 0) {
printf("AES software test: FAIL (init ret=%d)\r\n", r);
return;
}
/* CBC */
r = wc_AesSetKey(&aes, k, 16, iv, AES_ENCRYPTION);
if (r == 0) r = wc_AesCbcEncrypt(&aes, o, pt, 16);
@ -1432,11 +1502,17 @@ static void wolf_aes_test(void)
* NIST SP800-38A vectors are asserted where we have them; multi-block,
* split-call and in-place cases are checked hardware-against-software, since
* software AES is already covered by wolfcrypt_test. */
/* Set only when the AESA device actually registered. Without it the 'hw'
* context silently falls back to software and every cross-check would compare
* software against software and report PASS. */
static int g_aesaReady = 0;
static void hw_report(const char* name, int r, const byte* a, const byte* b,
word32 len)
{
printf("HW %s: %s\r\n", name,
(r == 0 && XMEMCMP(a, b, len) == 0) ? "PASS" : "FAIL");
int cmp = XMEMCMP(a, b, len);
printf("HW %s: %s (ret=%d cmp=%d)\r\n", name,
(r == 0 && cmp == 0) ? "PASS" : "FAIL", r, cmp);
}
static void wolf_aes_hw_test(void)
@ -1494,9 +1570,20 @@ static void wolf_aes_hw_test(void)
static byte oh[64], os[64], dh[64];
int rh, rs;
if (wc_AesInit(&hw, NULL, WOLFSSL_C2000_DEVID) != 0 ||
wc_AesInit(&sw, NULL, INVALID_DEVID) != 0) {
printf("HW AES init: FAIL\r\n");
if (!g_aesaReady) {
printf("HW AES cross-checks: SKIP (AESA not registered)\r\n");
return;
}
rh = wc_AesInit(&hw, NULL, WOLFSSL_C2000_DEVID);
if (rh != 0) {
printf("HW AES init (hw ctx): FAIL (ret=%d)\r\n", rh);
return;
}
rs = wc_AesInit(&sw, NULL, INVALID_DEVID);
if (rs != 0) {
printf("HW AES init (sw ctx): FAIL (ret=%d)\r\n", rs);
wc_AesFree(&hw);
return;
}
@ -1506,7 +1593,7 @@ static void wolf_aes_hw_test(void)
if (rh == 0) rh = wc_AesEcbEncrypt(&hw, oh, pt, 64);
if (rs == 0) rs = wc_AesEcbEncrypt(&sw, os, pt, 64);
hw_report("AES-128-ECB encrypt vs NIST", rh, oh, ecb_ct1, 16);
hw_report("AES-128-ECB encrypt vs SW", (rh | rs), oh, os, 64);
hw_report("AES-128-ECB encrypt vs SW", ((rh != 0) ? rh : rs), oh, os, 64);
rh = wc_AesSetKey(&hw, k128, 16, NULL, AES_DECRYPTION);
if (rh == 0) rh = wc_AesEcbDecrypt(&hw, dh, oh, 64);
@ -1518,7 +1605,7 @@ static void wolf_aes_hw_test(void)
if (rh == 0) rh = wc_AesCbcEncrypt(&hw, oh, pt, 64);
if (rs == 0) rs = wc_AesCbcEncrypt(&sw, os, pt, 64);
hw_report("AES-128-CBC encrypt vs NIST", rh, oh, cbc_ct1, 16);
hw_report("AES-128-CBC encrypt vs SW", (rh | rs), oh, os, 64);
hw_report("AES-128-CBC encrypt vs SW", ((rh != 0) ? rh : rs), oh, os, 64);
rh = wc_AesSetKey(&hw, k128, 16, iv, AES_DECRYPTION);
if (rh == 0) rh = wc_AesCbcDecrypt(&hw, dh, oh, 64);
@ -1528,7 +1615,7 @@ static void wolf_aes_hw_test(void)
rh = wc_AesSetKey(&hw, k128, 16, iv, AES_ENCRYPTION);
if (rh == 0) rh = wc_AesCbcEncrypt(&hw, oh, pt, 16);
if (rh == 0) rh = wc_AesCbcEncrypt(&hw, oh + 16, pt + 16, 48);
hw_report("AES-128-CBC split-call chain", (rh | rs), oh, os, 64);
hw_report("AES-128-CBC split-call chain", rh, oh, os, 64);
/* ---- CBC in-place decrypt: proves the last-block save ---- */
XMEMCPY(dh, os, 64);
@ -1543,26 +1630,53 @@ static void wolf_aes_hw_test(void)
if (rs == 0) rs = wc_AesCtrEncrypt(&sw, os, pt, 64);
hw_report("AES-128-CTR vs NIST (64B)", rh, oh, ctr_ct, 64);
hw_report("AES-128-CTR SW vs NIST (64B)", rs, os, ctr_ct, 64);
hw_report("AES-128-CTR vs SW", (rh | rs), oh, os, 64);
hw_report("AES-128-CTR vs SW", ((rh != 0) ? rh : rs), oh, os, 64);
/* ---- CTR split at a non-block boundary: proves aes->left/aes->tmp ---- */
rh = wc_AesSetKey(&hw, k128, 16, ctr_iv, AES_ENCRYPTION);
if (rh == 0) rh = wc_AesCtrEncrypt(&hw, oh, pt, 10);
if (rh == 0) rh = wc_AesCtrEncrypt(&hw, oh + 10, pt + 10, 54);
hw_report("AES-128-CTR partial split", (rh | rs), oh, os, 64);
hw_report("AES-128-CTR partial split", rh, oh, os, 64);
/* ---- 192- and 256-bit keys: the 6- and 8-word AES_setKey1 paths ---- */
rh = wc_AesSetKey(&hw, k192, 24, iv, AES_ENCRYPTION);
rs = wc_AesSetKey(&sw, k192, 24, iv, AES_ENCRYPTION);
if (rh == 0) rh = wc_AesCbcEncrypt(&hw, oh, pt, 64);
if (rs == 0) rs = wc_AesCbcEncrypt(&sw, os, pt, 64);
hw_report("AES-192-CBC encrypt vs SW", (rh | rs), oh, os, 64);
hw_report("AES-192-CBC encrypt vs SW", ((rh != 0) ? rh : rs), oh, os, 64);
rh = wc_AesSetKey(&hw, k256, 32, iv, AES_ENCRYPTION);
rs = wc_AesSetKey(&sw, k256, 32, iv, AES_ENCRYPTION);
if (rh == 0) rh = wc_AesCbcEncrypt(&hw, oh, pt, 64);
if (rs == 0) rs = wc_AesCbcEncrypt(&sw, os, pt, 64);
hw_report("AES-256-CBC encrypt vs SW", (rh | rs), oh, os, 64);
hw_report("AES-256-CBC encrypt vs SW", ((rh != 0) ? rh : rs), oh, os, 64);
/* ECB and CTR at 192/256 too: the accelerator's key schedule differs per
* key size, and marshalling the longer schedule into its 32-bit registers
* is exactly the octet handling this port is validating. */
rh = wc_AesSetKey(&hw, k192, 24, NULL, AES_ENCRYPTION);
rs = wc_AesSetKey(&sw, k192, 24, NULL, AES_ENCRYPTION);
if (rh == 0) rh = wc_AesEcbEncrypt(&hw, oh, pt, 64);
if (rs == 0) rs = wc_AesEcbEncrypt(&sw, os, pt, 64);
hw_report("AES-192-ECB encrypt vs SW", ((rh != 0) ? rh : rs), oh, os, 64);
rh = wc_AesSetKey(&hw, k256, 32, NULL, AES_ENCRYPTION);
rs = wc_AesSetKey(&sw, k256, 32, NULL, AES_ENCRYPTION);
if (rh == 0) rh = wc_AesEcbEncrypt(&hw, oh, pt, 64);
if (rs == 0) rs = wc_AesEcbEncrypt(&sw, os, pt, 64);
hw_report("AES-256-ECB encrypt vs SW", ((rh != 0) ? rh : rs), oh, os, 64);
rh = wc_AesSetKey(&hw, k192, 24, ctr_iv, AES_ENCRYPTION);
rs = wc_AesSetKey(&sw, k192, 24, ctr_iv, AES_ENCRYPTION);
if (rh == 0) rh = wc_AesCtrEncrypt(&hw, oh, pt, 64);
if (rs == 0) rs = wc_AesCtrEncrypt(&sw, os, pt, 64);
hw_report("AES-192-CTR encrypt vs SW", ((rh != 0) ? rh : rs), oh, os, 64);
rh = wc_AesSetKey(&hw, k256, 32, ctr_iv, AES_ENCRYPTION);
rs = wc_AesSetKey(&sw, k256, 32, ctr_iv, AES_ENCRYPTION);
if (rh == 0) rh = wc_AesCtrEncrypt(&hw, oh, pt, 64);
if (rs == 0) rs = wc_AesCtrEncrypt(&sw, os, pt, 64);
hw_report("AES-256-CTR encrypt vs SW", ((rh != 0) ? rh : rs), oh, os, 64);
wc_AesFree(&hw);
wc_AesFree(&sw);
@ -1964,12 +2078,13 @@ static void wolf_aes_modes_test(void)
#ifdef WOLFSSL_AES_OFB
{
static Aes oaes;
r = wc_AesSetKey(&oaes, mk, 16, miv, AES_ENCRYPTION);
r = wc_AesInit(&oaes, NULL, INVALID_DEVID);
if (r == 0) r = wc_AesSetKey(&oaes, mk, 16, miv, AES_ENCRYPTION);
if (r == 0) r = wc_AesOfbEncrypt(&oaes, mct, mpt, 32);
if (r == 0) r = wc_AesSetKey(&oaes, mk, 16, miv, AES_ENCRYPTION);
if (r == 0) r = wc_AesOfbDecrypt(&oaes, mdec, mct, 32);
printf("AES-128-OFB round-trip: %s\r\n",
(r == 0 && XMEMCMP(mdec, mpt, 32) == 0) ? "PASS":"FAIL");
printf("AES-128-OFB round-trip: %s (ret=%d)\r\n",
(r == 0 && XMEMCMP(mdec, mpt, 32) == 0) ? "PASS":"FAIL", r);
wc_AesFree(&oaes);
}
#endif
@ -2147,12 +2262,12 @@ int main(void)
printf("=== wolfSSL wolfCrypt on TI C2000 LAUNCHXL-F28P55X ===\r\n");
#ifdef WOLF_ENTROPY_PROBE
/* Measurement-only image: dump raw entropy samples and stop. */
{
extern void entropy_probe_run(void);
entropy_probe_run();
}
while (1) {
/* Measurement-only image: dump raw entropy samples and stop. Nothing
* after this runs, which is why the Makefile rejects combining
* ENTROPY_PROBE=1 with the other image toggles. */
entropy_probe_run();
for (;;) {
/* spin */
}
#endif
@ -2173,6 +2288,7 @@ int main(void)
}
else {
printf("C2000 AESA init: PASS\r\n");
g_aesaReady = 1;
}
#endif

View File

@ -30,7 +30,6 @@ an upper bound, and low measured correlation is what makes it a reasonable one.
Usage:
python3 tools/entropy_analyze.py capture.log
python3 tools/entropy_analyze.py --selftest
tail -f /tmp/uart-monitor/latest/ttyACMx.log | python3 tools/entropy_analyze.py -
"""
import math
@ -56,15 +55,22 @@ RAW = OrderedDict((
))
BANNER = "=== ENTROPY PROBE ==="
# The probe reports how many samples were taken after a DCC ERROR flag or a
# guard-loop timeout. Any nonzero count means the capture is not pure noise.
HEALTH_RE = re.compile(r"DCC errors (\d+), DCC timeouts (\d+), "
r"ADC timeouts (\d+)")
DONE = "PROBE DONE"
MAX_LAG = 64
# Below this the MCV bound is too wide to be worth reporting.
MIN_PACKED_OCTETS = 256
Z_99 = 2.5758293035489004 # two-sided 99% normal quantile
def first_pass(text):
"""The board loops main(), so the probe output repeats. Return just the
first complete pass, so a long capture does not silently concatenate
several runs into one sample set."""
"""Return just the first complete probe pass. The probe image runs once
and then spins, so a normal capture holds one pass - but a capture that
spans a reset or a re-flash would otherwise concatenate several runs into
one sample set."""
start = text.find(BANNER)
if start < 0:
return text
@ -77,21 +83,41 @@ def first_pass(text):
def parse(text):
"""tag -> list of ints, in emission order."""
"""Returns (out, per_window).
out tag -> list of ints, in emission order (all windows merged).
per_window tag -> {window: [ints]}.
The raw DCC tags are emitted once per sweep window (256/1024/4096), and a
count scales with the window, so merging them would mix populations of
different magnitude. The packed streams the min-entropy estimate uses are
emitted once, at a single window, so out[] is correct for those."""
out = {}
per_window = {}
# A console line is "<tag> <window> <hex> <hex> ...". Match per line, and
# never across a newline: the tags and window counts are themselves valid
# hex, so a multi-line match would swallow the next line's header as data.
# Tolerate any timestamp or prefix a log wrapper put ahead of the tag.
line_re = re.compile(r"\b(E[0-9])[^\S\n]+(\d+)[^\S\n]+"
r"((?:[0-9a-fA-F]+[^\S\n]*)+)$")
skipped = 0
for line in text.splitlines():
m = line_re.search(line.rstrip())
line = line.rstrip()
m = line_re.search(line)
if m is None:
# A line that starts with a probe tag but does not parse means a
# corrupted capture, not unrelated console output - say so.
if re.match(r"\s*E[0-9]\b", line):
skipped += 1
continue
out.setdefault(m.group(1), []).extend(
int(t, 16) for t in m.group(3).split())
return out
vals = [int(t, 16) for t in m.group(3).split()]
out.setdefault(m.group(1), []).extend(vals)
per_window.setdefault(m.group(1), {}).setdefault(
int(m.group(2)), []).extend(vals)
if skipped:
sys.stderr.write("warning: %d probe line(s) did not parse - capture may "
"be corrupted or truncated\n" % skipped)
return out, per_window
def unpack_bits(octets):
@ -266,7 +292,16 @@ def main():
with open(path, errors="replace") as f:
text = f.read()
data = parse(first_pass(text))
health = HEALTH_RE.search(text)
if health is None:
sys.stderr.write("warning: no probe health line - old probe image, or "
"the capture is truncated\n")
elif any(int(g) for g in health.groups()):
sys.exit("probe reported %s DCC errors, %s DCC timeouts, %s ADC "
"timeouts - these samples are not noise, reject the capture"
% health.groups())
data, per_window = parse(first_pass(text))
if not data:
sys.exit("no probe tags found - is this an ENTROPY_PROBE=1 capture?")
@ -276,6 +311,10 @@ def main():
if not octets:
sys.stderr.write("warning: no %s samples (%s)\n" % (tag, label))
continue
if len(octets) < MIN_PACKED_OCTETS:
sys.stderr.write("warning: %s has only %d octets (< %d) - the "
"min-entropy estimate will be unreliable\n"
% (tag, len(octets), MIN_PACKED_OCTETS))
bad = [v for v in octets if v > 0xFF]
if bad:
sys.exit("%s: %d values exceed one octet - capture is corrupt"
@ -283,11 +322,15 @@ def main():
rows.append(report(tag, label, octets))
for tag, label in RAW.items():
vals = data.get(tag)
if vals:
a = np.asarray(vals, dtype=np.int64)
print("%s %s: %d samples, min %d max %d mean %.1f, "
"%d distinct" % (tag, label, len(a), a.min(), a.max(),
wins = per_window.get(tag)
if not wins:
continue
# Per window: a raw count scales with the window, so pooling them
# would report a spread that is an artifact of the sweep.
for win in sorted(wins):
a = np.asarray(wins[win], dtype=np.int64)
print("%s %s [window %d]: %d samples, min %d max %d mean %.1f, "
"%d distinct" % (tag, label, win, len(a), a.min(), a.max(),
a.mean(), len(np.unique(a))))
print()

View File

@ -32,7 +32,6 @@
#include <wolfssl/wolfcrypt/sha512.h>
#include <wolfssl/wolfcrypt/hash.h>
#include <stdio.h>
#include <string.h>
#define MSG_SZ 512
#define OUT_NAME "mldsa_octet_kat.h"
@ -167,17 +166,25 @@ int main(void)
for (i = 0; i < MSG_SZ; i++)
msg[i] = (byte)(i & 0xFF);
ret = wolfCrypt_Init();
if (ret != 0) {
fprintf(stderr, "wolfCrypt_Init failed: %d\n", ret);
return 1;
}
ret = wc_Sha256Hash(msg, MSG_SZ, sha256);
if (ret == 0)
ret = wc_Sha512Hash(msg, MSG_SZ, sha512);
if (ret != 0) {
fprintf(stderr, "hash failed: %d\n", ret);
wolfCrypt_Cleanup();
return 1;
}
out = fopen(OUT_NAME, "w");
if (out == NULL) {
fprintf(stderr, "cannot open output\n");
wolfCrypt_Cleanup();
return 1;
}
@ -205,7 +212,9 @@ int main(void)
if (ret != 0) {
/* Do not leave a truncated header behind for the build to pick up. */
remove(OUT_NAME);
wolfCrypt_Cleanup();
return 1;
}
wolfCrypt_Cleanup();
return 0;
}