Added LICENSE.md, updated README.md, fixed test tool, and organized driver code.

This commit is contained in:
Mickey Malone
2021-02-14 12:29:00 -06:00
parent 142a0fcdac
commit 94699bfa10
5 changed files with 228 additions and 146 deletions

29
LICENSE
View File

@@ -1,29 +0,0 @@
BSD 3-Clause License
Copyright (c) 2021, Mickey Malone
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.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
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 HOLDER 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.

11
LICENSE.md Normal file
View File

@@ -0,0 +1,11 @@
Copyright 2020 Mickey Malone
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.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
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 HOLDER 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.

View File

@@ -1 +1,66 @@
Raspberry Pi Pico Random Number Generator
# Raspberry Pi Pico Random Number Generator
A basic random number generator that generates numbers from the onboard DAC of the Raspberry Pi Pico. The project used the Raspberry Pi Pico USB dev_lowlevel as a starting point. The RNG is not meant to be FIPS 140-2 compliant by a long shot. This is not meant to by used in a production system as a TRNG. Maybe one day the next gen Pico's will include an onboard crypto module.
## Project Goals
* Raspberry Pi Pico firmware generates random numbers as a USB Endpoint
* Linux Kernel Module (aka driver) provides random numbers to the Kernel
* Driver can transmit random numbers on demand to the system and/or user processes via a character device
### Prerequisites
* Raspberry Pi Pico development environment. See [Raspberry Pi Pico Getting Started Documentation](https://www.raspberrypi.org/documentation/pico/getting-started/)
* Linux Kernel development headers
### Building
The entire project uses CMake to keep with Rasberry Pi Pico's development environment and project setup instructions.
```bash
# Create build directory
mkdir build
# Change to the build directory
cd build
# Run cmake
cmake ..
# Run make
make
```
### Install
```bash
# Assumes CWD is 'build/'
# debug will enable debug log level
# timeout will set the usb endpoint timeout. Currently defaults to 100 msecs
sudo insmod driver/pico_rng.ko [debug=1] [timeout=<msec timeout>]
```
### Testing
You can test Pico RNG firmware with the [pico_rng_test.py](firmware/pico_rng_test.py) script.
```bash
# Running with --performance will measure the devices' KB/s.
# if the kernel module is inserted, then the test tool will use /dev/pico_rng otherwise python's libusb implementation will be used.
sudo firmware/pico_rng_test.py [--performance]
```
# Remove
```bash
sudo rmmod pico_rng
```
### License
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
### References
* https://github.com/raspberrypi/pico-examples/tree/master/usb/device/dev_lowlevel

View File

@@ -1,75 +1,91 @@
/**
* Copyright (c) 2020 Mickey Malone.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <asm/uaccess.h>
#include <linux/hw_random.h>
#include <linux/kthread.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mickey Malone");
MODULE_DESCRIPTION("Random number generator using a Raspberry Pi Pico");
MODULE_VERSION("1.0");
/**
* Macros
* USB Device Macros
**/
#define VENDOR_ID 0x0
#define PRODUCT_ID 0x4
/**
* Logger macros
* Logger Macros
**/
#define LOGGER_INFO(fmt, args ...) printk( KERN_INFO "[info] %s(%d): " fmt, __FUNCTION__, __LINE__, ## args)
#define LOGGER_ERR(fmt, args ...) printk( KERN_ERR "[err] %s(%d): " fmt, __FUNCTION__, __LINE__, ## args)
#define LOGGER_WARN(fmt, args ...) printk( KERN_ERR "[warn] %s(%d): " fmt, __FUNCTION__, __LINE__, ## args)
#define LOGGER_DEBUG(fmt, args ...) if (debug == 1) { printk( KERN_DEBUG "[debug] %s(%d): " fmt, __FUNCTION__, __LINE__, ## args); }
/**
* Enable module LOGGER_DEBUG macro. Defaults to 0 or false.
**/
static int debug = 0;
module_param(debug, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(debug, "Set the log level to debug");
static int timeout = 500;
/**
* Lever that will set the usb timeout in milliseconds. Defaults to 500.
**/
static int timeout = 100;
module_param(timeout, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(timeout, "Set the read timeout for the pico rng usb device");
MODULE_PARM_DESC(timeout, "Set the read timeout in milliseconds for the pico rng usb device. Defaults to 100.");
/**
* The main data structure for this module
* The main data structure for this module.
**/
struct pico_rng_data {
struct usb_device *dev;
struct usb_interface *interface;
struct usb_endpoint_descriptor *endpoint;
void *buffer;
int pipe;
struct task_struct *rng_task;
} module_data;
/**
* Prototype Functions
* Prototype USB Functions
**/
static int pico_rng_usb_probe(struct usb_interface *interface, const struct usb_device_id *id);
static void pico_rng_usb_disconnect(struct usb_interface *interface);
static int __init pico_rng_driver_init(void);
static void __exit pico_rng_driver_exit(void);
static int pico_rng_read_data(void);
static ssize_t pico_rng_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset);
/**
* Prototype File Operation Functions
**/
static int pico_rng_open(struct inode *inode, struct file *file);
static ssize_t pico_rng_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset);
/**
* Prototype RNG Kthread Functions
**/
static int pico_rng_kthread(void *data);
void pico_rng_kthread_start(void);
void pico_rng_kthread_stop(void);
/**
* Prototype module Functions
**/
static int pico_rng_read_data(void *buffer, int count);
static int __init pico_rng_driver_init(void);
static void __exit pico_rng_driver_exit(void);
module_init(pico_rng_driver_init);
module_exit(pico_rng_driver_exit);
/**
* Data structure of the USB vid:pid device that we will support
**/
@@ -102,71 +118,10 @@ static struct file_operations pico_rng_fops = {
* USB class data structure
**/
struct usb_class_driver pico_rng_usb_class = {
.name = "pico_raw",
.name = "pico_rng",
.fops = &pico_rng_fops,
};
/**
* File ops:open
**/
static int pico_rng_open(struct inode *inode, struct file *file)
{
LOGGER_DEBUG("inside pico_rng_open with inode %p file %p\n", inode, file);
return 0;
}
/**
* Read data from the pico rng
*/
static int pico_rng_read_data()
{
int retval = 0;
int actual_length = 0;
// int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
LOGGER_DEBUG("Calling usb_bulk_msg dev %p, pipe %u, buffer %p, size %d, and timeout %d", \
module_data.dev, module_data.pipe, module_data.buffer, module_data.endpoint->wMaxPacketSize, timeout);
retval = usb_bulk_msg(module_data.dev,
module_data.pipe,
module_data.buffer,
module_data.endpoint->wMaxPacketSize,
&actual_length,
timeout);
if(retval)
{
return -EFAULT;
}
return actual_length;
}
/**
* File ops:read
**/
static ssize_t pico_rng_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset)
{
int bytes_read = 0;
LOGGER_DEBUG("inside pico_rng_read with file %p, user_buffer %p, size %ld, offset %lld\n", file, user_buffer, size, *offset);
bytes_read = pico_rng_read_data();
if(!bytes_read)
{
LOGGER_ERR("Failed to read data");
return -EFAULT;
}
LOGGER_DEBUG("Copying %d bytest of random data to userspace with offset %lld\n", bytes_read, *offset);
if(copy_to_user(user_buffer, module_data.buffer, bytes_read))
{
return -EFAULT;
}
return bytes_read;
}
/**
* USB: Probe
* This method will be called if the device we plug in matches the vid:pid we are listening for
@@ -200,14 +155,6 @@ static int pico_rng_usb_probe(struct usb_interface *interface, const struct usb_
LOGGER_DEBUG("endpoint found %p with pipe %d", module_data.endpoint, module_data.pipe);
module_data.buffer = NULL;
module_data.buffer = kmalloc(module_data.endpoint->wMaxPacketSize, GFP_KERNEL);
if(!module_data.buffer)
{
LOGGER_ERR("failed to allocate buffer");
return -1;
}
retval = usb_register_dev(module_data.interface, &pico_rng_usb_class);
if(retval)
{
@@ -232,20 +179,73 @@ static void pico_rng_usb_disconnect(struct usb_interface *interface)
module_data.dev = NULL;
module_data.interface = NULL;
module_data.pipe = 0;
kfree(module_data.buffer);
module_data.buffer = NULL;
}
/**
* File:open
* Does nothing, just here as a place holder
**/
static int pico_rng_open(struct inode *inode, struct file *file)
{
LOGGER_DEBUG("inside pico_rng_open with inode %p file %p\n", inode, file);
return 0;
}
/**
* File:read
* Calls pico_rng_read_data() and returns module_data.endpoint->wMaxPacketSize bytes of data back to the user
**/
static ssize_t pico_rng_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset)
{
int bytes_read = 0;
void *buffer = NULL;
LOGGER_DEBUG("inside pico_rng_read with file %p, user_buffer %p, size %ld, offset %lld\n", file, user_buffer, size, *offset);
buffer = kmalloc(module_data.endpoint->wMaxPacketSize, GFP_USER);
if(!buffer)
{
LOGGER_ERR("Failed to allocate buffer");
return -EFAULT;
}
bytes_read = pico_rng_read_data(buffer, module_data.endpoint->wMaxPacketSize);
if(!bytes_read)
{
LOGGER_ERR("Failed to read data");
return -EFAULT;
}
LOGGER_DEBUG("Copying %d bytest of random data to userspace with offset %lld\n", bytes_read, *offset);
if(copy_to_user(user_buffer, buffer, bytes_read))
{
return -EFAULT;
}
kfree(buffer);
return bytes_read;
}
/*
* Pico rng thread that periodically adds hardware randomness
*/
static int pico_rng_kthread(void *data)
{
int bytes_read;
void *buffer = NULL;
buffer = kmalloc(module_data.endpoint->wMaxPacketSize, GFP_NOWAIT);
if(!buffer)
{
LOGGER_ERR("RNG kthread failed to allocate buffer");
return -EFAULT;
}
while (!kthread_should_stop())
{
bytes_read = pico_rng_read_data();
bytes_read = pico_rng_read_data(buffer, module_data.endpoint->wMaxPacketSize);
if(!bytes_read)
{
LOGGER_ERR("Failed to read data\n");
@@ -259,7 +259,7 @@ static int pico_rng_kthread(void *data)
LOGGER_DEBUG("Adding hardware randomness\n");
// I would not exactly call this rng as trusted, so it will not add entropy, only random bits to the pool
// A trusted device would call add_hwgenerator_randomness and credit the entropy pool. For now the credit is 0 while still adding random bits.
add_hwgenerator_randomness(module_data.buffer, bytes_read, 0);
add_hwgenerator_randomness(buffer, bytes_read, 0);
LOGGER_DEBUG("Randomness added\n");
}
@@ -291,6 +291,38 @@ void pico_rng_kthread_stop()
}
}
/**
* Read data from the pico rng.
* Fills the buffer and returns the number of bytes filled.
* Count is the maximum number of bytes the buffer can hold.
*/
static int pico_rng_read_data(void *buffer, int count)
{
int retval = 0;
int actual_length = 0;
// int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
LOGGER_DEBUG("Calling usb_bulk_msg dev %p, pipe %u, buffer %p, size %d, and timeout %d", \
module_data.dev, module_data.pipe, buffer, count, timeout);
retval = usb_bulk_msg(module_data.dev,
module_data.pipe,
buffer,
count,
&actual_length,
timeout);
if(retval)
{
return -EFAULT;
}
return actual_length;
}
/**
* Module:init
**/
static int __init pico_rng_driver_init(void)
{
int retval = 0;
@@ -310,18 +342,11 @@ static int __init pico_rng_driver_init(void)
return 0;
}
/**
* Module:exit
**/
static void __exit pico_rng_driver_exit(void)
{
usb_deregister(&pico_rng_usb_driver);
if(module_data.buffer)
{
kfree(module_data.buffer);
}
return;
}
module_init(pico_rng_driver_init);
module_exit(pico_rng_driver_exit);
}

View File

@@ -2,6 +2,7 @@
import usb.core
import usb.util
import os
import random
import time
import argparse
@@ -11,18 +12,26 @@ parser = argparse.ArgumentParser(description="Raspberry Pi Pico Random Number Ge
parser.add_argument("--performance", action="store_true", help="Performance test the RNG.")
args = parser.parse_args()
# Get the device
rng = usb.core.find(idVendor=0x0000, idProduct=0x0004)
assert rng is not None
# If this is set, then the /dev/pico_rng file exists
rng_chardev = None
# Get the configuration of the device
cfg = rng.get_active_configuration()
if os.path.exists("/dev/pico_rng"):
rng_chardev = open("/dev/pico_rng", "rb")
# File does not exist, test with usb.core
if not rng_chardev:
# Get the device
rng = usb.core.find(idVendor=0x0000, idProduct=0x0004)
assert rng is not None
# Get the only interface of our device
intf = cfg.interfaces()[0]
# Get the configuration of the device
cfg = rng.get_active_configuration()
# Get the endpoint
endpt = intf.endpoints()[0]
# Get the only interface of our device
intf = cfg.interfaces()[0]
# Get the endpoint
endpt = intf.endpoints()[0]
# Time tracking for bits/s
count = 0
@@ -31,11 +40,12 @@ start_time = (int(time.time()) - 1)
if args.performance:
while True:
try:
from_device = endpt.read(endpt.wMaxPacketSize, 500)
from_device = rng_chardev.read(64) if rng_chardev else endpt.read(64, 500)
count = count+1
print(":".join("{:02x}".format(b) for b in from_device), end="")
print(" KBps {0:.2f}".format((int((count * 64) / (int(time.time()) - start_time))) / 1024 ))
print(from_device, end="")
print("\t{0:.2f} KB/s".format((int((count * 64) / (int(time.time()) - start_time))) / 1024 ))
except KeyboardInterrupt:
exit(0)
else:
print(":".join("{:02x}".format(b) for b in endpt.read(endpt.wMaxPacketSize, 500)))
from_device = rng_chardev.read(64) if rng_chardev else endpt.read(64, 500)
print(from_device)