// onetimepad.cpp // // Copyright (C) 2006-2020 MicroNeil Research Corporation. // // This software is released under the MIT license. See LICENSE.TXT. #include "onetimepad.hpp" #include "timing.hpp" /* class OneTimePad { // One Time Pad generator. private: MANGLER PagGenerator; // MANGLER as a PRNG. PadBuffer Entropy(int Length = 1024); // System entropy source. public: ** OneTimePad(); // Constructor initializes w/ Entropy. ** PadBuffer Pad(int Length); // Get a pad of Length. ** void addEntropy(); // Add entropy from the system source. ** void addEntropy(PadBuffer Entropy); // Add entropy from this source. }; */ //////////////////////////////////////////////////////////////////////////////// // Platform specific strong entropy sourcing. #ifdef WIN32 //// WIN32 Strong Entropy Source == CryptGenRandom() /////////////////////////// #include #include namespace codedweller { PadBuffer OneTimePad::Entropy(int Length) { // Get a PadBuffer full of randomness. PadBuffer Buffer(Length, 0); // Start by initializing the buffer. HCRYPTPROV provider = 0; // We will need a handle for the source. if( // Try a two different sources. !CryptAcquireContext( // If we can get a hardware source &provider, NULL, NULL, PROV_INTEL_SEC, CRYPT_VERIFYCONTEXT)) { // from an intel CPU then use that first. if( // If we can't use get that !CryptAcquireContext( // entropy source for some reason then &provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { // try to use the RSA source. That should // always work, but if it doesn't we need provider = 0; // to know about it so we'll have a zero } // handle. } if(0 != provider) { // If we did get a good source then CryptGenRandom (provider, Length, (BYTE*)&Buffer[0]); // grab the random bit required and CryptReleaseContext(provider,0); // then let the provider go. StrongEntropyFlag = true; // We DID get strong entropy. } else { // If we did not get a good source StrongEntropyFlag = false; // then we are not strong. } return Buffer; // Return the data we got. } } // End namespace codddweller #else //// *NIX Strong Entropy Source == /dev/urandom //////////////////////////////// #include namespace codedweller { PadBuffer OneTimePad::Entropy(int Length) { // Get Length bytes of strong entropy. PadBuffer Buffer(Length, 0); // Initialize a buffer to hold them. try { // Handle this in a try block. std::ifstream Source("/dev/urandom", std::ios::binary); // Open /dev/urandom if possible. Source.read(reinterpret_cast(&Buffer[0]), Length); // Read data into the buffer. if(!Source.bad() && Source.gcount() == Length) { // If we got what we came for then StrongEntropyFlag = true; // we have strong cryptography. } else { // If we didn't then we are not StrongEntropyFlag = false; // strong, and don't have things } // to make us go. Source.close(); // We're done, so close the stream. } catch(...) { // If we had an exception then we StrongEntropyFlag = false; // did not get strong entropy. } return Buffer; // Return the buffer. } } // End namespace codedweller #endif // End Platform Specific Bits //////////////////////////////////////////////////////////////////////////////// namespace codedweller { // Lightweight entropy is built from a combination of the time in ms UTC that // the application was started, the number of milliseconds since that time in // milliseconds, the number and times of calls to addLightweightEntropy(), and // the state of the MANGLER engine at each call -- that state is effected by // the combined previous use of the MANGLER and any other entropy that was // added including the timing of those events (since they all trigger the // addLightweightEntropy() function. Timer OneTimePadRunTimer; // Millisecond entropy source. msclock LightweightEntropyBuffer; // Lightweight entropy bucket. void OneTimePad::addLightweightEntropy() { // Add entropy based on the msclock StartFill = OneTimePadRunTimer.getStartClock(); // initial start time of the app msclock ElapsedFill = OneTimePadRunTimer.getElapsedTime(); // and the number of millisecs since. msclock CombinedFill = StartFill ^ ElapsedFill; // XOR the two together to combine. CombinedFill = CombinedFill ^ LightweightEntropyBuffer; // Pick up some previous state entropy. unsigned char* PrimerBuffer = (unsigned char*) &CombinedFill; // Treat the value as a bunch of bytes. unsigned char* EntropyBuffer = (unsigned char*) &LightweightEntropyBuffer; // Likewise with the entropy buffer. for(size_t i = 0; i < sizeof(msclock); i++) { // Fold bytes into the mangler one EntropyBuffer[i] += // byte at a time, capturing the PadGenerator.Encrypt( // the results and using one extra PadGenerator.Encrypt(PrimerBuffer[i])); // round per byte to increase the } // amount of guessing an attacker } // needs to do. void OneTimePad::addEntropy() { // Add strong entropy if available. PadBuffer Fill = Entropy(); // Grab the entropy bits to add. for(size_t i = 0; i < Fill.size(); i++) { // Pump them in one byte at a PadGenerator.Encrypt( // time and then run an extra PadGenerator.Encrypt(Fill.at(i))); // round per byte to increase the } // amount of guessing an attacker } // needs to do. void OneTimePad::addEntropy(PadBuffer Entropy) { // Add entropy from a given source. addLightweightEntropy(); // Start with some lightweight entropy. for(size_t i = 0; i < Entropy.size(); i++) { // Then loop through the provided PadGenerator.Encrypt( // entropy and mix it in with one PadGenerator.Encrypt(Entropy.at(i))); // extra round per byte to increase } // the amount of guessing an attacker } // needs to do. PadBuffer OneTimePad::Pad(int Length) { // Grab a pad of a specific length. addLightweightEntropy(); // Add some lightweight entropy. PadBuffer Output; Output.reserve(Length); // Create a buffer the right size. unsigned char x; // Starting with an uninitialized for(int i = 0; i < Length; i++) // char, fill the buffer with Output.push_back(x = PadGenerator.Encrypt(x)); // random bytes from the mangler. return Output; // Return the new pad. } void* OneTimePad::fill(void* Object, int Size) { // Fill *Object with random bytes. PadBuffer FillData = Pad(Size); // Get a Pad of the correct size. unsigned char* Ptr = reinterpret_cast(Object); // Reinterpret the pointer type. for(int i = 0; i < Size; i++) Ptr[i] = FillData.at(i); // Fill the object with the Pad. return Object; // Return the object. } bool OneTimePad::isStrong() { return StrongEntropyFlag; } // Tell them if I'm strong! OneTimePad::OneTimePad() { // Initialize the one time pad. addLightweightEntropy(); // Add lightweight entropy. addEntropy(); // Add cryptographic entropy. unsigned char x; // Starting with an uninitialized for(int i = 0; i < 1024; i++) { // character, run 1024 rounds to x = PadGenerator.Encrypt(x); // reduce the predictability of the } // initial Mangler state. } // The OneTimePad object is ready. } // End namespace codedweller