In this work we’ll consider a particular true random number generator (TRNG): TrueRNG v3. This device is developed by the company ubld.it, manufacturers of some cool electronics.

The TrueRNG Hardware Random Number Generator uses the avalanche effect in a semiconductor junction to generate true random numbers. The avalanche effect has long been used for generation of random number / noise and is a time-tested and proven random noise source.

The semiconductor junction is biased to 12 volts using a boost voltage regulator (since USB only supplies 5V), amplified, then digitized at high-speed. The digitized data is selected and whitened internal to the TrueRNG and sent over the USB port with more than 400 kilobits/second of throughput.

When working on Windows, it is convenient to access TrueRNG from the Windows Subsystem for Linux (WSL). We’ll explain how this can be done. (These notes partly follow Microsoft’s Connect USB devices.)

We need to make sure that usbipd-win v5.0.0 or higher is installed on Windows. If it isn’t, install it:

  1. Go to the latest release page for the usbipd-win project.
  2. Select the .msi file, which will download the installer. (You may get a warning asking you to confirm that you trust this download.)
  3. Run the downloaded usbipd-win_x.msi installer file.

To use USBIPD with Windows Subsystem for Linux (WSL), we need to have a Linux kernel version of 5.10.60.1 or higher. If the installed kernel version is older than 5.10.60.1, then it can be updated by first shutting down any running instances of WSL with

wsl --shutdown

and then running the command

wsl --update

(This can be done from the PowerShell Prompt.)

Obtain a list of the USB devices connected to Windows by opening PowerShell Prompt in administrator mode (use Run as Administrator) and entering the following command:

usbipd list

On our system, TrueRNG 3 appears as “USB Serial Device (COM3)” on this list when plugged in.

Next, we are going to share the device with WSL using usbipd bind:

usbipd bind --busid 2-4 --force

here 2-4 is the BUSID from the relevant entry in the BUSID list.

To attach the USB device, run the following command. (You no longer need to use an elevated administrator prompt.) Ensure that a WSL command prompt is open in order to keep the WSL 2 lightweight VM active. Note that as long as the USB device is attached to WSL, it cannot be used by Windows. Once attached to WSL, the USB device can be used by any distribution running as WSL 2.

usbipd attach --wsl --busid 2-4

You should see something like this:

usbipd: info: Using WSL distribution 'Ubuntu-22.04' to attach; the device will be available in all WSL 2 distributions.
usbipd: info: Loading vhci_hcd module.
usbipd: info: Detected networking mode 'nat'.
usbipd: info: Using IP address 172.21.64.1 to reach the host.

And then, you can verify that “Attached” appears as STATE in

usbipd list

For example:

Connected:
BUSID  VID:PID    DEVICE                                                        STATE
1-3    059f:10fd  USB Attached SCSI (UAS) Mass Storage Device                   Not shared
2-1    5986:213a  FHD Camera, FHD IR Camera, Generic software component         Not shared
2-2    1038:113a  USB Input Device                                              Not shared
2-3    27c6:6094  Goodix MOC Fingerprint                                        Not shared
2-4    04d8:f5fe  USB Serial Device (COM3)                                      Attached
2-10   8087:0033  Intel(R) Wireless Bluetooth(R)                                Not shared

Persisted:
GUID                                  DEVICE

It is also helpful to know that the device is associated with the port COM3.

You can then use the following truerng_read_example.py from WSL Linux to check that the device is working properly. Before running it with python truerng_read_example.py install the Pyserial package with:

python -m pip install pyserial

truerng_read_example.py:

#!/usr/bin/python3

# TrueRNG Read - Simple Example
# Chris K Cockrum
# 6/8/2020
#
# Requires Python 3.8, pyserial
#
# On Linux - may need to be root or set /dev/tty port permissions to 666
#
# Python 3.8.xx is available here: https://www.python.org/downloads/
# Install Pyserial package with:   python -m pip install pyserial

import serial
import time
import os
from serial.tools import list_ports

# Size of block for each loop
blocksize=102400

# Number of loops
numloops=10

# Set com port to default None
# Set this to the exact port name if you want to choose a specific port
# like this: rng_com_port = 'COM6'
rng_com_port = None

# Set mode (only has effect on TrueRNGpro and TrueRNGproV2)
capture_mode = 'MODE_NORMAL'

########################
# Function: modeChange #
########################
# Supported Modes
# MODE_NORMAL       300       /* Streams combined + Mersenne Twister */
# MODE_PSDEBUG      1200      /* PS Voltage in mV in ASCII */
# MODE_RNGDEBUG     2400      /* RNG Debug 0x0RRR 0x0RRR in ASCII */
# MODE_RNG1WHITE    4800      /* RNG1 + Mersenne Twister */
# MODE_RNG2WHITE    9600      /* RNG2 + Mersenns Twister*/
# MODE_RAW_BIN      19200     /* Raw ADC Samples in Binary Mode */
# MODE_RAW_ASC      38400     /* Raw ADC Samples in Ascii Mode */
# MODE_UNWHITENED   57600     /* Unwhitened RNG1-RNG2 (TrueRNGproV2 Only) */
# MODE_NORMAL_ASC   115200    /* Normal in Ascii Mode (TrueRNGproV2 Only) */
# MODE_NORMAL_ASC_SLOW 230400    /* Normal in Ascii Mode - Slow for small devices (TrueRNGproV2 Only) */
def modeChange(MODE, PORT):
    # "Knock" Sequence to activate mode change
    ser = serial.Serial(port=PORT,baudrate=110,timeout=1)
    time.sleep(0.5)
    ser.close()
    ser = serial.Serial(port=PORT,baudrate=300,timeout=1)
    ser.close()
    ser = serial.Serial(port=PORT,baudrate=110,timeout=1)
    ser.close()
    if MODE=='MODE_NORMAL':
        ser = serial.Serial(port=PORT,baudrate=300,timeout=1)
    if MODE=='MODE_PSDEBUG':
        ser = serial.Serial(port=PORT,baudrate=1200,timeout=1)
    if MODE=='MODE_RNGDEBUG':
        ser = serial.Serial(port=PORT,baudrate=2400,timeout=1)
    if MODE=='MODE_RNG1WHITE':
        ser = serial.Serial(port=PORT,baudrate=4800,timeout=1)
    if MODE=='MODE_RNG2WHITE':
        ser = serial.Serial(port=PORT,baudrate=9600,timeout=1)
    if MODE=='MODE_RAW_BIN':
        ser = serial.Serial(port=PORT,baudrate=19200,timeout=1)
    if MODE=='MODE_RAW_ASC':
        ser = serial.Serial(port=PORT,baudrate=38400,timeout=1)
    if MODE=='MODE_UNWHITENED':
        ser = serial.Serial(port=PORT,baudrate=57600,timeout=1)
    if MODE=='MODE_NORMAL_ASC':
        ser = serial.Serial(port=PORT,baudrate=115200,timeout=1)
    if MODE=='MODE_NORMAL_ASC_SLOW':
        ser = serial.Serial(port=PORT,baudrate=230400,timeout=1)
    ser.close()

# Print Header
print('TrueRNGpro Data Read Example')
print('http://ubld.it')
print('==================================================')


# Call list_ports to get com port info
ports_avaiable = list_ports.comports()

# Loop on all available ports to find TrueRNG
print('Com Port List')
for temp in ports_avaiable:
 #   print(temp[1] + ' : ' + temp[2])
    if '04D8:F5FE' in temp[2]:
        print('Found TrueRNG on ' + temp[0])
        if rng_com_port == None:        # always chooses the 1st TrueRNG found
            rng_com_port=temp[0]
    if '16D0:0AA0' in temp[2]:
        print('Found TrueRNGpro on ' + temp[0])
        if rng_com_port == None:        # always chooses the 1st TrueRNG found
            rng_com_port=temp[0]
    if '04D8:EBB5' in temp[2]:
        print('Found TrueRNGproV2 on ' + temp[0])
        if rng_com_port == None:        # always chooses the 1st TrueRNG found
            rng_com_port=temp[0]

print('==================================================')

# Print which port we're using
print('Using com port:  ' + str(rng_com_port))

# Print block size and number of loops
print('Block Size:      ' + '{:2.2f}'.format(blocksize/1000) + ' KB')
print('Number of loops: ' + str(numloops))
print('Total size:      ' + '{:2.2f}'.format(blocksize * numloops/1000000) + ' MB')
print('Writing to:      random.bin')
print('Capture Mode:    ' + capture_mode)
print('==================================================')

# Change to above mode (only has effect on the TrueRNGpro and TrueRNGproV2)
modeChange(capture_mode, rng_com_port)

# Open/create the file random.bin in the current directory with 'write binary'
fp=open('random.bin','wb')

# Print an error if we can't open the file
if fp==None:
    print('Error Opening File!')

# Try to setup and open the comport
try:
    ser = serial.Serial(port=rng_com_port,timeout=10)  # timeout set at 10 seconds in case the read fails
except:
    print('Port Not Usable!')
    print('Do you have permissions set to read ' + rng_com_port + ' ?')

# Open the serial port if it isn't open
if(ser.isOpen() == False):
    ser.open()

# Set Data Terminal Ready to start flow
ser.setDTR(True)

# This clears the receive buffer so we aren't using buffered data
ser.flushInput()

# Keep track of total bytes read
totalbytes=0

# Loop
for _ in range(numloops):

    # Try to read the port and record the time before and after
    try:
        before = time.time()    # in microseconds
        x=ser.read(blocksize)   # read bytes from serial port
        after = time.time()     # in microseconds
    except:
        print('Read Failed!!!')
        break

    # Update total bytes read
    totalbytes +=len(x)

    # If we were able to open the file, write to disk
    if fp !=0:
        fp.write(x)

    # Calculate the rate
    rate=float(blocksize) / ((after-before)*1000000.0) *8

    print(str(totalbytes) + ' Bytes Read at ' + '{:2.3f}'.format(rate) + ' Mbits/s')

# Close the serial port
ser.close()

# If the file is open then close it
if fp != 0:
    fp.close()

# If we're on Linux set min on com port back to 1
# Pyserial screws this up
if os.name == 'posix':
    os.system('stty -F '+rng_com_port+' min 1')

You should then see something like this:

TrueRNGpro Data Read Example
http://ubld.it
==================================================
Com Port List
Found TrueRNG on /dev/ttyACM0
==================================================
Using com port:  /dev/ttyACM0
Block Size:      102.40 KB
Number of loops: 10
Total size:      1.02 MB
Writing to:      random.bin
Capture Mode:    MODE_NORMAL
==================================================
102400 Bytes Read at 0.419 Mbits/s
204800 Bytes Read at 0.421 Mbits/s
307200 Bytes Read at 0.421 Mbits/s
409600 Bytes Read at 0.420 Mbits/s
512000 Bytes Read at 0.254 Mbits/s
614400 Bytes Read at 0.422 Mbits/s
716800 Bytes Read at 0.420 Mbits/s
819200 Bytes Read at 0.419 Mbits/s
921600 Bytes Read at 0.419 Mbits/s
1024000 Bytes Read at 0.420 Mbits/s

Leave a Reply

Your email address will not be published. Required fields are marked *