EP_ROUND_RAW_DATA_INPUTS
[gnuk/neug.git] / src / random.c
index 7fabf85..a1786f2 100644 (file)
@@ -5,14 +5,14 @@
  * Author: NIIBE Yutaka <gniibe@fsij.org>
  *
  * This file is a part of NeuG, a Random Number Generator
- * implementation (for STM32F103).
+ * implementation based on quantization error of ADC (for STM32F103).
  *
  * NeuG 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.
  *
- * Gnuk is distributed in the hope that it will be useful, but WITHOUT
+ * NeuG 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.
 
 #include "ch.h"
 #include "hal.h"
+#include "sys.h"
+#include "neug.h"
+#include "adc.h"
+#include "sha256.h"
 
-static Thread *rng_thread;
+Thread *rng_thread;
 #define ADC_DATA_AVAILABLE ((eventmask_t)1)
 
-/* Total number of channels to be sampled by a single ADC operation.*/
-#define ADC_GRP1_NUM_CHANNELS   2
-/* Depth of the conversion buffer, channels are sampled one time each.*/
-#define ADC_GRP1_BUF_DEPTH      4
-/*
- * ADC samples buffer.
- */
-static adcsample_t samp[ADC_GRP1_NUM_CHANNELS * ADC_GRP1_BUF_DEPTH];
-static void adccb (ADCDriver *adcp, adcsample_t *buffer, size_t n);
-static void adccb_err (ADCDriver *adcp, adcerror_t err);
-
-/*
- * ADC conversion group.
- * Mode:        Linear buffer, 4 samples of 2 channels, SW triggered.
- * Channels:    Vref   (1.5 cycles sample time, violating the spec.)
- *              Sensor (1.5 cycles sample time, violating the spec.)
- */
-static const ADCConversionGroup adcgrpcfg = {
-  FALSE,
-  ADC_GRP1_NUM_CHANNELS,
-  adccb,
-  adccb_err,
-  0,
-  ADC_CR2_TSVREFE,
-  ADC_SMPR1_SMP_SENSOR(ADC_SAMPLE_1P5) | ADC_SMPR1_SMP_VREF(ADC_SAMPLE_1P5),
-  0,
-  ADC_SQR1_NUM_CH(ADC_GRP1_NUM_CHANNELS),
-  0,
-  ADC_SQR3_SQ2_N(ADC_CHANNEL_SENSOR) | ADC_SQR3_SQ1_N(ADC_CHANNEL_VREFINT)
-};
-
-/*
- * ADC end conversion callback.
- */
-static void adccb (ADCDriver *adcp, adcsample_t *buffer, size_t n)
-{
-  (void) buffer; (void) n;
-
-  chSysLockFromIsr();
-  if (adcp->state == ADC_COMPLETE)
-    chEvtSignalFlagsI (rng_thread, ADC_DATA_AVAILABLE);
-  chSysUnlockFromIsr();
-}
-
-static void adccb_err (ADCDriver *adcp, adcerror_t err)
-{
-  (void)adcp;  (void)err;
-}
-
-\f
-#include "sha256.h"
+static uint32_t adc_buf[SHA256_BLOCK_SIZE/sizeof (uint32_t)];
 
 static sha256_context sha256_ctx_data;
 static uint32_t sha256_output[SHA256_DIGEST_SIZE/sizeof (uint32_t)];
 
 /*
- * We did an experiment of measuring entropy of ADC output with MUST.
- * The entropy of a byte by raw sampling of LSBs has more than 6.0 bit/byte.
- *
- * More tests will be required, but for now we assume min-entropy >= 5.0.
- * 
- * To be a full entropy source, the requirement is to have N samples for
- * output of 256-bit, where:
+ * To be a full entropy source, the requirement is to have N samples
+ * for output of 256-bit, where:
  *
  *      N = (256 * 2) / <min-entropy of a sample>
  *
- * For min-entropy = 5.0, N should be more than 103.
+ * For example, N should be more than 103 for min-entropy = 5.0.
  *
- * On the other hand, in the section 6.2 "Full Entropy Source Requirements",
- * it says:
+ * On the other hand, in the section 6.2 "Full Entropy Source
+ * Requirements", it says:
  *
  *     At least twice the block size of the underlying cryptographic
  *     primitive shall be provided as input to the conditioning
  *     function to produce full entropy output.
  *
- * For us, cryptographic primitive is SHA-256 and its blocksize is 512-bit
- * (64-byte), N >= 128.
+ * For us, cryptographic primitive is SHA-256 and its blocksize is
+ * 512-bit (64-byte), thus, N >= 128.
  *
- * We chose N=131, since we love prime number, and we have "additional bits"
- * of 32-byte for last block (feedback from previous output of SHA-256).
+ * We chose N=140.  Note that we have "additional bits" of 16-byte for
+ * last block (feedback from previous output of SHA-256) to feed
+ * hash_df function of SHA-256, together with sample data of 140-byte.
  *
- * This corresponds to min-entropy >= 3.91.
+ * N=140 corresponds to min-entropy >= 3.68.
  *
  */
-#define NUM_NOISE_INPUTS 131
+#define NUM_NOISE_INPUTS 140
 
-static const uint8_t hash_df_initial_string[5] = {
-  1,          /* counter = 1 */
-  0, 0, 1, 0  /* no_of_bits_returned (big endian) */
-};
+#define EP_ROUND_0 0 /* initial-five-byte and 3-byte, then 56-byte-input */
+#define EP_ROUND_1 1 /* 64-byte-input */
+#define EP_ROUND_2 2 /* 17-byte-input */
+#define EP_ROUND_RAW      3 /* 32-byte-input */
+#define EP_ROUND_RAW_DATA 4 /* 32-byte-input */
 
-static void ep_init (void)
+#define EP_ROUND_0_INPUTS 56
+#define EP_ROUND_1_INPUTS 64
+#define EP_ROUND_2_INPUTS 17
+#define EP_ROUND_RAW_INPUTS 32
+#define EP_ROUND_RAW_DATA_INPUTS 32
+
+static uint8_t ep_round;
+
+/*
+ * Hash_df initial string:
+ *
+ *  1,          : counter = 1
+ *  0, 0, 1, 0  : no_of_bits_returned (in big endian)
+ */
+static void ep_fill_initial_string (void)
 {
-  sha256_start (&sha256_ctx_data);
-  sha256_update (&sha256_ctx_data, hash_df_initial_string, 5);
+  adc_buf[0] = 0x01000001; /* Regardless of endian */
+  adc_buf[1] = (CRC->DR & 0xffffff00);
 }
 
-static void ep_add (uint8_t entropy_bits)
+static void ep_init (int mode)
 {
-  sha256_update (&sha256_ctx_data, &entropy_bits, 1);
+  chEvtClearFlags (ADC_DATA_AVAILABLE);
+  if (mode == NEUG_MODE_RAW)
+    {
+      ep_round = EP_ROUND_RAW;
+      adc_start_conversion (ADC_CRC32_MODE, adc_buf, EP_ROUND_RAW_INPUTS);
+    }
+  else if (mode == NEUG_MODE_RAW_DATA)
+    {
+      ep_round = EP_ROUND_RAW_DATA;
+      adc_start_conversion (ADC_SAMPLE_MODE, adc_buf, EP_ROUND_RAW_DATA_INPUTS);
+    }
+  else
+    {
+      ep_round = EP_ROUND_0;
+      ep_fill_initial_string ();
+      adc_start_conversion (ADC_CRC32_MODE,
+                           &adc_buf[2], EP_ROUND_0_INPUTS);
+    }
 }
 
-#define PROBABILITY_50_BY_TICK() ((SysTick->VAL & 0x02) != 0)
+static void noise_source_continuous_test (uint8_t noise);
 
-static const uint32_t *ep_output (void)
+static void ep_fill_wbuf (int i, int flip, int test)
 {
-#if ((SHA256_BLOCK_SIZE - 9) - ((5 + NUM_NOISE_INPUTS) % SHA256_BLOCK_SIZE)) \
-    > SHA256_DIGEST_SIZE
-  int n = SHA256_DIGEST_SIZE;
-#else
-  int n = (SHA256_BLOCK_SIZE - 9)
-    - ((5 + NUM_NOISE_INPUTS) % SHA256_BLOCK_SIZE);
-#endif
+  uint32_t v = adc_buf[i];
+
+  if (test)
+    {
+      uint8_t b0, b1, b2, b3;
+
+      b3 = v >> 24;
+      b2 = v >> 16;
+      b1 = v >> 8;
+      b0 = v;
+
+      noise_source_continuous_test (b0);
+      noise_source_continuous_test (b1);
+      noise_source_continuous_test (b2);
+      noise_source_continuous_test (b3);
+    }
 
-  if (PROBABILITY_50_BY_TICK ())
-    n = n - 3;
+  if (flip)
+    v = __builtin_bswap32 (v);
 
-  sha256_update (&sha256_ctx_data, (uint8_t *)sha256_output, n);
-  sha256_finish (&sha256_ctx_data, (uint8_t *)sha256_output);
-  ep_init ();
-  return sha256_output;
+  sha256_ctx_data.wbuf[i] = v;
+}
+
+/* Here assumes little endian architecture.  */
+static int ep_process (int mode)
+{
+  int i, n;
+
+  if (ep_round == EP_ROUND_RAW)
+    {
+      for (i = 0; i < EP_ROUND_RAW_INPUTS / 4; i++)
+       ep_fill_wbuf (i, 0, 0);
+
+      ep_init (mode);
+      return EP_ROUND_RAW_INPUTS / 4;
+    }
+  else if (ep_round == EP_ROUND_RAW_DATA)
+    {
+      for (i = 0; i < EP_ROUND_RAW_DATA_INPUTS / 4; i++)
+       ep_fill_wbuf (i, 0, 0);
+
+      ep_init (mode);
+      return EP_ROUND_RAW_DATA_INPUTS / 4;
+    }
+
+  if (ep_round == EP_ROUND_0)
+    {
+      for (i = 0; i < 64 / 4; i++)
+       ep_fill_wbuf (i, 1, 1);
+
+      adc_start_conversion (ADC_CRC32_MODE, adc_buf, EP_ROUND_1_INPUTS);
+      sha256_start (&sha256_ctx_data);
+      sha256_process (&sha256_ctx_data);
+      ep_round++;
+      return 0;
+    }
+  else if (ep_round == EP_ROUND_1)
+    {
+      for (i = 0; i < 64 / 4; i++)
+       ep_fill_wbuf (i, 1, 1);
+
+      adc_start_conversion (ADC_CRC32_MODE, adc_buf, EP_ROUND_2_INPUTS);
+      sha256_process (&sha256_ctx_data);
+      ep_round++;
+      return 0;
+    }
+  else
+    {
+      for (i = 0; i < (EP_ROUND_2_INPUTS + 3) / 4; i++)
+       ep_fill_wbuf (i, 0, 1);
+
+      n = SHA256_DIGEST_SIZE / 2;
+      ep_init (NEUG_MODE_CONDITIONED); /* The three-byte is used here.  */
+      memcpy (((uint8_t *)sha256_ctx_data.wbuf)
+             + ((NUM_NOISE_INPUTS+5)%SHA256_BLOCK_SIZE),
+             sha256_output, n); /* Don't use the last three-byte.  */
+      sha256_ctx_data.total[0] = 5 + NUM_NOISE_INPUTS + n;
+      sha256_finish (&sha256_ctx_data, (uint8_t *)sha256_output);
+      return SHA256_DIGEST_SIZE / sizeof (uint32_t);
+    }
 }
 
+
+static const uint32_t *ep_output (int mode)
+{
+  if (mode)
+    return sha256_ctx_data.wbuf;
+  else
+    return sha256_output;
+}
+\f
 #define REPETITION_COUNT           1
 #define ADAPTIVE_PROPORTION_64     2
 #define ADAPTIVE_PROPORTION_4096   4
 
-uint32_t neug_err_state;
+uint8_t neug_err_state;
+uint16_t neug_err_cnt;
+uint16_t neug_err_cnt_rc;
+uint16_t neug_err_cnt_p64;
+uint16_t neug_err_cnt_p4k;
 
 static void noise_source_error_reset (void)
 {
@@ -171,11 +228,36 @@ static void noise_source_error_reset (void)
 static void noise_source_error (uint32_t err)
 {
   neug_err_state |= err;
+  neug_err_cnt++;
+
+  if ((err & REPETITION_COUNT))
+    neug_err_cnt_rc++;
+  if ((err & ADAPTIVE_PROPORTION_64))
+    neug_err_cnt_p64++;
+  if ((err & ADAPTIVE_PROPORTION_4096))
+    neug_err_cnt_p4k++;
+
+#include "board.h"
+#if defined(BOARD_FST_01)
+  palSetPad (IOPORT1, 2);
+#endif
+#if defined(BOARD_STBEE_MINI)
+  palClearPad (IOPORT1, GPIOA_LED2);
+#endif
 }
 
+/*
+ * For health tests, we assumes that the device noise source has
+ * min-entropy >= 4.2.  Observing raw data stream (before CRC-32) has
+ * more than 4.2 bit/byte entropy.  When the data stream after CRC-32
+ * filter will be less than 4.2 bit/byte entropy, that must be
+ * something wrong.  Note that even we observe < 4.2, we still have
+ * some margin, since we use NUM_NOISE_INPUTS=140.
+ *
+ */
 
-/* Cuttoff = 9, when min-entropy = 4.0, W= 2^-30 */
-/* ceiling of (1+30/4.0) */
+/* Cuttoff = 9, when min-entropy = 4.2, W= 2^-30 */
+/* ceiling of (1+30/4.2) */
 #define REPITITION_COUNT_TEST_CUTOFF 9
 
 static uint8_t rct_a;
@@ -196,9 +278,9 @@ static void repetition_count_test (uint8_t sample)
     }
 }
 
-/* Cuttoff = 20, when min-entropy = 4.0, W= 2^-30 */
-/* With R, qbinom(1-2^-30,64,2^-4.0) */
-#define ADAPTIVE_PROPORTION_64_TEST_CUTOFF 20
+/* Cuttoff = 18, when min-entropy = 4.2, W= 2^-30 */
+/* With R, qbinom(1-2^-30,64,2^-4.2) */
+#define ADAPTIVE_PROPORTION_64_TEST_CUTOFF 18
 
 static uint8_t ap64t_a;
 static uint8_t ap64t_b;
@@ -224,9 +306,9 @@ static void adaptive_proportion_64_test (uint8_t sample)
     }
 }
 
-/* Cuttoff = 354, when min-entropy = 4.0, W= 2^-30 */
-/* With R, qbinom(1-2^-30,4096,2^-4.0) */
-#define ADAPTIVE_PROPORTION_4096_TEST_CUTOFF 354
+/* Cuttoff = 315, when min-entropy = 4.2, W= 2^-30 */
+/* With R, qbinom(1-2^-30,4096,2^-4.2) */
+#define ADAPTIVE_PROPORTION_4096_TEST_CUTOFF 315
 
 static uint8_t ap4096t_a;
 static uint16_t ap4096t_b;
@@ -258,7 +340,7 @@ static void noise_source_continuous_test (uint8_t noise)
   adaptive_proportion_64_test (noise);
   adaptive_proportion_4096_test (noise);
 }
-
+\f
 /*
  * Ring buffer, filled by generator, consumed by neug_get routine.
  */
@@ -308,80 +390,7 @@ static uint32_t rb_del (struct rng_rb *rb)
   return v;
 }
 
-static uint8_t neug_raw;
-
-/**
- * @brief  Random number generation from ADC sampling.
- * @param  RB: Pointer to ring buffer structure
- * @return -1 when failure, 0 otherwise.
- * @note   Called holding the mutex, with RB->full == 0.
- *         Keep generating until RB->full == 1.
- */
-static int rng_gen (struct rng_rb *rb)
-{
-  uint8_t round = 0;
-  uint32_t v = 0;
-
-  while (1)
-    {
-      uint8_t b;
-
-      chEvtWaitOne (ADC_DATA_AVAILABLE);
-
-      /* Got an ADC sampling data */
-      b = (((samp[0] & 0x01) << 0) | ((samp[1] & 0x01) << 1)
-          | ((samp[2] & 0x01) << 2) | ((samp[3] & 0x01) << 3)
-          | ((samp[4] & 0x01) << 4) | ((samp[5] & 0x01) << 5)
-          | ((samp[6] & 0x01) << 6) | ((samp[7] & 0x01) << 7));
-
-      adcStartConversion (&ADCD1, &adcgrpcfg, samp, ADC_GRP1_BUF_DEPTH);
-
-      noise_source_continuous_test (b);
-      if (neug_raw)
-       {
-         v |= (b << (round * 8));
-         round++;
-         if (round >= 4)
-           {
-             rb_add (rb, v);
-             if (rb->full)
-               return 0;
-             v = 0;
-             round = 0;
-           }
-       }
-      else
-       {
-         /*
-          * Put a random byte to entropy pool.
-          */
-         ep_add (b);
-         round++;
-         if (round >= NUM_NOISE_INPUTS)
-           {
-             /*
-              * We have enough entropy in the pool.
-              * Thus, we pull the random bits from the pool.
-              */
-             int i;
-             const uint32_t *vp = ep_output ();
-
-             /* We get the random bits, add it to the ring buffer.  */
-             for (i = 0; i < SHA256_DIGEST_SIZE / 4; i++)
-               {
-                 rb_add (rb, *vp);
-                 vp++;
-                 if (rb->full)
-                   return 0;
-               }
-
-             round = 0;
-           }
-       }
-    }
-
-  return 0;                    /* success */
-}
+uint8_t neug_mode;
 
 /**
  * @brief Random number generation thread.
@@ -392,30 +401,51 @@ static msg_t rng (void *arg)
 
   rng_thread = chThdSelf ();
 
-  adcStart (&ADCD1, NULL);
-  adcStartConversion (&ADCD1, &adcgrpcfg, samp, ADC_GRP1_BUF_DEPTH);
+  /* Enable ADCs */
+  adc_start ();
+
+  ep_init (NEUG_MODE_CONDITIONED);
 
   while (!chThdShouldTerminate ())
     {
-      chMtxLock (&rb->m);
-      while (rb->full)
-       chCondWait (&rb->space_available);
-      while (1)
+      int n;
+      int mode = neug_mode;
+
+      chEvtWaitOne (ADC_DATA_AVAILABLE); /* Get ADC sampling.  */
+
+      if ((n = ep_process (mode)))
        {
-         rng_gen (rb);
-         if (neug_err_state != 0)
+         int i;
+         const uint32_t *vp;
+
+         if (neug_err_state != 0
+             && neug_mode == NEUG_MODE_CONDITIONED)
            {
-             while (!rb->empty)
-               (void)rb_del (rb);
+             /* Don't use the result and do it again.  */
              noise_source_error_reset ();
+             continue;
            }
-         else
-           break;
+
+         vp = ep_output (mode);
+
+         chMtxLock (&rb->m);
+         while (rb->full)
+           chCondWait (&rb->space_available);
+
+         for (i = 0; i < n; i++)
+           {
+             rb_add (rb, *vp++);
+             if (rb->full)
+               break;
+           }
+
+         chCondSignal (&rb->data_available);
+         chMtxUnlock ();
        }
-      chCondSignal (&rb->data_available);
-      chMtxUnlock ();
     }
 
+  adc_stop ();
+
   return 0;
 }
 
@@ -428,10 +458,21 @@ static WORKING_AREA(wa_rng, 256);
 void
 neug_init (uint32_t *buf, uint8_t size)
 {
+  const uint32_t *u = (const uint32_t *)unique_device_id ();
   struct rng_rb *rb = &the_ring_buffer;
+  int i;
 
-  neug_raw = 0;
-  ep_init ();
+  RCC->AHBENR |= RCC_AHBENR_CRCEN;
+  CRC->CR = CRC_CR_RESET;
+
+  /*
+   * This initialization ensures that it generates different sequence
+   * even if all physical conditions are same.
+   */
+  for (i = 0; i < 3; i++)
+    CRC->DR = *u++;
+
+  neug_mode = NEUG_MODE_CONDITIONED;
   rb_init (rb, buf, size);
   chThdCreateStatic (wa_rng, sizeof (wa_rng), NORMALPRIO, rng, rb);
 }
@@ -513,9 +554,19 @@ neug_fini (void)
 }
 
 void
-neug_select (uint8_t raw)
+neug_mode_select (uint8_t mode)
 {
   neug_wait_full ();
-  neug_raw = raw;
+  while (rng_thread->p_state != THD_STATE_WTCOND)
+    chThdSleep (MS2ST (1));
+
+  if (neug_mode != mode)
+    ep_init (mode);
+
+#if defined(BOARD_FST_01)
+  palClearPad (IOPORT1, 2);
+#endif
+
+  neug_mode = mode;
   neug_flush ();
 }