On Saturday, March 14, 2015 at 10:36:28 AM UTC-4, Jean-Pierre Münch wrote:
>
> Hey everyone,
>
> as you may or may not know I'm currently modernizing Crypto++ to some 
> extent.
> During some of my other research I noticed that the LibreSSL team decided 
> to drop their (OpenSSL's) PRNG.
> They stated that it's not the job of the TLS library to provide users with 
> randomness but rather the OS's job.
>

I disagree.

Dropping PRNG code effectively violates sound security engineering 
principals. There's a lot to security engineering, but one of the 
foundations is: it must be easy to use correctly and hard to use 
incorrectly. Forcing users to re-invent it means its *not* easy to use 
correctly. And allowing each developer to "roll their own" makes its easy 
to use incorrectly.

Also, if RTFM was going to work, then it would have happened in the last 50 
years or so.
 

> So here comes my question:
>
> How far do we trust the PRNGs of Windows (CryptGenRandom()) and UNIX 
> (/dev/random?)?
>

This has already been studied and answered. See, for example:

    * Cryptanalysis of the Random Number Generator of the
        Windows Operating System, https://eprint.iacr.org/2007/419.pdf.

    * Analysis of the Linux Random Number Generator,
        https://eprint.iacr.org/2006/086.pdf.
 

> Is it necessary to find any source of potential entropy we can get or do 
> we just sit there and use the entropy the OS provides to us?
>

OS entropy should be one input to the generator. You should add other 
entropy if you have it available.

For example, I have an entropy gatherer that collects entropy from sensors 
on mobile devices (see the code below I am using for iOS and Crypto++). As 
another example, if I am on a desktop with dev/urandom, 
/proc/sys/kernel/random/boot_id, /proc/sys/kernel/random/uuid and RDRAND, 
then I sample them and add to my generator's state (see the code below I am 
using for Linux and OpenSSL).

I actually avoid /dev/random because (1) I don't want to block, and (2) I 
think /dev/random and /dev/urandom are mostly equivalent. From the network 
attacker's perspective, they are equivalent.

You also have /dev/hw_rand and virtio on virtual machines. If available, 
you should sample them, too.

Also see Peter Gutmann's comments at 
http://lists.randombit.net/pipermail/cryptography/2013-July/004746.html. I 
tend to agree with Gutmann: add any entropy you can get your hands on, even 
the less than perfect ones. That's why I am happy to add the high 
resolution time stamp, the pid, boot_id and uuid.
 

> Depending on your answers I'll adapt my Fortuna implementation (if we 
> trust in the OS, the OS will feed the pools, if not I have to do it).
>

I think it depends on your threat model and security goals for Fortuna. 
Either the built-in Crypto++ stuff meets your requirements, or they don't 
and you have to address the gaps.

Now the master question: DO we even CAN get GOOD entropy in USERLAND? (-> 
> Crypto++'s main usage)
>

I believe so, as long as process isolation is working as expected. But it 
depends on your threat model and security goals.

Note: nearly all threat models I have seen related to PRNGs presume an 
attacker cannot determine the generator's internal state (either view it or 
predict it). This is usually fine for network based attackers and other 
userland processes. Obviously, the underlying OS can see your generator's 
state, so you can't defend against the OS or its compromise.

Jeff

**********

static CryptoPP::RandomPool& GetRandomPool()
{
    static CryptoPP::RandomPool pool;
    static dispatch_once_t once = 0;
    
    dispatch_once(&once, ^{
        CryptoPP::SecByteBlock seed(32);
        CryptoPP::OS_GenerateRandomBlock(false, seed.data(), seed.size());
        pool.IncorporateEntropy(seed.data(), seed.size());
    });
    
    // First, send in all the uninitialized data. Then:
    //  sesnors[0,1,2] uses accelerometer, if available
    //  sesnors[3,4,5] uses gyroscope, if available
    //  sesnors[6,7,8] uses magnetometer, if available
    CryptoPP::SecBlock<double> sensors(3 * 3);
    pool.IncorporateEntropy(sensors.BytePtr(), sensors.SizeInBytes());
    
    // See Event Handling Guide for iOS, Event Motions
    // 
https://developer.apple.com/library/ios/documentation/EventHandling/Conceptual/EventHandlingiPhoneOS/motion_event_basics/motion_event_basics.html
    CMMotionManager* mgr = [[CMMotionManager alloc] init];
    if(mgr == nil) { return pool; }
    
    // Start motion services
    if([mgr isAccelerometerAvailable] != NO)
        [mgr startAccelerometerUpdates];
    if([mgr isGyroAvailable] != NO)
        [mgr startGyroUpdates];
    if([mgr isMagnetometerAvailable] != NO)
        [mgr startMagnetometerUpdates];
    
    // The components starts slowly... A pause less than about 0.150s
    //  will cause individual sensor data to return nil.    
    [NSThread sleepForTimeInterval:0.150f];
    
    if([mgr isAccelerometerAvailable] != NO) {
        CMAccelerometerData* accelData = [mgr accelerometerData];
        if(accelData) {
            sensors[0] = [accelData acceleration].x;
            sensors[1] = [accelData acceleration].y;
            sensors[2] = [accelData acceleration].z;
        }
        [mgr stopAccelerometerUpdates];
    }
    
    if([mgr isGyroAvailable] != NO) {
        CMGyroData* gyroData = [mgr gyroData];
        if(gyroData) {
            sensors[3] = [gyroData rotationRate].x;
            sensors[4] = [gyroData rotationRate].y;
            sensors[5] = [gyroData rotationRate].z;
        }
        [mgr stopGyroUpdates];
    }
    
    if([mgr isMagnetometerAvailable] != NO) {
        CMMagnetometerData* magnetData = [mgr magnetometerData];
        if(magnetData) {
            sensors[6] = [magnetData magneticField].x;
            sensors[7] = [magnetData magneticField].y;
            sensors[8] = [magnetData magneticField].z;
        }
        [mgr stopMagnetometerUpdates];
    }
    
    pool.IncorporateEntropy(sensors.BytePtr(), sensors.SizeInBytes());
    
    [mgr release], mgr = nil;
    
#if !defined(NDEBUG)
    NSLog(@"\n  A: %f, %f, %f" \
          @"\n  G: %f, %f, %f" \
          @"\n  M: %f, %f, %f",
          sensors[0], sensors[1], sensors[2],
          sensors[3], sensors[4], sensors[5],
          sensors[6], sensors[7], sensors[8] );
#endif
    
    return pool;
}

**********

int ReseedPrng()
{
    int rc = 0;
    unsigned long err = 0;

    AcConfig config;
    ostringstream oss;
    unsigned int seed = 0;

    byte buffer1[RAND_BUFFER_BYTES], buffer2[RAND_BUFFER_BYTES];
    string seedFile = config.GetPrngSeedFile();
    AC_ASSERT(!seedFile.empty());

    /* Stash away some current bytes */
    rc = RAND_bytes(buffer1, (int) sizeof(buffer1));
    AC_ASSERT(rc == 1);

    rc = RAND_load_file(seedFile.c_str(), -1);
    err = ERR_get_error();

    AC_ASSERT(rc > 0);
    if (!(rc > 0))
    {
        rc = CreateSeedFile();
        AC_ASSERT(rc == 0);
        if (rc != 0)
        {
            oss << "RAND_load_file failed for seed file, error ";
            oss << err << ", " << std::hex << "0x" << err;
            LogError(oss);
        }
    }

    if (rc > 0)
        seed += rc;

    if (HasRDRAND())
    {
        rc = RDRAND_bytes(buffer2, sizeof(buffer2));
        AC_ASSERT(rc == RAND_BUFFER_BYTES);

        if (!(rc == RAND_BUFFER_BYTES))
        {
            oss << "RDRAND_bytes failed, requested " << sizeof(buffer2);
            oss << " bytes, received " << rc << " bytes";
            LogError(oss);
        }

        if (rc > 0)
            seed += rc;
    }

    /* Add previous random bytes with RDRAND bytes */
    RAND_seed(buffer1, (int) sizeof(buffer1));
    RAND_seed(buffer2, (int) sizeof(buffer2));

    /* /dev/urandom */
    rc = RAND_load_file("/dev/urandom", RAND_BUFFER_BYTES);
    AC_ASSERT(rc == RAND_BUFFER_BYTES);

    if (!(rc == RAND_BUFFER_BYTES))
    {
        oss << "/dev/urandom failed, requested " << RAND_BUFFER_BYTES;
        oss << " bytes, received " << rc << " bytes";
        LogError(oss);
    }

    if (rc > 0)
        seed += rc;

    /* High resolution time stamp */
    uint64_t tsc = rdtsc();
    RAND_seed(&tsc, (int) sizeof(tsc));
    seed += (int) sizeof(tsc);

    /* Process ID */
    pid_t pid = getpid();
    RAND_seed(&pid, (int) sizeof(pid));
    seed += (int) sizeof(pid);

#if defined(__linux__) || defined(__linux)
    /* Boot ID */
    rc = RAND_load_file("/proc/sys/kernel/random/boot_id", -1);
    AC_ASSERT(rc > 0);
    if (!(rc > 0))
    {
        LogError("RAND_load_file failed for RANDOM_BOOT_ID");
    }

    if (rc > 0)
        seed += rc;
#endif

#if defined(__linux__) || defined(__linux)
    /* UUID */
    rc = RAND_load_file("/proc/sys/kernel/random/uuid", -1);
    AC_ASSERT(rc > 0);
    if (!(rc > 0))
    {
        LogError("RAND_load_file failed for RANDOM_UUID");
    }

    if (rc > 0)
        seed += rc;
#endif

    /* Discard bytes for good measure */
    rc = RAND_bytes(buffer1, (int) sizeof(buffer1));
    AC_ASSERT(rc == 1);

    rc = RAND_bytes(buffer2, (int) sizeof(buffer2));
    AC_ASSERT(rc == 1);

    OPENSSL_cleanse(buffer1, sizeof(buffer1));
    OPENSSL_cleanse(buffer2, sizeof(buffer2));

    oss << "seeded PRNG with " << seed << " bytes";
    LogRelevant(oss);

    return (seed >= (unsigned int) RAND_BUFFER_BYTES) ? 0 : 1;
}

/* Shamelessly ripped from somewhere */
static uint64_t rdtsc(void)
{
    unsigned hi, lo;
    __asm__ __volatile__ (
            "rdtsc" : "=a"(lo), "=d"(hi)
    );

    return ((((uint64_t) hi) << 32) | (uint64_t) lo);
}

-- 
-- 
You received this message because you are subscribed to the "Crypto++ Users" 
Google Group.
To unsubscribe, send an email to [email protected].
More information about Crypto++ and this group is available at 
http://www.cryptopp.com.
--- 
You received this message because you are subscribed to the Google Groups 
"Crypto++ Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to