wolfssl-examples/embedded/ti-c2000-f28p55x/Source/entropy_probe.c

253 lines
8.1 KiB
C

/* entropy_probe.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
*/
/* Raw entropy-source characterization for the TMS320F28P550SJ.
*
* `make ENTROPY_PROBE=1`. A MEASUREMENT tool, not part of the RNG: it dumps
* unconditioned samples over SCI so a host can estimate min-entropy before
* anything is wired into wc_GenerateSeed().
*
* Candidates: DCC oscillator jitter (a DCC counts PLL edges inside a window of
* INTOSC cycles, leaving the drift between two independent oscillators in the
* low bits) and ADC LSB noise (floating high-Z input, short acquisition so the
* SAR never settles).
*
* Tagged hex output for the host analyzer:
* E0/E1 <N> DCC1 INTOSC1 / DCC0 INTOSC2 window, PLL counted, N cycles
* E3 0 ADCC raw 12-bit results
* E4/E5/E6 packed LSB streams (8 samples per emitted octet)
* PROBE DONE
*/
#include <stdio.h>
#include <stdint.h>
#include "driverlib.h"
#include "device.h"
#ifdef WOLF_ENTROPY_PROBE
/* 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
#define PROBE_SAMPLES 1024
/* printf over SCI dominates; keep the ADC set smaller. */
#define PROBE_ADC_SAMPLES 1024
#define PROBE_PER_LINE 16
/* Packed-LSB stream size. 32 KiB = 262144 bits per source, enough for the
* MCV confidence bound to stop being the limiting factor. */
#define PROBE_PACKED_BYTES 32768UL
/* Window sweep (slow-clock cycles). Entropy per sample grows with the window
* while the rate falls, so the useful entropy rate peaks in the middle. */
static const uint32_t probeWindows[] = { 256UL, 1024UL, 4096UL };
#define PROBE_NUM_WINDOWS (sizeof(probeWindows) / sizeof(probeWindows[0]))
/* One DCC measurement, at register level: DCC_measureClockFrequency() uses
* float32_t, which does not belong here. */
static uint32_t probe_dccSample(uint32_t base, DCC_Count0ClockSource src0,
DCC_Count1ClockSource src1, uint32_t window)
{
uint32_t guard;
DCC_clearErrorFlag(base);
DCC_clearDoneFlag(base);
DCC_disableModule(base);
DCC_disableErrorSignal(base);
DCC_disableDoneSignal(base);
DCC_setCounter0ClkSource(base, src0);
DCC_setCounter1ClkSource(base, src1);
DCC_setCounterSeeds(base, window, DCC_VALIDSEED_MIN, PROBE_CNT1_SEED);
DCC_enableSingleShotMode(base, DCC_MODE_COUNTER_ZERO);
/* DONE only latches with the done/error signals enabled - driverlib's own
* DCC_measureClockFrequency() does this and it is easy to miss. */
DCC_enableErrorSignal(base);
DCC_enableDoneSignal(base);
DCC_enableModule(base);
/* Bounded wait, scaled to the window, so a bad mux cannot hang. */
for (guard = 0; guard < (window * 256UL) + 100000UL; guard++) {
if (DCC_getSingleShotStatus(base) || DCC_getErrorStatus(base)) {
break;
}
}
return (PROBE_CNT1_SEED - (DCC_getCounter1Value(base) & PROBE_CNT1_SEED));
}
static void probe_dccInit(void)
{
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_DCC0);
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_DCC1);
SysCtl_delay(100);
}
static void probe_adcInit(void)
{
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_ADCC);
SysCtl_delay(100);
ASysCtl_setAnalogReferenceInternal(ASYSCTL_ANAREF_INTREF_ADCC);
ADC_setPrescaler(ADCC_BASE, ADC_CLK_DIV_4_0);
ADC_setInterruptPulseMode(ADCC_BASE, ADC_PULSE_END_OF_CONV);
ADC_enableConverter(ADCC_BASE);
DEVICE_DELAY_US(1000);
/* Short acquisition on a floating high-Z input: the SAR deliberately does
* not settle, which is where the noise comes from. */
ADC_setupSOC(ADCC_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_SW_ONLY,
ADC_CH_ADCIN0, 8U);
ADC_setInterruptSource(ADCC_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER0);
ADC_enableInterrupt(ADCC_BASE, ADC_INT_NUMBER1);
ADC_clearInterruptStatus(ADCC_BASE, ADC_INT_NUMBER1);
}
static uint16_t probe_adcSample(void)
{
uint32_t guard;
ADC_clearInterruptStatus(ADCC_BASE, ADC_INT_NUMBER1);
ADC_forceSOC(ADCC_BASE, ADC_SOC_NUMBER0);
for (guard = 0; guard < 1000000UL; guard++) {
if (ADC_getInterruptStatus(ADCC_BASE, ADC_INT_NUMBER1)) {
break;
}
}
return ADC_readResult(ADCCRESULT_BASE, ADC_SOC_NUMBER0);
}
static void probe_dumpDcc(const char* tag, uint32_t base,
DCC_Count0ClockSource src0,
DCC_Count1ClockSource src1, uint32_t window)
{
uint32_t i;
for (i = 0; i < PROBE_SAMPLES; i++) {
if ((i % PROBE_PER_LINE) == 0) {
printf("\r\n%s %lu ", tag, (unsigned long)window);
}
printf("%05lx ",
(unsigned long)probe_dccSample(base, src0, src1, window));
}
printf("\r\n");
}
/* Packed LSB stream. A useful min-entropy estimate needs far more samples
* than a 115200 UART can carry one hex count at a time, so the bit extraction
* happens on-target: 8 samples per emitted octet. This is also the stream a
* real entropy source consumes, so it is the right thing to assess. */
static void probe_dumpPackedDcc(const char* tag, uint32_t base,
DCC_Count0ClockSource src0,
DCC_Count1ClockSource src1, uint32_t window,
uint32_t nbytes)
{
uint32_t i;
int b;
uint16_t acc;
for (i = 0; i < nbytes; i++) {
if ((i % 32U) == 0U) {
printf("\r\n%s %lu ", tag, (unsigned long)window);
}
acc = 0U;
for (b = 0; b < 8; b++) {
acc = (uint16_t)(acc |
(uint16_t)((probe_dccSample(base, src0, src1, window) & 1U)
<< b));
}
printf("%02x ", (unsigned int)(acc & 0xFFU));
}
printf("\r\n");
}
static void probe_dumpPackedAdc(uint32_t nbytes)
{
uint32_t i;
int b;
uint16_t acc;
for (i = 0; i < nbytes; i++) {
if ((i % 32U) == 0U) {
printf("\r\nE6 0 ");
}
acc = 0U;
for (b = 0; b < 8; b++) {
acc = (uint16_t)(acc |
(uint16_t)((probe_adcSample() & 1U) << b));
}
printf("%02x ", (unsigned int)(acc & 0xFFU));
}
printf("\r\n");
}
void entropy_probe_run(void)
{
uint32_t w;
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);
probe_dccInit();
probe_adcInit();
for (w = 0; w < PROBE_NUM_WINDOWS; w++) {
probe_dumpDcc("E0", DCC1_BASE, DCC_COUNT0SRC_INTOSC1,
DCC_COUNT1SRC_PLL, probeWindows[w]);
probe_dumpDcc("E1", DCC0_BASE, DCC_COUNT0SRC_INTOSC2,
DCC_COUNT1SRC_PLL, probeWindows[w]);
}
for (i = 0; i < PROBE_ADC_SAMPLES; i++) {
if ((i % PROBE_PER_LINE) == 0) {
printf("\r\nE3 0 ");
}
printf("%05lx ", (unsigned long)probe_adcSample());
}
printf("\r\n");
/* Packed LSB streams for the real min-entropy assessment. */
probe_dumpPackedDcc("E4", DCC1_BASE, DCC_COUNT0SRC_INTOSC1,
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);
printf("\r\nPROBE DONE\r\n");
}
#endif /* WOLF_ENTROPY_PROBE */