aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/sw/trng_extract.py348
1 files changed, 348 insertions, 0 deletions
diff --git a/src/sw/trng_extract.py b/src/sw/trng_extract.py
new file mode 100755
index 0000000..0da8ae8
--- /dev/null
+++ b/src/sw/trng_extract.py
@@ -0,0 +1,348 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#=======================================================================
+#
+# trng_extract.py
+# --------------
+# This program extracts data values from the trng. The program supports
+# reading from the entropy providers as well as the rng output.
+# Extraxted data can be delivered in text or binary form.
+#
+#
+# Author: Joachim Strömbergson, Paul Sekirk
+# Copyright (c) 2014, SUNET
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#=======================================================================
+
+#-------------------------------------------------------------------
+# Python module imports.
+#-------------------------------------------------------------------
+import sys
+import io
+import fcntl
+import argparse
+
+
+#-------------------------------------------------------------------
+# Defines.
+#-------------------------------------------------------------------
+
+# Output control. Shall be flags set by the argument parser.
+VERBOSE = False
+DEBUG = False
+BINARY_OUTPUT = True
+
+
+# TRNG defines.
+TRNG_PREFIX = 0x00
+TRNG_ADDR_NAME0 = 0x00
+TRNG_ADDR_NAME1 = 0x01
+TRNG_ADDR_VERSION = 0x02
+ENT1_PREFIX = 0x05
+CSPRNG_PREFIX = 0x0b
+CSPRNG_DATA = 0x20
+
+
+# ENT1 defines. This is the Avalanche noise based entropy provider.
+ENT11_PREFIX = 0x05
+ENT1_NOISE = 0x20
+ENT1_DELTA = 0x30
+
+
+# ENT2 defines. This is the ROSC entropy provider.
+ENT2_PREFIX = 0x06
+ENT2_ADDR_NAME0 = 0x00
+ENT2_ADDR_NAME1 = 0x01
+ENT2_ADDR_VERSION = 0x02
+ENT2_DATA = 0x20
+ENT2_CTRL = 0x10
+ENT2_STATUS = 0x11
+ENT2_ENT_DATA = 0x20
+ENT2_ENT_RAW = 0x21
+ENT2_ROSC_OUT = 0x22
+
+
+# Mixer defines
+MIXER_PREFIX = 0x0a
+
+
+# CSPRNG defines
+CSPRNG_PREFIX = 0x0b
+
+
+
+# Command codes
+SOC = 0x55
+EOC = 0xaa
+READ_CMD = 0x10
+WRITE_CMD = 0x11
+RESET_CMD = 0x01
+
+
+# Response codes
+SOR = 0xaa
+EOR = 0x55
+READ_OK = 0x7f
+WRITE_OK = 0x7e
+RESET_OK = 0x7d
+UNKNOWN = 0xfe
+ERROR = 0xfd
+
+
+# I2C interface defines
+# from /usr/include/linux/i2c-dev.h
+I2C_SLAVE = 0x0703
+I2C_DEVICE = "/dev/i2c-2"
+I2C_ADDR = 0x0f
+
+
+# Number of 32 bit data words extracted in a run.
+# Should be set by the arg parser.
+NUM_WORDS = 40000000
+
+
+#----------------------------------------------------------------
+# hexlist()
+#
+# Helper function to cretae a list of hex numbers from a
+# given list of values.
+#----------------------------------------------------------------
+def hexlist(list):
+ return "[ " + ' '.join('%02x' % b for b in list) + " ]"
+
+
+#----------------------------------------------------------------
+# I2C class
+#
+# Handles the actual device including reading and writing
+# bytes from the device.
+#----------------------------------------------------------------
+class I2C:
+ # file handle for the i2c device
+ file = None
+
+ # constructor: initialize the i2c communications channel
+ def __init__(self, dev, addr):
+ self.dev = dev
+ self.addr = addr
+ try:
+ self.file = io.FileIO(self.dev, 'r+b')
+ except IOError as e:
+ print "Unable to open %s: %s" % (self.dev, e.strerror)
+ sys.exit(1)
+ try:
+ fcntl.ioctl(self.file, I2C_SLAVE, self.addr)
+ except IOError as e:
+ print "Unable to set I2C slave device 0x%02x: %s" % (self.addr, e.strerror)
+ sys.exit(1)
+
+ # destructor: close the i2c communications channel
+ def __del__(self):
+ if (self.file):
+ self.file.close()
+
+ # write a command to the i2c device
+ def write(self, buf):
+ if DEBUG:
+ print "write %s" % hexlist(buf)
+ self.file.write(bytearray(buf))
+
+ # read one response byte from the i2c device
+ def read(self):
+ # read() on the i2c device will only return one byte at a time,
+ # and tc.get_resp() needs to parse the response one byte at a time
+ return ord(self.file.read(1))
+
+
+#----------------------------------------------------------------
+# Commerror()
+#
+# Empty class exception eater.
+#----------------------------------------------------------------
+class Commerror(Exception):
+ pass
+
+
+#----------------------------------------------------------------
+# Comm
+#
+# Class for communicating with the HW via the I2C interface
+#----------------------------------------------------------------
+class Comm:
+ def __init__(self):
+ self.i2c = I2C(I2C_DEVICE, I2C_ADDR)
+
+ def send_write_cmd(self, prefix, addr, data):
+ buf = [SOC, WRITE_CMD, prefix, addr]
+ for s in (24, 16, 8, 0):
+ buf.append(data >> s & 0xff)
+ buf.append(EOC)
+ self.i2c.write(buf)
+
+ def send_read_cmd(self, prefix, addr):
+ buf = [SOC, READ_CMD, prefix, addr, EOC]
+ self.i2c.write(buf)
+
+ def get_resp(self):
+ buf = []
+ len = 2
+ i = 0
+ while i < len:
+ b = self.i2c.read()
+ if ((i == 0) and (b != SOR)):
+ # we've gotten out of sync, and there's probably nothing we can do
+ print "response byte 0: expected 0x%02x (SOR), got 0x%02x" % (SOR, b)
+ raise CommError()
+ elif (i == 1): # response code
+ try:
+ # anonymous dictionary of message lengths
+ len = {READ_OK:9, WRITE_OK:5, RESET_OK:3, ERROR:4, UNKNOWN:4}[b]
+ except KeyError: # unknown response code
+ # we've gotten out of sync, and there's probably nothing we can do
+ print "unknown response code 0x%02x" % b
+ raise CommError()
+ buf.append(b)
+ i += 1
+ if DEBUG:
+ print "read %s" % hexlist(buf)
+ return buf
+
+ def write_data(self, prefix, addr, data):
+ self.send_write_cmd(prefix, addr, data)
+ return self.get_resp()
+
+ def read_data(self, prefix, addr):
+ self.send_read_cmd(prefix, addr)
+ return self.get_resp()
+
+
+#----------------------------------------------------------------
+# print_data()
+#
+# Print either text or binary data to std out.
+#----------------------------------------------------------------
+def print_data(my_data):
+ my_bytes = my_data[4 : 8]
+
+ if (BINARY_OUTPUT):
+ for my_byte in my_bytes:
+ sys.stdout.write(chr(my_byte))
+
+ else:
+ print("0x%02x 0x%02x 0x%02x 0x%02x" %
+ (my_bytes[0], my_bytes[1], my_bytes[2], my_bytes[3]))
+
+
+#----------------------------------------------------------------
+# wait_ready()
+#
+# Wait for the ready bit in the status register for the
+# given core to be set accessible via the given device.
+#----------------------------------------------------------------
+def wait_ready(dev, prefix, addr):
+ my_status = False
+ while not my_status:
+ my_status = dev.read_data(prefix, addr)[7]
+ if VERBOSE:
+ print("Status: %s" % my_status)
+
+
+#----------------------------------------------------------------
+# get_avalanche_entropy()
+#----------------------------------------------------------------
+def get_avalanche_entropy(dev):
+ if VERBOSE:
+ print "Reading avalanche entropy data."
+
+ for i in range(NUM_WORDS):
+ dev.read_data(ENT1_PREFIX, ENT1_DATA)
+
+
+#----------------------------------------------------------------
+# get_rosc_entropy()
+#----------------------------------------------------------------
+def get_rosc_entropy(dev):
+ if VERBOSE:
+ print "Reading rosc entropy data."
+
+# for i in range(NUM_WORDS):
+ for i in range(10):
+ wait_ready(dev, ENT2_PREFIX, ENT2_STATUS)
+ my_data = dev.read_data(ENT2_PREFIX, ENT2_ENT_DATA)
+ print_data(my_data)
+
+
+#----------------------------------------------------------------
+# looptest()
+#
+# Simple test that loops a large number of times to see
+# if the ready bit is ever cleared.
+#----------------------------------------------------------------
+def looptest(dev):
+ print("TRNG name: ", my_commdev.read_data(TRNG_PREFIX, TRNG_ADDR_NAME0))
+ print("ENT2 status: ", my_commdev.read_data(ENT2_PREFIX, ENT2_STATUS))
+
+ for i in range(100000000):
+ ent_data = dev.read_data(ENT2_PREFIX, ENT2_ENT_DATA)
+ ent_status = dev.read_data(ENT2_PREFIX, ENT2_STATUS)
+ if not ent_status[7]:
+ print("Got: %d" % ent_status[7])
+
+#----------------------------------------------------------------
+# main
+#----------------------------------------------------------------
+def main():
+ if VERBOSE:
+ print("Starting trng data extraction.")
+
+ my_commdev = Comm()
+
+ # looptest(my_commdev)
+
+# get_avalanche_entropy()
+# get_avalanche_delta()
+
+ get_rosc_entropy(my_commdev)
+# get_rosc_raw()
+
+# get_rng_data()
+
+
+#-------------------------------------------------------------------
+# __name__
+# Python thingy which allows the file to be run standalone as
+# well as parsed from within a Python interpreter.
+#-------------------------------------------------------------------
+if __name__=="__main__":
+ # Run the main function.
+ sys.exit(main())
+
+#=======================================================================
+# EOF trng_extract.py
+#=======================================================================