This sample code is platform independent and can be run on Windows, Linux and macOS.
A sample source code for retrieving data from a SwiftRNG device utilizing the SwiftRNG device API.
import sys
import hashlib
import serial
from serial.tools import list_ports
# This Python 3 code demonstrates how to use the SwiftRNG device API.
# It retrieves entropy in 16,000-byte chunks and applies SHA-256 post-processing for SwiftRNG and SwiftRNG LE models.
# SwiftRNG Pro and SwiftRNG Z models don't require this post-processing.
# This code is compatible with all SwiftRNG models and works on Windows, Linux and macOS.
# You may need sudo permissions to open the device on Linux and macOS.
class SwiftRNG:
"""A simple example class for communicating with a SwiftRNG device over USB serial interface."""
def __init__(self):
self.post_process = True
self.ser = None
self.model = ""
self.sha = hashlib.sha256()
def __search_for_swiftrng_device(self):
"""Search for SwiftRNG devices using USB VID/PID and description."""
plist = list(serial.tools.list_ports.comports())
for p in plist:
desc = p.hwid
if "1FC9:8111" in desc and "SWRNG" in desc:
return p
return None
def __clear(self):
"""Clear the receive buffer from any data that may have been left from a previous command."""
self.ser.reset_input_buffer()
def post_process_enabled(self):
"""Retrieves a flag indicating if post processing is required for the connected device."""
return self.post_process
def get_model(self):
"""Converts device model code to a name."""
if self.ser is None:
raise Exception("Device not connected")
match self.model:
case b'LWTSWRNG':
return "SwiftRNG LE"
case b'SWFTRNGZ':
return "SwiftRNG Z"
case b'SWRNGPRO':
return "SwiftRNG Pro"
case b'SwiftRNG':
return "SwiftRNG"
case _:
return str(self.model, 'ASCII')
def get_serial_number(self):
"""Retrieves the device serial number."""
return str(self.process_cmd(b's', 15), 'ASCII')
def get_version(self):
"""Retrieves the device version."""
return str(self.process_cmd(b'v', 4), 'ASCII')
def get_device_name(self):
"""Retrieves the device name as it is used in OS. For example 'COM3' in Windows or '/dev/ttyUSB' in Linux"""
if self.ser is None:
raise Exception("Device not connected")
return self.device.device
def get_entropy(self):
"""Retrieves 16,000 bytes of entropy. A sha256 post processing is applied for SwiftRNG and SwiftRNG LE."""
x = self.process_cmd(b'x', 16000)
if self.post_process == False:
return x
else:
resp = bytearray()
blocks = 16000 / 32
cur_block = 0
while (cur_block < blocks):
subx = x[cur_block * 32 : cur_block * 32 + 32]
self.sha.update(subx)
cur_digest = self.sha.digest()
cur_block += 1
resp.extend(cur_digest)
return resp
def process_cmd(self, cmd, resp_size):
"""Sends a 1-byte command to device and returns a response. Handles timeout scenarios."""
if self.ser is None:
raise Exception("Device not connected")
retry_count = 5
while retry_count > 0:
try:
return self.__call_device_api(cmd, resp_size)
except serial.SerialTimeoutException:
retry_count -= 1
self.__clear()
raise serial.SerialTimeoutException("Timeout reached when processing command", cmd)
def __call_device_api(self, cmd, resp_size):
"""Sends a 1-byte command to device and returns a response. Validates the status byte."""
self.ser.write(cmd)
self.ser.flush()
act_resp_size = resp_size + 1
resp = self.ser.read(act_resp_size)
if len(resp) != act_resp_size:
raise serial.SerialTimeoutException("Time out when reading response from device")
status = resp[resp_size]
if status != 0:
raise Exception("Device internal state in error")
return resp[0:resp_size]
def close(self):
"""Closes the serial connection if previously established."""
if self.ser is None:
raise Exception("Device not connected")
self.ser.close()
self.ser = None
def open(self):
"""Opens a connection to an exisiting SwiftRNG device."""
if self.ser is not None:
raise Exception("Device already connected")
self.device = self.__search_for_swiftrng_device()
if self.device is not None:
self.ser = serial.Serial(self.device.device, timeout = 2, write_timeout = 0.05, exclusive = True)
self.__clear()
self.model = self.process_cmd(b'm', 8)
if b'SWFTRNGZ' == self.model or b'SWRNGPRO' == self.model:
# SwiftRNG Pro and SwiftRNG Z do not require post processing.
self.post_process = False
return True
else:
return False
def __del__(self):
if self.ser is not None:
self.ser.close()
self.ser = None
##################
# App main entry
##################
# Search for available SwiftRNG devices and open the first one available.
obj = SwiftRNG()
if obj.open() == False:
print("Could not find any SwiftRNG device")
sys.exit(1)
print("Found attached device:", obj.get_device_name())
print("Device model:", obj.get_model())
print("Device serial number:", obj.get_serial_number())
print("Device version:", obj.get_version())
print("Post process enabled:", obj.post_process_enabled())
# Retrieve one chunk (16,000 bytes) of entropy from device.
entropy = obj.get_entropy()
print("Retrieved", len(entropy), "bytes of entropy from device")
print("Printing the first 10 bytes from the entropy pool retrieved:")
for x in range(10):
print ("entropy [", x, "]:", entropy[x])
# Retrieve unprocessed and unmodified random bytes generated from the first noise source
noise_source_1 = obj.process_cmd(b'<', 16000)
print("Retrieved", len(noise_source_1), "raw bytes from noise source 1")
# Retrieve unprocessed and unmodified random bytes generated from the second noise source
noise_source_2 = obj.process_cmd(b'>', 16000)
print("Retrieved", len(noise_source_2), "raw bytes from noise source 2")
# Retrieve frequency table of each noise source. Each table contains 256 16-bit integers.
freq_table = obj.process_cmd(b'f', 1024)
print("Retrieved frequency tables")
obj.close()