make;
make install;

some notes to myself

Blog

About

Feeding /dev/random

Written on June 25, 2016

Recently I was generating two 4096-bit RSA keys for PGP encryption & signing… and it took nearly 6 minutes for me. I can’t wait that long! GPG uses /dev/random as the RNG when generating a new key pair and there is no way to use /dev/urandom without modifying the GPG code.

A note from the urandom man page:

/dev/random should be suitable for uses that need very high quality randomness such as one-time pad or key generation. When the entropy pool is empty, reads from /dev/random will block until additional environmental noise is gathered.

A read from the /dev/urandom device will not block waiting for more entropy. As a result, if there is not sufficient entropy in the entropy pool, the returned values are theoretically vulnerable to a cryptographic attack on the algorithms used by the driver. Knowledge of how to do this is not available in the current unclassified literature, but it is theoretically possible that such an attack may exist. If this is a concern in your application, use /dev/random instead. 

My available entropy averages about 870 bits over 30 minutes during light computer use (reading Hacker News, listening to Pandora, etc.).

while true
do
    cat /proc/sys/kernel/random/entropy_avail >> /tmp/entropy-avail
    awk '{s+=$1} END {print s / NR}' /tmp/entropy-avail
    sleep 10
done

Obviously the available entropy will drop as soon as you start to make use of /dev/random. Take a look at your available entropy after running dd if=/dev/random of=/dev/null bs=1 count=512.

You might be waiting a while. The fact that /dev/random blocks can be a pain.

I ran a simple loop time test generating two 2048-bit RSA keys (the GPG defaults) for PGP encrypting and signing.

i="0"
while [ $i -lt 10 ]
do
time gpg2 --batch --gen-key <<EOF
%echo Generating a GPG key
Key-Type: default
Key-Length: default
Name-Real: testing
Name-Comment: this is a test
Name-Email: testing@test
Expire-Date: 1
Passphrase: testing123
%pubring foo.pub
%secring foo.sec
%commit
%echo done
EOF
i=$[$i+1]
done

The results for 10 tests were:

2:33.28
2:45.34
2:54.74
3:11.41
2:35.56
3:45.13
2:20.55
2:28.49
2:49.02
3:36.33

So, an average of 2:53.98, with a standard deviation of 1:24.58.

Hmm, how to improve this?

The answer: hardware.

Turns out there is a project out there to use a smartcard to feed entropy to /dev/random. You can also type “random 256” when running opensc-explorer to check your card. Anyway, I’ve trimmed down the code here:

#!/usr/bin/env python

import PyKCS11
import platform
import sys
import logging
import binascii
import struct
import fcntl

PKCS11_LIBRARY = '/usr/lib/x86_64-linux-gnu/opensc-pkcs11.so'
FS_DEV_RANDOM = '/dev/random'
PROC_ENTROPY_AVAIL = '/proc/sys/kernel/random/entropy_avail'
RNDADDENTROPY = 1074287107 # see include/uapi/linux/random.h in linux source

def print_entropy_avail():
    with open(PROC_ENTROPY_AVAIL, 'r') as entropy_avail:
        print('Entropy in pool: %s' % entropy_avail.readline())

if __name__ == '__main__':
        
    pkcs11 = PyKCS11.PyKCS11Lib()
    pkcs11.load(PKCS11_LIBRARY)
    pkcs11.getSlotList()    

    session = pkcs11.openSession(1) # I use slot 1, slot 0 is a "virtual" slot
    raw = session.generateRandom(256) # Pull 256 bits from the card
    random_sample = bytearray(raw)
    	
    if random_sample is not None:
        fmt = 'ii%is' % 256
        packed_data = struct.pack(fmt,
            len(random_sample) * 2,
            len(random_sample),
            str(random_sample))

    print_entropy_avail()
	
    with open(FS_DEV_RANDOM, 'a+') as dev_random:
        fcntl.ioctl(dev_random, RNDADDENTROPY, packed_data)
    
    print_entropy_avail()

Okay, now I’m going to run this in a loop while rerunning my 2048-bit test. The results:

17.330
15.005
17.606
14.781
14.742
17.496
14.877
17.677
17.455
17.112

An average of 16.41, with a standard devation of 2.93. Much improved!

As for the 4096-bit keys? It finished in 29.744 seconds. Good stuff.