mirror of
https://github.com/taigrr/arduinolibs
synced 2025-01-18 04:33:12 -08:00
Random number generator class based on ChaCha
This commit is contained in:
parent
4fc27f1005
commit
6ec1b93cf9
@ -753,6 +753,7 @@ IMAGE_PATH = ../libraries/BlinkLED/examples/Cylon \
|
||||
../libraries/LCD/examples/HelloWorld \
|
||||
../libraries/LCD/examples/Form \
|
||||
../libraries/RTC/examples/AlarmClock \
|
||||
../libraries/Crypto \
|
||||
../libraries/DMD \
|
||||
../libraries/IR \
|
||||
../libraries/I2C
|
||||
|
@ -30,6 +30,7 @@
|
||||
\li Block cipher modes: CTR, CFB, CBC, OFB
|
||||
\li Stream ciphers: ChaCha
|
||||
\li Hash algorithms: SHA1, SHA256, BLAKE2s
|
||||
\li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource
|
||||
|
||||
All cryptographic algorithms have been optimized for 8-bit Arduino platforms
|
||||
like the Uno. Memory usage is also reduced, particularly for SHA1 and SHA256
|
||||
|
@ -94,6 +94,7 @@ realtime clock and the LCD library to implement an alarm clock.
|
||||
\li Block cipher modes: CTR, CFB, CBC, OFB
|
||||
\li Stream ciphers: ChaCha
|
||||
\li Hash algorithms: SHA1, SHA256, BLAKE2s
|
||||
\li Random number generation: \link RNGClass RNG\endlink, TransistorNoiseSource
|
||||
|
||||
More information can be found on the \ref crypto "Cryptographic Library" page.
|
||||
|
||||
|
105
libraries/Crypto/NoiseSource.cpp
Normal file
105
libraries/Crypto/NoiseSource.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "NoiseSource.h"
|
||||
#include "RNG.h"
|
||||
|
||||
/**
|
||||
* \class NoiseSource NoiseSource.h <NoiseSource.h>
|
||||
* \brief Abstract base class for random noise sources.
|
||||
*
|
||||
* \sa \link RNGClass RNG\endlink, TransistorNoiseSource
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Constructs a new random noise source.
|
||||
*/
|
||||
NoiseSource::NoiseSource()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Destroys this random noise source.
|
||||
*/
|
||||
NoiseSource::~NoiseSource()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \fn bool NoiseSource::calibrating() const
|
||||
* \brief Determine if the noise source is still calibrating itself.
|
||||
*
|
||||
* \return Returns true if calibration is in progress; false if the noise
|
||||
* source is generating valid random data.
|
||||
*
|
||||
* Noise sources that require calibration start doing so at system startup
|
||||
* and then switch over to random data generation once calibration is complete.
|
||||
* Since no random data is being generated during calibration, the output
|
||||
* from \link RNGClass::rand() RNG.rand()\endlink may be predictable.
|
||||
* Use \link RNGClass::available() RNG.available()\endlink to determine
|
||||
* when sufficient entropy is available to generate good random values.
|
||||
*
|
||||
* It is possible that the noise source never exits calibration. This can
|
||||
* happen if the input voltage is insufficient to trigger noise or if the
|
||||
* noise source is not connected. Noise sources may also periodically
|
||||
* recalibrate themselves.
|
||||
*
|
||||
* \sa stir()
|
||||
*/
|
||||
|
||||
/**
|
||||
* \fn void NoiseSource::stir()
|
||||
* \brief Stirs entropy from this noise source into the global random
|
||||
* number pool.
|
||||
*
|
||||
* This function should call output() to add the entropy from this noise
|
||||
* source to the global random number pool.
|
||||
*
|
||||
* The noise source should batch up the entropy data, providing between
|
||||
* 16 and 48 bytes of data each time. If the noise source does not have
|
||||
* sufficient entropy data at the moment, it should return without stiring
|
||||
* the current data in.
|
||||
*
|
||||
* \sa calibrating(), output()
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Called from subclasses to output noise to the global random
|
||||
* number pool.
|
||||
*
|
||||
* \param data Points to the noise data.
|
||||
* \param len Number of bytes of noise data.
|
||||
* \param credit The number of bits of entropy to credit for the data.
|
||||
* Note that this is bits, not bytes.
|
||||
*
|
||||
* The default implementation of this function calls
|
||||
* \link RNGClass::stir() RNG.stir()\endlink to add the entropy from
|
||||
* this noise source to the global random number pool.
|
||||
*
|
||||
* This function may be overridden by subclasses to capture the raw
|
||||
* output from the noise source before it is mixed into the pool to
|
||||
* allow the raw data to be analyzed for randomness.
|
||||
*/
|
||||
void NoiseSource::output(const uint8_t *data, size_t len, unsigned int credit)
|
||||
{
|
||||
RNG.stir(data, len, credit);
|
||||
}
|
42
libraries/Crypto/NoiseSource.h
Normal file
42
libraries/Crypto/NoiseSource.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CRYPTO_NOISESOURCE_H
|
||||
#define CRYPTO_NOISESOURCE_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
|
||||
class NoiseSource
|
||||
{
|
||||
public:
|
||||
NoiseSource();
|
||||
virtual ~NoiseSource();
|
||||
|
||||
virtual bool calibrating() const = 0;
|
||||
virtual void stir() = 0;
|
||||
|
||||
protected:
|
||||
virtual void output(const uint8_t *data, size_t len, unsigned int credit);
|
||||
};
|
||||
|
||||
#endif
|
540
libraries/Crypto/RNG.cpp
Normal file
540
libraries/Crypto/RNG.cpp
Normal file
@ -0,0 +1,540 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "RNG.h"
|
||||
#include "NoiseSource.h"
|
||||
#include "ChaCha.h"
|
||||
#include "Crypto.h"
|
||||
#include "utility/ProgMemUtil.h"
|
||||
#include <Arduino.h>
|
||||
#include <avr/eeprom.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* \class RNGClass RNG.h <RNG.h>
|
||||
* \brief Pseudo random number generator suitable for cryptography.
|
||||
*
|
||||
* Random number generators must be seeded properly before they can
|
||||
* be used or an adversary may be able to predict the random output.
|
||||
* Seed data may be:
|
||||
*
|
||||
* \li Device-specific, for example serial numbers or MAC addresses.
|
||||
* \li Application-specific, unique to the application. The tag that is
|
||||
* passed to begin() is an example of an application-specific value.
|
||||
* \li Noise-based, generated by a hardware random number generator
|
||||
* that provides unpredictable values from a noise source.
|
||||
*
|
||||
* The following example demonstrates how to initialise the random
|
||||
* number generator:
|
||||
*
|
||||
* \code
|
||||
* #include <SPI.h>
|
||||
* #include <Ethernet.h>
|
||||
* #include <Crypto.h>
|
||||
* #include <RNG.h>
|
||||
* #include <TransistorNoiseSource.h>
|
||||
*
|
||||
* // Noise source to seed the random number generator.
|
||||
* TransistorNoiseSource noise(A1);
|
||||
*
|
||||
* // MAC address for Ethernet communication.
|
||||
* byte mac_address[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
|
||||
*
|
||||
* void setup() {
|
||||
* // Initialize the Ethernet shield.
|
||||
* Ethernet.begin(mac_address);
|
||||
*
|
||||
* // Initialize the random number generator with the application tag
|
||||
* // "MyApp 1.0" and load the previous seed from EEPROM address 500.
|
||||
* RNG.begin("MyApp 1.0", 500);
|
||||
*
|
||||
* // Stir in the Ethernet MAC address.
|
||||
* RNG.stir(mac_address, sizeof(mac_address));
|
||||
*
|
||||
* // ...
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* The application should regularly call stir() to mix in new data from
|
||||
* the noise source and also regularly call loop():
|
||||
*
|
||||
* \code
|
||||
* void loop() {
|
||||
* // ...
|
||||
*
|
||||
* // If the noise source has accumulated new entropy, then stir it in.
|
||||
* RNG.stir(noise);
|
||||
*
|
||||
* // Perform regular housekeeping on the random number generator.
|
||||
* RNG.loop();
|
||||
*
|
||||
* // ...
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* The loop() function will automatically save the random number seed on a
|
||||
* regular basis. By default the seed is saved every hour but this can be
|
||||
* changed using setAutoSaveTime().
|
||||
*
|
||||
* Keep in mind that saving too often may cause the EEPROM to wear out quicker.
|
||||
* It is wise to limit saving to once an hour or once a day depending
|
||||
* upon how long you intend to field the device before replacing it.
|
||||
* For example, an EEPROM rated for 100k erase/write cycles will last about
|
||||
* 69 days saving once a minute or 11 years saving once an hour.
|
||||
*
|
||||
* The application can still elect to call save() at any time if wants.
|
||||
* For example, if the application can detect power loss or shutdown
|
||||
* conditions programmatically, then it may make sense to force a save()
|
||||
* of the seed upon shutdown.
|
||||
*
|
||||
* \sa NoiseSource
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Global random number generator instance.
|
||||
*
|
||||
* \sa RNGClass
|
||||
*/
|
||||
RNGClass RNG;
|
||||
|
||||
/**
|
||||
* \var RNGClass::SEED_SIZE
|
||||
* \brief Size of a saved random number seed in EEPROM space.
|
||||
*/
|
||||
|
||||
// Number of ChaCha hash rounds to use for random number generation.
|
||||
#define RNG_ROUNDS 20
|
||||
|
||||
// Force a rekey after this many blocks of random data.
|
||||
#define RNG_REKEY_BLOCKS 16
|
||||
|
||||
// Maximum entropy credit that can be contained in the pool.
|
||||
#define RNG_MAX_CREDITS 384
|
||||
|
||||
/** @cond */
|
||||
|
||||
// Tag for 256-bit ChaCha20 keys. This will always appear in the
|
||||
// first 16 bytes of the block. The remaining 48 bytes are the seed.
|
||||
static const char tagRNG[16] PROGMEM = {
|
||||
'e', 'x', 'p', 'a', 'n', 'd', ' ', '3',
|
||||
'2', '-', 'b', 'y', 't', 'e', ' ', 'k'
|
||||
};
|
||||
|
||||
// Initialization seed. This is the ChaCha20 output of hashing
|
||||
// "expand 32-byte k" followed by 48 bytes set to the numbers 1 to 48.
|
||||
// The ChaCha20 output block is then truncated to the first 48 bytes.
|
||||
//
|
||||
// This value is intended to start the RNG in a semi-chaotic state if
|
||||
// we don't have a previously saved seed in EEPROM.
|
||||
static const uint8_t initRNG[48] PROGMEM = {
|
||||
0xB0, 0x2A, 0xAE, 0x7D, 0xEE, 0xCB, 0xBB, 0xB1,
|
||||
0xFC, 0x03, 0x6F, 0xDD, 0xDC, 0x7D, 0x76, 0x67,
|
||||
0x0C, 0xE8, 0x1F, 0x0D, 0xA3, 0xA0, 0xAA, 0x1E,
|
||||
0xB0, 0xBD, 0x72, 0x6B, 0x2B, 0x4C, 0x8A, 0x7E,
|
||||
0x34, 0xFC, 0x37, 0x60, 0xF4, 0x1E, 0x22, 0xA0,
|
||||
0x0B, 0xFB, 0x18, 0x84, 0x60, 0xA5, 0x77, 0x72
|
||||
};
|
||||
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* \brief Constructs a new random number generator instance.
|
||||
*
|
||||
* This constructor must be followed by a call to begin() to
|
||||
* properly initialize the random number generator.
|
||||
*
|
||||
* \sa begin()
|
||||
*/
|
||||
RNGClass::RNGClass()
|
||||
: address(0)
|
||||
, credits(0)
|
||||
, firstSave(1)
|
||||
, timer(0)
|
||||
, timeout(3600000UL) // 1 hour in milliseconds
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Destroys this random number generator instance.
|
||||
*/
|
||||
RNGClass::~RNGClass()
|
||||
{
|
||||
clean(block);
|
||||
clean(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initializes the random number generator.
|
||||
*
|
||||
* \param tag A string that is stirred into the random pool at startup;
|
||||
* usually this should be a value that is unique to the application and
|
||||
* version such as "MyApp 1.0" so that different applications do not
|
||||
* generate the same sequence of values upon first boot.
|
||||
* \param eepromAddress The EEPROM address to load the previously saved
|
||||
* seed from and to save new seeds when save() is called. There must be
|
||||
* at least SEED_SIZE (49) bytes of EEPROM space available at the address.
|
||||
*
|
||||
* This function should be followed by calls to stir() to mix in
|
||||
* additional entropy data from noise sources to initialize the random
|
||||
* number generator properly.
|
||||
*
|
||||
* \sa stir(), save()
|
||||
*/
|
||||
void RNGClass::begin(const char *tag, int eepromAddress)
|
||||
{
|
||||
// Save the EEPROM address for use by save().
|
||||
address = eepromAddress;
|
||||
|
||||
// Initialize the ChaCha20 input block from the saved seed.
|
||||
memcpy_P(block, tagRNG, sizeof(tagRNG));
|
||||
memcpy_P(block + 4, initRNG, sizeof(initRNG));
|
||||
if (eeprom_read_byte((const uint8_t *)address) == 'S') {
|
||||
// We have a saved seed: XOR it with the initialization block.
|
||||
for (int posn = 0; posn < 12; ++posn) {
|
||||
block[posn + 4] ^=
|
||||
eeprom_read_dword((const uint32_t *)(address + posn * 4 + 1));
|
||||
}
|
||||
}
|
||||
|
||||
// No entropy credits for the saved seed.
|
||||
credits = 0;
|
||||
|
||||
// Trigger an automatic save once the entropy credits max out.
|
||||
firstSave = 1;
|
||||
|
||||
// Rekey the random number generator immediately.
|
||||
rekey();
|
||||
|
||||
// Stir in the supplied tag data but don't credit any entropy to it.
|
||||
if (tag)
|
||||
stir((const uint8_t *)tag, strlen(tag));
|
||||
|
||||
// Re-save the seed to obliterate the previous value and to ensure
|
||||
// that if the system is reset without a call to save() that we won't
|
||||
// accidentally generate the same sequence of random data again.
|
||||
save();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the amount of time between automatic seed saves.
|
||||
*
|
||||
* \param minutes The number of minutes between automatic seed saves.
|
||||
*
|
||||
* The default time between automatic seed saves is 1 hour.
|
||||
*
|
||||
* This function is intended to help with EEPROM wear by slowing down how
|
||||
* often seed data is saved as noise is stirred into the random pool.
|
||||
* The exact period to use depends upon how long you intend to field
|
||||
* the device before replacing it. For example, an EEPROM rated for
|
||||
* 100k erase/write cycles will last about 69 days saving once a minute
|
||||
* or 11 years saving once an hour.
|
||||
*
|
||||
* \sa save(), stir()
|
||||
*/
|
||||
void RNGClass::setAutoSaveTime(uint16_t minutes)
|
||||
{
|
||||
if (!minutes)
|
||||
minutes = 1; // Just in case.
|
||||
timeout = ((uint32_t)minutes) * 60000U;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Generates random bytes into a caller-supplied buffer.
|
||||
*
|
||||
* \param data Points to the buffer to fill with random bytes.
|
||||
* \param len Number of bytes to generate.
|
||||
*
|
||||
* Calling this function will decrease the amount of entropy in the
|
||||
* random number pool by \a len * 8 bits. If there isn't enough
|
||||
* entropy, then this function will still return \a len bytes of
|
||||
* random data generated from what entropy it does have.
|
||||
*
|
||||
* If the application requires a specific amount of entropy before
|
||||
* generating important values, the available() function can be
|
||||
* polled to determine when sufficient entropy is available.
|
||||
*
|
||||
* \sa available(), stir()
|
||||
*/
|
||||
void RNGClass::rand(uint8_t *data, size_t len)
|
||||
{
|
||||
// Decrease the amount of entropy in the pool.
|
||||
if (len > (credits / 8))
|
||||
credits = 0;
|
||||
else
|
||||
credits -= len * 8;
|
||||
|
||||
// Generate the random data.
|
||||
uint8_t count = 0;
|
||||
while (len > 0) {
|
||||
// Force a rekey if we have generated too many blocks in this request.
|
||||
if (count >= RNG_REKEY_BLOCKS) {
|
||||
rekey();
|
||||
count = 1;
|
||||
} else {
|
||||
++count;
|
||||
}
|
||||
|
||||
// Increment the low counter word and generate a new keystream block.
|
||||
++(block[12]);
|
||||
ChaCha::hashCore(stream, block, RNG_ROUNDS);
|
||||
|
||||
// Copy the data to the return buffer.
|
||||
if (len < 64) {
|
||||
memcpy(data, stream, len);
|
||||
break;
|
||||
} else {
|
||||
memcpy(data, stream, 64);
|
||||
data += 64;
|
||||
len -= 64;
|
||||
}
|
||||
}
|
||||
|
||||
// Force a rekey after every request.
|
||||
rekey();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Determine if there is sufficient entropy available for a
|
||||
* specific request size.
|
||||
*
|
||||
* \param len The number of bytes of random data that will be requested
|
||||
* via a call to rand().
|
||||
* \return Returns true if there is at least \a len * 8 bits of entropy
|
||||
* in the random number pool, or false if not.
|
||||
*
|
||||
* This function can be used by the application to wait for sufficient
|
||||
* entropy to become available from the system's noise sources before
|
||||
* generating important values. For example:
|
||||
*
|
||||
* \code
|
||||
* bool haveKey = false;
|
||||
* byte key[32];
|
||||
*
|
||||
* void loop() {
|
||||
* ...
|
||||
*
|
||||
* if (!haveKey && RNG.available(sizeof(key))) {
|
||||
* RNG.rand(key, sizeof(key));
|
||||
* haveKey = true;
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* If \a len is larger than the maximum number of entropy credits supported
|
||||
* by the random number pool (384 bits, 48 bytes), then the maximum will be
|
||||
* used instead. For example, asking if 512 bits (64 bytes) are available
|
||||
* will return true if in reality only 384 bits are available. If this is a
|
||||
* problem for the application's security requirements, then large requests
|
||||
* for random data should be broken up into smaller chunks with the
|
||||
* application waiting for the entropy pool to refill between chunks.
|
||||
*
|
||||
* \sa rand()
|
||||
*/
|
||||
bool RNGClass::available(size_t len) const
|
||||
{
|
||||
if (len >= (RNG_MAX_CREDITS / 8))
|
||||
return credits >= RNG_MAX_CREDITS;
|
||||
else
|
||||
return len <= (credits / 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Stirs additional entropy data into the random pool.
|
||||
*
|
||||
* \param data Points to the additional data to be stirred in.
|
||||
* \param len Number of bytes to be stirred in.
|
||||
* \param credit The number of bits of entropy to credit for the
|
||||
* data that is stirred in. Note that this is bits, not bytes.
|
||||
*
|
||||
* The maximum credit allowed is \a len * 8 bits, indicating that
|
||||
* every bit in the input \a data is good and random. Practical noise
|
||||
* sources are rarely that good, so \a credit will usually be smaller.
|
||||
* For example, to credit 2 bits of entropy per byte, the function
|
||||
* would be called as follows:
|
||||
*
|
||||
* \code
|
||||
* RNG.stir(noise_data, noise_bytes, noise_bytes * 2);
|
||||
* \endcode
|
||||
*
|
||||
* If \a credit is zero, then the \a data will be stirred in but no
|
||||
* entropy credit is given. This is useful for static values like
|
||||
* serial numbers and MAC addresses that are different between
|
||||
* devices but highly predictable.
|
||||
*
|
||||
* \sa loop()
|
||||
*/
|
||||
void RNGClass::stir(const uint8_t *data, size_t len, unsigned int credit)
|
||||
{
|
||||
// Increase the entropy credit.
|
||||
if ((credit / 8) >= len)
|
||||
credit = len * 8;
|
||||
if ((RNG_MAX_CREDITS - credits) > credit)
|
||||
credits += credit;
|
||||
else
|
||||
credits = RNG_MAX_CREDITS;
|
||||
|
||||
// Process the supplied input data.
|
||||
if (len > 0) {
|
||||
// XOR the data with the ChaCha input block in 48 byte
|
||||
// chunks and rekey the ChaCha cipher for each chunk to mix
|
||||
// the data in. This should scatter any "true entropy" in
|
||||
// the input across the entire block.
|
||||
while (len > 0) {
|
||||
size_t templen = len;
|
||||
if (templen > 48)
|
||||
templen = 48;
|
||||
uint8_t *output = ((uint8_t *)block) + 16;
|
||||
len -= templen;
|
||||
while (templen > 0) {
|
||||
*output++ ^= *data++;
|
||||
--templen;
|
||||
}
|
||||
rekey();
|
||||
}
|
||||
} else {
|
||||
// There was no input data, so just force a rekey so we
|
||||
// get some mixing of the state even without new data.
|
||||
rekey();
|
||||
}
|
||||
|
||||
// Save if this is the first time we have reached max entropy.
|
||||
// This provides some protection if the system is powered off before
|
||||
// the first auto-save timeout occurs.
|
||||
if (firstSave && credits >= RNG_MAX_CREDITS) {
|
||||
firstSave = 0;
|
||||
save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Stirs in data from a noise source into the random pool.
|
||||
*
|
||||
* \param source The noise source to obtain entropy data from.
|
||||
*
|
||||
* \sa save(), NoiseSource::stir()
|
||||
*/
|
||||
void RNGClass::stir(NoiseSource &source)
|
||||
{
|
||||
source.stir();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Saves the random seed to EEPROM.
|
||||
*
|
||||
* During system startup, noise sources typically won't have accumulated
|
||||
* much entropy. But startup is usually the time when the system most
|
||||
* needs to generate random data for session keys, IV's, and the like.
|
||||
*
|
||||
* The purpose of this function is to pass some of the accumulated entropy
|
||||
* from one session to the next after a loss of power. Thus, once the system
|
||||
* has been running for a while it will get progressively better at generating
|
||||
* random values and the accumulated entropy will not be completely lost.
|
||||
*
|
||||
* Normally it isn't necessary to call save() directly. The loop() function
|
||||
* will automatically save the seed on a periodic basis (default of 1 hour).
|
||||
*
|
||||
* The seed that is saved is generated in such a way that it cannot be used
|
||||
* to predict random values that were generated previously or subsequently
|
||||
* in the current session. So a compromise of the EEPROM contents of a
|
||||
* captured device should not result in compromise of random values
|
||||
* that have already been generated. However, if power is lost and the
|
||||
* system restarted, then there will be a short period of time where the
|
||||
* random state will be predictable from the seed. For this reason it is
|
||||
* very important to stir() in new noise data at startup.
|
||||
*
|
||||
* \sa loop(), stir()
|
||||
*/
|
||||
void RNGClass::save()
|
||||
{
|
||||
// Generate random data from the current state and save
|
||||
// that as the seed. Then force a rekey.
|
||||
++(block[12]);
|
||||
ChaCha::hashCore(stream, block, RNG_ROUNDS);
|
||||
eeprom_write_block(stream, (void *)(address + 1), 48);
|
||||
eeprom_update_byte((uint8_t *)address, 'S');
|
||||
rekey();
|
||||
timer = millis();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Run periodic housekeeping tasks on the random number generator.
|
||||
*
|
||||
* This function must be called on a regular basis from the application's
|
||||
* main "loop()" function.
|
||||
*/
|
||||
void RNGClass::loop()
|
||||
{
|
||||
// Save the seed if the auto-save timer has expired.
|
||||
if ((millis() - timer) >= timeout)
|
||||
save();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Destroys the data in the random number pool and the saved seed
|
||||
* in EEPROM.
|
||||
*
|
||||
* This function attempts to throw away any data that could theoretically be
|
||||
* used to predict previous and future outputs of the random number generator
|
||||
* if the device is captured, sold, or otherwise compromised.
|
||||
*
|
||||
* After this function is called, begin() must be called again to
|
||||
* re-initialize the random number generator, followed by stir() to
|
||||
* add in new entropy from system noise sources.
|
||||
*
|
||||
* \note The rand() and save() functions take some care to manage the
|
||||
* random number pool in a way that makes prediction of past outputs from a
|
||||
* captured state very difficult. Future outputs may be predictable if
|
||||
* noise or other high-entropy data is not mixed in with stir() on a
|
||||
* regular basis.
|
||||
*
|
||||
* \sa begin()
|
||||
*/
|
||||
void RNGClass::destroy()
|
||||
{
|
||||
clean(block);
|
||||
clean(stream);
|
||||
for (int posn = 0; posn < SEED_SIZE; ++posn)
|
||||
eeprom_write_byte((uint8_t *)(address + posn), 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Rekeys the random number generator.
|
||||
*/
|
||||
void RNGClass::rekey()
|
||||
{
|
||||
// Rekey the cipher for the next request by generating a new block.
|
||||
// This is intended to make it difficult to wind the random number
|
||||
// backwards if the state is captured later. The first 16 bytes of
|
||||
// "block" remain set to "tagRNG".
|
||||
++(block[12]);
|
||||
ChaCha::hashCore(stream, block, RNG_ROUNDS);
|
||||
memcpy(block + 4, stream, 48);
|
||||
|
||||
// Permute the high word of the counter using the system microsecond
|
||||
// counter to introduce a little bit of non-stir randomness for each
|
||||
// request. Note: If random data is requested on a predictable schedule
|
||||
// then this may not help very much. It is still necessary to stir in
|
||||
// high quality entropy data on a regular basis using stir().
|
||||
block[13] ^= micros();
|
||||
}
|
69
libraries/Crypto/RNG.h
Normal file
69
libraries/Crypto/RNG.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CRYPTO_RNG_h
|
||||
#define CRYPTO_RNG_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stddef.h>
|
||||
|
||||
class NoiseSource;
|
||||
|
||||
class RNGClass
|
||||
{
|
||||
public:
|
||||
RNGClass();
|
||||
~RNGClass();
|
||||
|
||||
void begin(const char *tag, int eepromAddress);
|
||||
|
||||
void setAutoSaveTime(uint16_t minutes);
|
||||
|
||||
void rand(uint8_t *data, size_t len);
|
||||
bool available(size_t len) const;
|
||||
|
||||
void stir(const uint8_t *data, size_t len, unsigned int credit = 0);
|
||||
void stir(NoiseSource &source);
|
||||
|
||||
void save();
|
||||
|
||||
void loop();
|
||||
|
||||
void destroy();
|
||||
|
||||
static const int SEED_SIZE = 49;
|
||||
|
||||
private:
|
||||
uint32_t block[16];
|
||||
uint32_t stream[16];
|
||||
int address;
|
||||
uint16_t credits : 15;
|
||||
uint16_t firstSave : 1;
|
||||
unsigned long timer;
|
||||
unsigned long timeout;
|
||||
|
||||
void rekey();
|
||||
};
|
||||
|
||||
extern RNGClass RNG;
|
||||
|
||||
#endif
|
258
libraries/Crypto/TransistorNoiseSource.cpp
Normal file
258
libraries/Crypto/TransistorNoiseSource.cpp
Normal file
@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "TransistorNoiseSource.h"
|
||||
#include "RNG.h"
|
||||
#include "Crypto.h"
|
||||
#include <Arduino.h>
|
||||
|
||||
/**
|
||||
* \class TransistorNoiseSource TransistorNoiseSource.h <TransistorNoiseSource.h>
|
||||
* \brief Processes the signal from a transistor-based noise source.
|
||||
*
|
||||
* This class processes input from a transistor-based noise source, such as
|
||||
* that described by <a href="http://robseward.com/misc/RNG2/">Rob Seward</a>.
|
||||
* See that Web page for full details on how such noise sources work,
|
||||
* how the output should be used, and caveats for the unwary.
|
||||
* For convenience, Rob's circuit is reproduced below:
|
||||
*
|
||||
* \image html transistor_noise_source.png
|
||||
*
|
||||
* The following example shows how to initialize a transistor-based noise
|
||||
* source and use it with \link RNGClass RNG\endlink. The noise is read
|
||||
* from the A1 pin on the Arduino and stirred into the random number pool
|
||||
* on a regular basis. For more information, see the documentation for
|
||||
* \link RNGClass RNG\endlink.
|
||||
*
|
||||
* \code
|
||||
* #include <Crypto.h>
|
||||
* #include <RNG.h>
|
||||
* #include <TransistorNoiseSource.h>
|
||||
*
|
||||
* // Noise source to seed the random number generator.
|
||||
* TransistorNoiseSource noise(A1);
|
||||
*
|
||||
* void setup() {
|
||||
* // Initialize the random number generator with the application tag
|
||||
* // "MyApp 1.0" and load the previous seed from EEPROM address 500.
|
||||
* RNG.begin("MyApp 1.0", 500);
|
||||
*
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* void loop() {
|
||||
* // ...
|
||||
*
|
||||
* // If the noise source has accumulated new entropy, then stir it in.
|
||||
* RNG.stir(noise);
|
||||
*
|
||||
* // Perform regular housekeeping on the random number generator.
|
||||
* RNG.loop();
|
||||
*
|
||||
* // ...
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* \sa \link RNGClass RNG\endlink, NoiseSource
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Theory of operation:
|
||||
|
||||
From Rob Seward's original design we need to find the median of the input
|
||||
signal. That is, the threshold at which half the signal is below the
|
||||
threshold (a zero) and the other half is above the threshold (a one).
|
||||
Rob used a histogram table to find the median which is memory-intensive.
|
||||
We cannot afford to spend that much memory finding the median.
|
||||
|
||||
In this implementation we divide the signal up into "buckets" of 1024
|
||||
samples. We pick a starting threshold and count the number of ones
|
||||
within the bucket. If the number of ones is between 45% to 55% of the
|
||||
total number of samples, then we use that threshold to convert the
|
||||
bucket into output bits. Otherwise we adjust the threshold up or down,
|
||||
discard the bucket, and try again.
|
||||
|
||||
After a few buckets, the threshold naturally settles at the median without
|
||||
needing a histogram. The rest of the bucket processing can be done online
|
||||
with storage needed only for the debiased output bits.
|
||||
|
||||
If the input voltage to the noise source is too low to generate noise,
|
||||
then the delta between the minimum and maximum samples in the bucket will
|
||||
be quite small. This is used to detect disconnection of the noise source.
|
||||
No output is generated when the noise source is disconnected.
|
||||
|
||||
With 1024 raw input samples we get roughly 256 output bits after
|
||||
Von Neumann debiasing. As a further check, the output will be discarded
|
||||
if less than 192 bits are generated. This can happen when the noise source
|
||||
is connected or disconnected: only part of the bucket is valid.
|
||||
|
||||
One of the properties of Rob's circuit design is that over time the median
|
||||
changes due to environmental factors and component wear. Because we adjust
|
||||
the threshold from bucket to bucket, it should naturally float up or down
|
||||
to the new median level as the circuit's properties change.
|
||||
|
||||
*/
|
||||
|
||||
// Number of ADC values that can be generated by analogRead().
|
||||
#define ADC_NUM 1024
|
||||
|
||||
// Number of samples to collect for a single noise "bucket".
|
||||
#define SAMPLES_NUM 1024
|
||||
|
||||
// Calculate a percentage of the sample bucket size.
|
||||
#define SAMPLES_PCT(num) ((int)(((long)SAMPLES_NUM) * (num) / 100L))
|
||||
|
||||
// Expected spread between the minimum and maximum ADC readings for
|
||||
// the noise source to be considered as operating correctly.
|
||||
#define NOISE_SPREAD (ADC_NUM / 8)
|
||||
|
||||
// Calibration states.
|
||||
#define NOISE_NOT_CALIBRATING 0
|
||||
#define NOISE_CALIBRATING 1
|
||||
|
||||
/**
|
||||
* \brief Constructs a new transitor-based noise source handler.
|
||||
*
|
||||
* \param pin The analog input pin that the noise will appear on.
|
||||
*/
|
||||
TransistorNoiseSource::TransistorNoiseSource(uint8_t pin)
|
||||
: threshold(ADC_NUM / 2)
|
||||
, _pin(pin)
|
||||
, calState(NOISE_CALIBRATING)
|
||||
{
|
||||
// Configure the pin as an analog input with no pull-up.
|
||||
pinMode(pin, INPUT);
|
||||
digitalWrite(pin, LOW);
|
||||
|
||||
// Start the bit collection routines.
|
||||
restart();
|
||||
}
|
||||
|
||||
TransistorNoiseSource::~TransistorNoiseSource()
|
||||
{
|
||||
restart();
|
||||
}
|
||||
|
||||
bool TransistorNoiseSource::calibrating() const
|
||||
{
|
||||
return calState != NOISE_NOT_CALIBRATING;
|
||||
}
|
||||
|
||||
void TransistorNoiseSource::stir()
|
||||
{
|
||||
// Keep track of the minimum and maximum while generating data
|
||||
// so that we can detect when the input voltage falls too low
|
||||
// for the circuit to generate noise.
|
||||
int value = analogRead(_pin);
|
||||
if (value < minValue)
|
||||
minValue = value;
|
||||
if (value > maxValue)
|
||||
maxValue = value;
|
||||
|
||||
// Collect two bits of input and remove bias using the Von Neumann method.
|
||||
// If both bits are the same, then discard both. Otherwise choose one
|
||||
// of the bits and output that one. We have to do this carefully so that
|
||||
// instruction timing does not reveal the value of the bit that is chosen.
|
||||
uint8_t bit = ((threshold - value) >> 15) & 1; // Subtract and extract sign.
|
||||
if (count & 1) {
|
||||
if (prevBit ^ bit) {
|
||||
// The bits are different: add the new bit to the buffer.
|
||||
if (posn < sizeof(buffer)) {
|
||||
buffer[posn] = (buffer[posn] << 1) | bit;
|
||||
if (++bitNum >= 8) {
|
||||
++posn;
|
||||
bitNum = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
prevBit = bit;
|
||||
}
|
||||
|
||||
// Keep a count of the number of raw 1 bits.
|
||||
ones += bit;
|
||||
|
||||
// Bail out if we haven't collected enough samples for a full bucket yet.
|
||||
if (++count < SAMPLES_NUM)
|
||||
return;
|
||||
|
||||
// If the maximum minus the minimum is too small, then there probably
|
||||
// is no signal or the input voltage is insufficient to generate noise.
|
||||
// Discard the entire bucket and return to calibration.
|
||||
if ((maxValue - minValue) < NOISE_SPREAD) {
|
||||
restart();
|
||||
calState = NOISE_CALIBRATING;
|
||||
threshold = ADC_NUM / 2; // Reacquire threshold when the signal returns.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the number of 1's is between 45% and 55% of the total count,
|
||||
// then we have a good bucket. The threshold is at an appropriate level.
|
||||
if (ones >= SAMPLES_PCT(45) && ones <= SAMPLES_PCT(55)) {
|
||||
if (posn >= (sizeof(buffer) * 3 / 4)) {
|
||||
// The buffer is at least three-quarters full of debiased bits
|
||||
// so pass them onto output(). There may be less bits if we
|
||||
// lost or gained the signal half-way through the bucket.
|
||||
// Credit 4 bits of entropy for every 8 bits of output.
|
||||
output(buffer, posn, posn * 4);
|
||||
}
|
||||
restart();
|
||||
calState = NOISE_NOT_CALIBRATING;
|
||||
return;
|
||||
}
|
||||
|
||||
// The threshold is not close enough to the mid-point of the signal.
|
||||
// Adjust the threshold, discard the bucket, and try again.
|
||||
if (ones < SAMPLES_PCT(25) || ones > SAMPLES_PCT(75)) {
|
||||
// We are a long way away from the mid-point, so move the threshold
|
||||
// by a large amount based on the delta to get closer quicker.
|
||||
threshold -= (SAMPLES_PCT(50) - ones) / 8;
|
||||
} else if (ones < SAMPLES_PCT(50)) {
|
||||
// Not enough ones so move the threshold down a bit.
|
||||
--threshold;
|
||||
} else {
|
||||
// Too many ones so move the threshold up a bit.
|
||||
++threshold;
|
||||
}
|
||||
if (threshold < 0)
|
||||
threshold = 0;
|
||||
else if (threshold >= ADC_NUM)
|
||||
threshold = ADC_NUM - 1;
|
||||
restart();
|
||||
calState = NOISE_CALIBRATING;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Restarts the bit collection process for the next bucket.
|
||||
*/
|
||||
void TransistorNoiseSource::restart()
|
||||
{
|
||||
clean(buffer);
|
||||
prevBit = 0;
|
||||
posn = 0;
|
||||
bitNum = 0;
|
||||
minValue = ADC_NUM - 1;
|
||||
maxValue = 0;
|
||||
count = 0;
|
||||
ones = 0;
|
||||
}
|
55
libraries/Crypto/TransistorNoiseSource.h
Normal file
55
libraries/Crypto/TransistorNoiseSource.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Southern Storm Software, Pty Ltd.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef CRYPTO_TRANSISTORNOISESOURCE_H
|
||||
#define CRYPTO_TRANSISTORNOISESOURCE_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "NoiseSource.h"
|
||||
|
||||
class TransistorNoiseSource : public NoiseSource
|
||||
{
|
||||
public:
|
||||
explicit TransistorNoiseSource(uint8_t pin);
|
||||
virtual ~TransistorNoiseSource();
|
||||
|
||||
bool calibrating() const;
|
||||
|
||||
void stir();
|
||||
|
||||
private:
|
||||
int threshold;
|
||||
uint8_t _pin;
|
||||
uint8_t prevBit;
|
||||
uint8_t posn;
|
||||
uint8_t bitNum;
|
||||
uint8_t calState;
|
||||
uint8_t buffer[32];
|
||||
int minValue;
|
||||
int maxValue;
|
||||
int count;
|
||||
int ones;
|
||||
|
||||
void restart();
|
||||
};
|
||||
|
||||
#endif
|
@ -27,6 +27,7 @@ This example runs tests on the ChaCha implementation to verify correct behaviour
|
||||
#include <Crypto.h>
|
||||
#include <ChaCha.h>
|
||||
#include <string.h>
|
||||
#include <avr/pgmspace.h>
|
||||
|
||||
#define MAX_PLAINTEXT_SIZE 64
|
||||
#define MAX_CIPHERTEXT_SIZE 64
|
||||
@ -50,7 +51,7 @@ struct TestVector
|
||||
// specification doesn't contain test vectors - these were generated
|
||||
// using the reference implementation from http://cr.yp.to/chacha.html.
|
||||
|
||||
static TestVector const testVectorChaCha20_128 = {
|
||||
static TestVector const testVectorChaCha20_128 PROGMEM = {
|
||||
.name = "ChaCha20 128-bit",
|
||||
.key = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
|
||||
.keySize = 16,
|
||||
@ -76,7 +77,7 @@ static TestVector const testVectorChaCha20_128 = {
|
||||
.size = 64
|
||||
};
|
||||
|
||||
static TestVector const testVectorChaCha20_256 = {
|
||||
static TestVector const testVectorChaCha20_256 PROGMEM = {
|
||||
.name = "ChaCha20 256-bit",
|
||||
.key = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
|
||||
@ -104,7 +105,7 @@ static TestVector const testVectorChaCha20_256 = {
|
||||
.size = 64
|
||||
};
|
||||
|
||||
static TestVector const testVectorChaCha12_128 = {
|
||||
static TestVector const testVectorChaCha12_128 PROGMEM = {
|
||||
.name = "ChaCha12 128-bit",
|
||||
.key = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
|
||||
.keySize = 16,
|
||||
@ -130,7 +131,7 @@ static TestVector const testVectorChaCha12_128 = {
|
||||
.size = 64
|
||||
};
|
||||
|
||||
static TestVector const testVectorChaCha12_256 = {
|
||||
static TestVector const testVectorChaCha12_256 PROGMEM = {
|
||||
.name = "ChaCha12 256-bit",
|
||||
.key = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
|
||||
@ -158,7 +159,7 @@ static TestVector const testVectorChaCha12_256 = {
|
||||
.size = 64
|
||||
};
|
||||
|
||||
static TestVector const testVectorChaCha8_128 = {
|
||||
static TestVector const testVectorChaCha8_128 PROGMEM = {
|
||||
.name = "ChaCha8 128-bit",
|
||||
.key = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
|
||||
.keySize = 16,
|
||||
@ -184,7 +185,7 @@ static TestVector const testVectorChaCha8_128 = {
|
||||
.size = 64
|
||||
};
|
||||
|
||||
static TestVector const testVectorChaCha8_256 = {
|
||||
static TestVector const testVectorChaCha8_256 PROGMEM = {
|
||||
.name = "ChaCha8 256-bit",
|
||||
.key = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
201, 202, 203, 204, 205, 206, 207, 208, 209, 210,
|
||||
@ -212,6 +213,8 @@ static TestVector const testVectorChaCha8_256 = {
|
||||
.size = 64
|
||||
};
|
||||
|
||||
TestVector testVector;
|
||||
|
||||
ChaCha chacha;
|
||||
|
||||
byte buffer[128];
|
||||
@ -272,6 +275,9 @@ void testCipher(ChaCha *cipher, const struct TestVector *test)
|
||||
{
|
||||
bool ok;
|
||||
|
||||
memcpy_P(&testVector, test, sizeof(TestVector));
|
||||
test = &testVector;
|
||||
|
||||
Serial.print(test->name);
|
||||
Serial.print(" ... ");
|
||||
|
||||
@ -291,20 +297,15 @@ void testCipher(ChaCha *cipher, const struct TestVector *test)
|
||||
Serial.println("Failed");
|
||||
}
|
||||
|
||||
// The data space of this sketch is too big if we try to test the
|
||||
// performance of all of setKey(), encrypt(), and decrypt().
|
||||
// Since decryption is almost identical to encryption, only test
|
||||
// that if the PERF_DECRYPT option is enabled, suppressing setKey().
|
||||
//#define PERF_DECRYPT 1
|
||||
|
||||
#if !defined(PERF_DECRYPT)
|
||||
|
||||
void perfCipherSetKey(ChaCha *cipher, const struct TestVector *test)
|
||||
{
|
||||
unsigned long start;
|
||||
unsigned long elapsed;
|
||||
int count;
|
||||
|
||||
memcpy_P(&testVector, test, sizeof(TestVector));
|
||||
test = &testVector;
|
||||
|
||||
Serial.print(test->name);
|
||||
Serial.print(" SetKey ... ");
|
||||
|
||||
@ -322,14 +323,15 @@ void perfCipherSetKey(ChaCha *cipher, const struct TestVector *test)
|
||||
Serial.println(" per second");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void perfCipherEncrypt(ChaCha *cipher, const struct TestVector *test)
|
||||
{
|
||||
unsigned long start;
|
||||
unsigned long elapsed;
|
||||
int count;
|
||||
|
||||
memcpy_P(&testVector, test, sizeof(TestVector));
|
||||
test = &testVector;
|
||||
|
||||
Serial.print(test->name);
|
||||
Serial.print(" Encrypt ... ");
|
||||
|
||||
@ -348,14 +350,15 @@ void perfCipherEncrypt(ChaCha *cipher, const struct TestVector *test)
|
||||
Serial.println(" bytes per second");
|
||||
}
|
||||
|
||||
#if defined(PERF_DECRYPT)
|
||||
|
||||
void perfCipherDecrypt(ChaCha *cipher, const struct TestVector *test)
|
||||
{
|
||||
unsigned long start;
|
||||
unsigned long elapsed;
|
||||
int count;
|
||||
|
||||
memcpy_P(&testVector, test, sizeof(TestVector));
|
||||
test = &testVector;
|
||||
|
||||
Serial.print(test->name);
|
||||
Serial.print(" Decrypt ... ");
|
||||
|
||||
@ -374,17 +377,11 @@ void perfCipherDecrypt(ChaCha *cipher, const struct TestVector *test)
|
||||
Serial.println(" bytes per second");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void perfCipher(ChaCha *cipher, const struct TestVector *test)
|
||||
{
|
||||
#if !defined(PERF_DECRYPT)
|
||||
perfCipherSetKey(cipher, test);
|
||||
perfCipherEncrypt(cipher, test);
|
||||
#else
|
||||
perfCipherEncrypt(cipher, test);
|
||||
perfCipherDecrypt(cipher, test);
|
||||
#endif
|
||||
}
|
||||
|
||||
void setup()
|
||||
|
44
libraries/Crypto/examples/TestNoise/TestNoise.ino
Normal file
44
libraries/Crypto/examples/TestNoise/TestNoise.ino
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
// This example dumps the raw data from a transistor noise source
|
||||
// without any of the whitening normally performed by the RNG class.
|
||||
|
||||
#include <Crypto.h>
|
||||
#include <TransistorNoiseSource.h>
|
||||
|
||||
char const hexchars[] = "0123456789ABCDEF";
|
||||
|
||||
class RawNoiseSource : public TransistorNoiseSource
|
||||
{
|
||||
public:
|
||||
RawNoiseSource(uint8_t pin) : TransistorNoiseSource(pin) {}
|
||||
|
||||
protected:
|
||||
void output(const uint8_t *data, size_t len, unsigned int credit)
|
||||
{
|
||||
for (size_t posn = 0; posn < len; ++posn) {
|
||||
uint8_t value = data[posn];
|
||||
Serial.print(hexchars[(value >> 4) & 0x0F]);
|
||||
Serial.print(hexchars[value & 0x0F]);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
};
|
||||
|
||||
RawNoiseSource noise(A1);
|
||||
bool calibrating = true;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println();
|
||||
Serial.println("calibrating");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
noise.stir();
|
||||
bool nowCalibrating = noise.calibrating();
|
||||
if (nowCalibrating != calibrating) {
|
||||
calibrating = nowCalibrating;
|
||||
if (calibrating)
|
||||
Serial.println("calibrating");
|
||||
}
|
||||
}
|
70
libraries/Crypto/examples/TestRNG/TestRNG.ino
Normal file
70
libraries/Crypto/examples/TestRNG/TestRNG.ino
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
// Example of initializing and using the random number generator.
|
||||
|
||||
#include <Crypto.h>
|
||||
#include <RNG.h>
|
||||
#include <TransistorNoiseSource.h>
|
||||
|
||||
// Change "MyApp 1.0" to some other tag for your application
|
||||
// so that different applications will generate different results
|
||||
// even if the input noise or seed data is otherwise identical.
|
||||
#define RNG_APP_TAG "MyApp 1.0"
|
||||
|
||||
// EEPROM address to save the random number seed at.
|
||||
#define RNG_EEPROM_ADDRESS 500
|
||||
|
||||
// Noise source to seed the random number generator.
|
||||
TransistorNoiseSource noise(A1);
|
||||
|
||||
bool calibrating = false;
|
||||
byte data[32];
|
||||
unsigned long startTime;
|
||||
|
||||
void setup() {
|
||||
Serial.begin(9600);
|
||||
Serial.println("start");
|
||||
|
||||
// Initialize the random number generator.
|
||||
RNG.begin(RNG_APP_TAG, RNG_EEPROM_ADDRESS);
|
||||
|
||||
startTime = millis();
|
||||
}
|
||||
|
||||
void printHex(const byte *data, unsigned len)
|
||||
{
|
||||
static char const hexchars[] = "0123456789ABCDEF";
|
||||
unsigned long time = millis() - startTime;
|
||||
Serial.print(time / 1000);
|
||||
Serial.print('.');
|
||||
Serial.print((time / 100) % 10);
|
||||
Serial.print(": ");
|
||||
while (len > 0) {
|
||||
int b = *data++;
|
||||
Serial.print(hexchars[(b >> 4) & 0x0F]);
|
||||
Serial.print(hexchars[b & 0x0F]);
|
||||
--len;
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Track changes to the calibration state on the noise source.
|
||||
bool newCalibrating = noise.calibrating();
|
||||
if (newCalibrating != calibrating) {
|
||||
calibrating = newCalibrating;
|
||||
if (calibrating)
|
||||
Serial.println("calibrating");
|
||||
}
|
||||
|
||||
// If the noise source has accumulated new entropy, then stir it in.
|
||||
RNG.stir(noise);
|
||||
|
||||
// Perform regular housekeeping on the random number generator.
|
||||
RNG.loop();
|
||||
|
||||
// Generate output whenever 32 bytes of entropy have been accumulated.
|
||||
if (RNG.available(sizeof(data))) {
|
||||
RNG.rand(data, sizeof(data));
|
||||
printHex(data, sizeof(data));
|
||||
}
|
||||
}
|
203
libraries/Crypto/transistor_noise_source.fig
Normal file
203
libraries/Crypto/transistor_noise_source.fig
Normal file
@ -0,0 +1,203 @@
|
||||
#FIG 3.2 Produced by xfig version 3.2.5c
|
||||
Landscape
|
||||
Center
|
||||
Metric
|
||||
A4
|
||||
100.00
|
||||
Single
|
||||
-2
|
||||
1200 2
|
||||
0 32 #404040
|
||||
0 33 #808080
|
||||
0 34 #c0c0c0
|
||||
0 35 #e0e0e0
|
||||
0 36 #808080
|
||||
0 37 #c0c0c0
|
||||
0 38 #e0e0e0
|
||||
0 39 #444444
|
||||
0 40 #8e8f8e
|
||||
6 3465 4275 3690 4590
|
||||
5 1 0 1 0 -1 0 0 -1 0.000 1 0 0 0 3600.000 4600.000 3510 4480 3600 4450 3690 4480
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 1 -1 0 0 2
|
||||
3600 4450 3600 4590
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 1 -1 0 0 2
|
||||
3510 4410 3690 4410
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 1 -1 0 0 2
|
||||
3600 4410 3600 4275
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 1 -1 0 0 2
|
||||
3536 4304 3536 4374
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 1 -1 0 0 2
|
||||
3506 4339 3566 4339
|
||||
-6
|
||||
6 4725 4500 5220 4950
|
||||
1 3 0 1 0 -1 0 0 -1 0.000 1 0.0000 4995 4725 186 186 4995 4725 5040 4905
|
||||
2 1 0 1 0 -1 0 0 20 0.000 0 0 -1 0 0 4
|
||||
5095 4870 5050 4780 5005 4825 5095 4870
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
4950 4725 5175 4950
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
4950 4725 5175 4500
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
4950 4590 4950 4860
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
4950 4725 4725 4725
|
||||
-6
|
||||
6 4680 3600 4770 4050
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
4725 3955 4725 4050
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 7
|
||||
4725 3690 4685 3717 4765 3771 4685 3825 4765 3879 4685 3933
|
||||
4725 3960
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
4725 3600 4725 3695
|
||||
-6
|
||||
6 4230 4500 4725 4950
|
||||
1 3 0 1 0 -1 0 0 -1 0.000 1 0.0000 4455 4725 186 186 4455 4725 4410 4545
|
||||
2 1 0 1 0 -1 0 0 20 0.000 0 0 -1 0 0 4
|
||||
4355 4580 4400 4670 4445 4625 4355 4580
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
4500 4725 4275 4500
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
4500 4725 4275 4950
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
4500 4860 4500 4590
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
4500 4725 4725 4725
|
||||
-6
|
||||
6 5490 4185 5805 4365
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
5625 4275 5490 4275
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
5670 4275 5805 4275
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
5625 4365 5625 4185
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
5670 4365 5670 4185
|
||||
-6
|
||||
6 6075 4050 6570 4500
|
||||
1 3 0 1 0 -1 0 0 -1 0.000 1 0.0000 6345 4275 186 186 6345 4275 6390 4455
|
||||
2 1 0 1 0 -1 0 0 20 0.000 0 0 -1 0 0 4
|
||||
6445 4420 6400 4330 6355 4375 6445 4420
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
6300 4275 6525 4500
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
6300 4275 6525 4050
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
6300 4140 6300 4410
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
6300 4275 6075 4275
|
||||
-6
|
||||
6 5895 3600 5985 4050
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
5940 3955 5940 4050
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 7
|
||||
5940 3690 5900 3717 5980 3771 5900 3825 5980 3879 5900 3933
|
||||
5940 3960
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
5940 3600 5940 3695
|
||||
-6
|
||||
6 3555 3330 3645 3420
|
||||
1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 3600 3375 30 30 3600 3375 3600 3405
|
||||
-6
|
||||
6 4680 3330 4770 3420
|
||||
1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 4725 3375 30 30 4725 3375 4725 3405
|
||||
-6
|
||||
6 5895 3330 5985 3420
|
||||
1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 5940 3375 30 30 5940 3375 5940 3405
|
||||
-6
|
||||
6 5895 4230 5985 4320
|
||||
1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 5940 4275 30 30 5940 4275 5940 4305
|
||||
-6
|
||||
6 5130 4230 5220 4320
|
||||
1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 5175 4275 30 30 5175 4275 5175 4305
|
||||
-6
|
||||
6 4680 4230 4770 4320
|
||||
1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 4725 4275 30 30 4725 4275 4725 4305
|
||||
-6
|
||||
6 5130 5355 5220 5445
|
||||
1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 5175 5400 30 30 5175 5400 5175 5430
|
||||
-6
|
||||
6 3555 5355 3645 5445
|
||||
1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 3600 5400 30 30 3600 5400 3600 5430
|
||||
-6
|
||||
6 6480 5355 6570 5445
|
||||
1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 6525 5400 30 30 6525 5400 6525 5430
|
||||
-6
|
||||
6 7155 4635 7245 5085
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
7200 4990 7200 5085
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 7
|
||||
7200 4725 7160 4752 7240 4806 7160 4860 7240 4914 7160 4968
|
||||
7200 4995
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
7200 4635 7200 4730
|
||||
-6
|
||||
6 7155 3510 7245 3960
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
7200 3865 7200 3960
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 7
|
||||
7200 3600 7160 3627 7240 3681 7160 3735 7240 3789 7160 3843
|
||||
7200 3870
|
||||
2 1 0 1 0 -1 0 0 -1 0.000 0 0 -1 0 0 2
|
||||
7200 3510 7200 3605
|
||||
-6
|
||||
6 7155 4005 7245 4095
|
||||
1 3 0 1 0 -1 0 0 20 0.000 1 0.0000 7200 4050 30 30 7200 4050 7200 4080
|
||||
-6
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
5175 4950 5175 5400
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
3600 4545 3600 5400
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
3600 4275 3600 3375
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
4725 4050 4725 4275
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
4275 4950 4275 5085
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
|
||||
4275 4500 4275 4275 5175 4275 5175 4500
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
4725 3600 4725 3375
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
6525 4500 6525 5400
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
5490 4275 5130 4275
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
5760 4275 6120 4275
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
5940 4050 5940 4275
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
5940 3645 5940 3375
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
7200 5085 7200 5400
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
7200 3510 7200 3375
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
|
||||
1 1 1.00 60.00 120.00
|
||||
6525 4050 7650 4050
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
|
||||
7200 3915 7200 4635
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
|
||||
8 1 1.00 60.00 120.00
|
||||
2925 5400 7200 5400
|
||||
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 1 2
|
||||
8 1 1.00 60.00 120.00
|
||||
2925 3375 7200 3375
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 420 3015 4455 10uF\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 345 6705 3780 10K\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 390 6705 4905 4.7K\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 435 5400 3870 1.5M\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 390 4185 3915 4.7K\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 345 3060 4680 25V\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 525 5400 4545 100nF\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 165 240 5265 4770 Q2\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 165 240 6615 4320 Q3\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 270 3915 5175 NC\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 165 240 3960 4770 Q1\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 405 2385 5490 GND\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 180 1185 7785 4005 To Analog Pin\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 960 7875 4230 On Arduino\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 180 3735 3330 5805 All transistors: BC548, 2N3904, or equivalent\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 180 4440 3015 6075 Collector of Q1 must be left not connected to anything\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 810 2070 3600 10 to 15V\001
|
||||
4 0 0 50 -1 0 12 0.0000 4 135 330 2295 3375 VIN\001
|
BIN
libraries/Crypto/transistor_noise_source.png
Normal file
BIN
libraries/Crypto/transistor_noise_source.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Loading…
x
Reference in New Issue
Block a user