From 6ec1b93cf9d9471170cc0af352c82f0ea8ee9f68 Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Sun, 1 Mar 2015 12:08:35 +1000 Subject: [PATCH] Random number generator class based on ChaCha --- doc/Doxyfile | 1 + doc/crypto.dox | 1 + doc/mainpage.dox | 1 + libraries/Crypto/NoiseSource.cpp | 105 ++++ libraries/Crypto/NoiseSource.h | 42 ++ libraries/Crypto/RNG.cpp | 540 ++++++++++++++++++ libraries/Crypto/RNG.h | 69 +++ libraries/Crypto/TransistorNoiseSource.cpp | 258 +++++++++ libraries/Crypto/TransistorNoiseSource.h | 55 ++ .../Crypto/examples/TestChaCha/TestChaCha.ino | 45 +- .../Crypto/examples/TestNoise/TestNoise.ino | 44 ++ libraries/Crypto/examples/TestRNG/TestRNG.ino | 70 +++ libraries/Crypto/transistor_noise_source.fig | 203 +++++++ libraries/Crypto/transistor_noise_source.png | Bin 0 -> 15062 bytes 14 files changed, 1410 insertions(+), 24 deletions(-) create mode 100644 libraries/Crypto/NoiseSource.cpp create mode 100644 libraries/Crypto/NoiseSource.h create mode 100644 libraries/Crypto/RNG.cpp create mode 100644 libraries/Crypto/RNG.h create mode 100644 libraries/Crypto/TransistorNoiseSource.cpp create mode 100644 libraries/Crypto/TransistorNoiseSource.h create mode 100644 libraries/Crypto/examples/TestNoise/TestNoise.ino create mode 100644 libraries/Crypto/examples/TestRNG/TestRNG.ino create mode 100644 libraries/Crypto/transistor_noise_source.fig create mode 100644 libraries/Crypto/transistor_noise_source.png diff --git a/doc/Doxyfile b/doc/Doxyfile index 2a60d107..0b73c089 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -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 diff --git a/doc/crypto.dox b/doc/crypto.dox index 5b823208..1e80ba50 100644 --- a/doc/crypto.dox +++ b/doc/crypto.dox @@ -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 diff --git a/doc/mainpage.dox b/doc/mainpage.dox index 651e6e40..d3f5a709 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -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. diff --git a/libraries/Crypto/NoiseSource.cpp b/libraries/Crypto/NoiseSource.cpp new file mode 100644 index 00000000..e11b6b11 --- /dev/null +++ b/libraries/Crypto/NoiseSource.cpp @@ -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 + * \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); +} diff --git a/libraries/Crypto/NoiseSource.h b/libraries/Crypto/NoiseSource.h new file mode 100644 index 00000000..c16a4122 --- /dev/null +++ b/libraries/Crypto/NoiseSource.h @@ -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 +#include + +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 diff --git a/libraries/Crypto/RNG.cpp b/libraries/Crypto/RNG.cpp new file mode 100644 index 00000000..fa23d9d9 --- /dev/null +++ b/libraries/Crypto/RNG.cpp @@ -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 +#include +#include + +/** + * \class RNGClass 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 + * #include + * #include + * #include + * #include + * + * // 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(); +} diff --git a/libraries/Crypto/RNG.h b/libraries/Crypto/RNG.h new file mode 100644 index 00000000..2d77e656 --- /dev/null +++ b/libraries/Crypto/RNG.h @@ -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 +#include + +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 diff --git a/libraries/Crypto/TransistorNoiseSource.cpp b/libraries/Crypto/TransistorNoiseSource.cpp new file mode 100644 index 00000000..9ce6c9cf --- /dev/null +++ b/libraries/Crypto/TransistorNoiseSource.cpp @@ -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 + +/** + * \class TransistorNoiseSource 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 Rob Seward. + * 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 + * #include + * #include + * + * // 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; +} diff --git a/libraries/Crypto/TransistorNoiseSource.h b/libraries/Crypto/TransistorNoiseSource.h new file mode 100644 index 00000000..3f387102 --- /dev/null +++ b/libraries/Crypto/TransistorNoiseSource.h @@ -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 +#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 diff --git a/libraries/Crypto/examples/TestChaCha/TestChaCha.ino b/libraries/Crypto/examples/TestChaCha/TestChaCha.ino index cc48bf9a..ed691bab 100644 --- a/libraries/Crypto/examples/TestChaCha/TestChaCha.ino +++ b/libraries/Crypto/examples/TestChaCha/TestChaCha.ino @@ -27,6 +27,7 @@ This example runs tests on the ChaCha implementation to verify correct behaviour #include #include #include +#include #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() diff --git a/libraries/Crypto/examples/TestNoise/TestNoise.ino b/libraries/Crypto/examples/TestNoise/TestNoise.ino new file mode 100644 index 00000000..2b28746a --- /dev/null +++ b/libraries/Crypto/examples/TestNoise/TestNoise.ino @@ -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 +#include + +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"); + } +} diff --git a/libraries/Crypto/examples/TestRNG/TestRNG.ino b/libraries/Crypto/examples/TestRNG/TestRNG.ino new file mode 100644 index 00000000..d0df122e --- /dev/null +++ b/libraries/Crypto/examples/TestRNG/TestRNG.ino @@ -0,0 +1,70 @@ + +// Example of initializing and using the random number generator. + +#include +#include +#include + +// 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)); + } +} diff --git a/libraries/Crypto/transistor_noise_source.fig b/libraries/Crypto/transistor_noise_source.fig new file mode 100644 index 00000000..f216e798 --- /dev/null +++ b/libraries/Crypto/transistor_noise_source.fig @@ -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 diff --git a/libraries/Crypto/transistor_noise_source.png b/libraries/Crypto/transistor_noise_source.png new file mode 100644 index 0000000000000000000000000000000000000000..7356074a8906f0f9c6a021bddc0224291922407b GIT binary patch literal 15062 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