Skip to content

Commit 94699bf

Browse files
committedFeb 14, 2021
Added LICENSE.md, updated README.md, fixed test tool, and organized driver code.
1 parent 142a0fc commit 94699bf

File tree

5 files changed

+228
-146
lines changed

5 files changed

+228
-146
lines changed
 

‎LICENSE

-29
This file was deleted.

‎LICENSE.md

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Copyright 2020 Mickey Malone
2+
3+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4+
5+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6+
7+
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.
8+
9+
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.
10+
11+
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.

‎README.md

+66-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,66 @@
1-
Raspberry Pi Pico Random Number Generator
1+
# Raspberry Pi Pico Random Number Generator
2+
3+
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.
4+
5+
6+
## Project Goals
7+
* Raspberry Pi Pico firmware generates random numbers as a USB Endpoint
8+
* Linux Kernel Module (aka driver) provides random numbers to the Kernel
9+
* Driver can transmit random numbers on demand to the system and/or user processes via a character device
10+
11+
12+
### Prerequisites
13+
14+
* Raspberry Pi Pico development environment. See [Raspberry Pi Pico Getting Started Documentation](https://www.raspberrypi.org/documentation/pico/getting-started/)
15+
* Linux Kernel development headers
16+
17+
18+
### Building
19+
The entire project uses CMake to keep with Rasberry Pi Pico's development environment and project setup instructions.
20+
21+
```bash
22+
# Create build directory
23+
mkdir build
24+
25+
# Change to the build directory
26+
cd build
27+
28+
# Run cmake
29+
cmake ..
30+
31+
# Run make
32+
make
33+
```
34+
35+
### Install
36+
37+
```bash
38+
# Assumes CWD is 'build/'
39+
# debug will enable debug log level
40+
# timeout will set the usb endpoint timeout. Currently defaults to 100 msecs
41+
sudo insmod driver/pico_rng.ko [debug=1] [timeout=<msec timeout>]
42+
```
43+
44+
### Testing
45+
46+
You can test Pico RNG firmware with the [pico_rng_test.py](firmware/pico_rng_test.py) script.
47+
48+
```bash
49+
# Running with --performance will measure the devices' KB/s.
50+
# if the kernel module is inserted, then the test tool will use /dev/pico_rng otherwise python's libusb implementation will be used.
51+
sudo firmware/pico_rng_test.py [--performance]
52+
```
53+
54+
55+
# Remove
56+
```bash
57+
sudo rmmod pico_rng
58+
```
59+
60+
### License
61+
62+
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
63+
64+
### References
65+
66+
* https://github.com/raspberrypi/pico-examples/tree/master/usb/device/dev_lowlevel

‎driver/pico_rng.c

+128-103
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,91 @@
1+
/**
2+
* Copyright (c) 2020 Mickey Malone.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
17
#include <linux/module.h>
28
#include <linux/kernel.h>
39
#include <linux/init.h>
410
#include <linux/module.h>
5-
#include <linux/init.h>
611
#include <linux/usb.h>
7-
#include <linux/ioctl.h>
812
#include <linux/slab.h>
9-
#include <linux/fs.h>
1013
#include <linux/kernel.h>
11-
#include <linux/semaphore.h>
12-
#include <asm/uaccess.h>
1314
#include <linux/hw_random.h>
1415
#include <linux/kthread.h>
1516

16-
1717
MODULE_LICENSE("GPL");
1818
MODULE_AUTHOR("Mickey Malone");
1919
MODULE_DESCRIPTION("Random number generator using a Raspberry Pi Pico");
2020
MODULE_VERSION("1.0");
2121

2222
/**
23-
* Macros
23+
* USB Device Macros
2424
**/
2525
#define VENDOR_ID 0x0
2626
#define PRODUCT_ID 0x4
2727

2828
/**
29-
* Logger macros
29+
* Logger Macros
3030
**/
3131
#define LOGGER_INFO(fmt, args ...) printk( KERN_INFO "[info] %s(%d): " fmt, __FUNCTION__, __LINE__, ## args)
3232
#define LOGGER_ERR(fmt, args ...) printk( KERN_ERR "[err] %s(%d): " fmt, __FUNCTION__, __LINE__, ## args)
3333
#define LOGGER_WARN(fmt, args ...) printk( KERN_ERR "[warn] %s(%d): " fmt, __FUNCTION__, __LINE__, ## args)
3434
#define LOGGER_DEBUG(fmt, args ...) if (debug == 1) { printk( KERN_DEBUG "[debug] %s(%d): " fmt, __FUNCTION__, __LINE__, ## args); }
3535

36+
/**
37+
* Enable module LOGGER_DEBUG macro. Defaults to 0 or false.
38+
**/
3639
static int debug = 0;
3740
module_param(debug, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
3841
MODULE_PARM_DESC(debug, "Set the log level to debug");
3942

40-
static int timeout = 500;
43+
/**
44+
* Lever that will set the usb timeout in milliseconds. Defaults to 500.
45+
**/
46+
static int timeout = 100;
4147
module_param(timeout, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
42-
MODULE_PARM_DESC(timeout, "Set the read timeout for the pico rng usb device");
48+
MODULE_PARM_DESC(timeout, "Set the read timeout in milliseconds for the pico rng usb device. Defaults to 100.");
4349

4450
/**
45-
* The main data structure for this module
51+
* The main data structure for this module.
4652
**/
4753
struct pico_rng_data {
4854
struct usb_device *dev;
4955
struct usb_interface *interface;
5056
struct usb_endpoint_descriptor *endpoint;
51-
void *buffer;
5257
int pipe;
5358
struct task_struct *rng_task;
5459
} module_data;
5560

5661
/**
57-
* Prototype Functions
62+
* Prototype USB Functions
5863
**/
5964
static int pico_rng_usb_probe(struct usb_interface *interface, const struct usb_device_id *id);
6065
static void pico_rng_usb_disconnect(struct usb_interface *interface);
61-
static int __init pico_rng_driver_init(void);
62-
static void __exit pico_rng_driver_exit(void);
63-
64-
static int pico_rng_read_data(void);
6566

66-
static ssize_t pico_rng_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset);
67+
/**
68+
* Prototype File Operation Functions
69+
**/
6770
static int pico_rng_open(struct inode *inode, struct file *file);
71+
static ssize_t pico_rng_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset);
6872

73+
/**
74+
* Prototype RNG Kthread Functions
75+
**/
6976
static int pico_rng_kthread(void *data);
7077
void pico_rng_kthread_start(void);
7178
void pico_rng_kthread_stop(void);
7279

80+
/**
81+
* Prototype module Functions
82+
**/
83+
static int pico_rng_read_data(void *buffer, int count);
84+
static int __init pico_rng_driver_init(void);
85+
static void __exit pico_rng_driver_exit(void);
86+
module_init(pico_rng_driver_init);
87+
module_exit(pico_rng_driver_exit);
88+
7389
/**
7490
* Data structure of the USB vid:pid device that we will support
7591
**/
@@ -102,71 +118,10 @@ static struct file_operations pico_rng_fops = {
102118
* USB class data structure
103119
**/
104120
struct usb_class_driver pico_rng_usb_class = {
105-
.name = "pico_raw",
121+
.name = "pico_rng",
106122
.fops = &pico_rng_fops,
107123
};
108124

109-
/**
110-
* File ops:open
111-
**/
112-
static int pico_rng_open(struct inode *inode, struct file *file)
113-
{
114-
LOGGER_DEBUG("inside pico_rng_open with inode %p file %p\n", inode, file);
115-
return 0;
116-
}
117-
118-
/**
119-
* Read data from the pico rng
120-
*/
121-
static int pico_rng_read_data()
122-
{
123-
int retval = 0;
124-
int actual_length = 0;
125-
126-
// int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
127-
LOGGER_DEBUG("Calling usb_bulk_msg dev %p, pipe %u, buffer %p, size %d, and timeout %d", \
128-
module_data.dev, module_data.pipe, module_data.buffer, module_data.endpoint->wMaxPacketSize, timeout);
129-
130-
retval = usb_bulk_msg(module_data.dev,
131-
module_data.pipe,
132-
module_data.buffer,
133-
module_data.endpoint->wMaxPacketSize,
134-
&actual_length,
135-
timeout);
136-
137-
if(retval)
138-
{
139-
return -EFAULT;
140-
}
141-
142-
return actual_length;
143-
}
144-
145-
/**
146-
* File ops:read
147-
**/
148-
static ssize_t pico_rng_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset)
149-
{
150-
int bytes_read = 0;
151-
152-
LOGGER_DEBUG("inside pico_rng_read with file %p, user_buffer %p, size %ld, offset %lld\n", file, user_buffer, size, *offset);
153-
154-
bytes_read = pico_rng_read_data();
155-
if(!bytes_read)
156-
{
157-
LOGGER_ERR("Failed to read data");
158-
return -EFAULT;
159-
}
160-
161-
LOGGER_DEBUG("Copying %d bytest of random data to userspace with offset %lld\n", bytes_read, *offset);
162-
if(copy_to_user(user_buffer, module_data.buffer, bytes_read))
163-
{
164-
return -EFAULT;
165-
}
166-
167-
return bytes_read;
168-
}
169-
170125
/**
171126
* USB: Probe
172127
* 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_
200155

201156
LOGGER_DEBUG("endpoint found %p with pipe %d", module_data.endpoint, module_data.pipe);
202157

203-
module_data.buffer = NULL;
204-
module_data.buffer = kmalloc(module_data.endpoint->wMaxPacketSize, GFP_KERNEL);
205-
if(!module_data.buffer)
206-
{
207-
LOGGER_ERR("failed to allocate buffer");
208-
return -1;
209-
}
210-
211158
retval = usb_register_dev(module_data.interface, &pico_rng_usb_class);
212159
if(retval)
213160
{
@@ -232,20 +179,73 @@ static void pico_rng_usb_disconnect(struct usb_interface *interface)
232179
module_data.dev = NULL;
233180
module_data.interface = NULL;
234181
module_data.pipe = 0;
235-
kfree(module_data.buffer);
236-
module_data.buffer = NULL;
237182
}
238183

184+
185+
/**
186+
* File:open
187+
* Does nothing, just here as a place holder
188+
**/
189+
static int pico_rng_open(struct inode *inode, struct file *file)
190+
{
191+
LOGGER_DEBUG("inside pico_rng_open with inode %p file %p\n", inode, file);
192+
return 0;
193+
}
194+
195+
/**
196+
* File:read
197+
* Calls pico_rng_read_data() and returns module_data.endpoint->wMaxPacketSize bytes of data back to the user
198+
**/
199+
static ssize_t pico_rng_read(struct file *file, char __user *user_buffer, size_t size, loff_t *offset)
200+
{
201+
int bytes_read = 0;
202+
void *buffer = NULL;
203+
204+
LOGGER_DEBUG("inside pico_rng_read with file %p, user_buffer %p, size %ld, offset %lld\n", file, user_buffer, size, *offset);
205+
206+
buffer = kmalloc(module_data.endpoint->wMaxPacketSize, GFP_USER);
207+
if(!buffer)
208+
{
209+
LOGGER_ERR("Failed to allocate buffer");
210+
return -EFAULT;
211+
}
212+
213+
bytes_read = pico_rng_read_data(buffer, module_data.endpoint->wMaxPacketSize);
214+
if(!bytes_read)
215+
{
216+
LOGGER_ERR("Failed to read data");
217+
return -EFAULT;
218+
}
219+
220+
LOGGER_DEBUG("Copying %d bytest of random data to userspace with offset %lld\n", bytes_read, *offset);
221+
if(copy_to_user(user_buffer, buffer, bytes_read))
222+
{
223+
return -EFAULT;
224+
}
225+
226+
kfree(buffer);
227+
return bytes_read;
228+
}
229+
230+
239231
/*
240232
* Pico rng thread that periodically adds hardware randomness
241233
*/
242234
static int pico_rng_kthread(void *data)
243235
{
244236
int bytes_read;
237+
void *buffer = NULL;
238+
239+
buffer = kmalloc(module_data.endpoint->wMaxPacketSize, GFP_NOWAIT);
240+
if(!buffer)
241+
{
242+
LOGGER_ERR("RNG kthread failed to allocate buffer");
243+
return -EFAULT;
244+
}
245245

246246
while (!kthread_should_stop())
247247
{
248-
bytes_read = pico_rng_read_data();
248+
bytes_read = pico_rng_read_data(buffer, module_data.endpoint->wMaxPacketSize);
249249
if(!bytes_read)
250250
{
251251
LOGGER_ERR("Failed to read data\n");
@@ -259,7 +259,7 @@ static int pico_rng_kthread(void *data)
259259
LOGGER_DEBUG("Adding hardware randomness\n");
260260
// I would not exactly call this rng as trusted, so it will not add entropy, only random bits to the pool
261261
// A trusted device would call add_hwgenerator_randomness and credit the entropy pool. For now the credit is 0 while still adding random bits.
262-
add_hwgenerator_randomness(module_data.buffer, bytes_read, 0);
262+
add_hwgenerator_randomness(buffer, bytes_read, 0);
263263
LOGGER_DEBUG("Randomness added\n");
264264
}
265265

@@ -291,6 +291,38 @@ void pico_rng_kthread_stop()
291291
}
292292
}
293293

294+
/**
295+
* Read data from the pico rng.
296+
* Fills the buffer and returns the number of bytes filled.
297+
* Count is the maximum number of bytes the buffer can hold.
298+
*/
299+
static int pico_rng_read_data(void *buffer, int count)
300+
{
301+
int retval = 0;
302+
int actual_length = 0;
303+
304+
// int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout)
305+
LOGGER_DEBUG("Calling usb_bulk_msg dev %p, pipe %u, buffer %p, size %d, and timeout %d", \
306+
module_data.dev, module_data.pipe, buffer, count, timeout);
307+
308+
retval = usb_bulk_msg(module_data.dev,
309+
module_data.pipe,
310+
buffer,
311+
count,
312+
&actual_length,
313+
timeout);
314+
315+
if(retval)
316+
{
317+
return -EFAULT;
318+
}
319+
320+
return actual_length;
321+
}
322+
323+
/**
324+
* Module:init
325+
**/
294326
static int __init pico_rng_driver_init(void)
295327
{
296328
int retval = 0;
@@ -310,18 +342,11 @@ static int __init pico_rng_driver_init(void)
310342
return 0;
311343
}
312344

345+
/**
346+
* Module:exit
347+
**/
313348
static void __exit pico_rng_driver_exit(void)
314349
{
315350
usb_deregister(&pico_rng_usb_driver);
316-
317-
if(module_data.buffer)
318-
{
319-
kfree(module_data.buffer);
320-
}
321-
322351
return;
323-
}
324-
325-
module_init(pico_rng_driver_init);
326-
module_exit(pico_rng_driver_exit);
327-
352+
}

‎firmware/pico_rng_test.py

+23-13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import usb.core
44
import usb.util
5+
import os
56
import random
67
import time
78
import argparse
@@ -11,18 +12,26 @@
1112
parser.add_argument("--performance", action="store_true", help="Performance test the RNG.")
1213
args = parser.parse_args()
1314

14-
# Get the device
15-
rng = usb.core.find(idVendor=0x0000, idProduct=0x0004)
16-
assert rng is not None
15+
# If this is set, then the /dev/pico_rng file exists
16+
rng_chardev = None
1717

18-
# Get the configuration of the device
19-
cfg = rng.get_active_configuration()
18+
if os.path.exists("/dev/pico_rng"):
19+
rng_chardev = open("/dev/pico_rng", "rb")
20+
21+
# File does not exist, test with usb.core
22+
if not rng_chardev:
23+
# Get the device
24+
rng = usb.core.find(idVendor=0x0000, idProduct=0x0004)
25+
assert rng is not None
2026

21-
# Get the only interface of our device
22-
intf = cfg.interfaces()[0]
27+
# Get the configuration of the device
28+
cfg = rng.get_active_configuration()
2329

24-
# Get the endpoint
25-
endpt = intf.endpoints()[0]
30+
# Get the only interface of our device
31+
intf = cfg.interfaces()[0]
32+
33+
# Get the endpoint
34+
endpt = intf.endpoints()[0]
2635

2736
# Time tracking for bits/s
2837
count = 0
@@ -31,11 +40,12 @@
3140
if args.performance:
3241
while True:
3342
try:
34-
from_device = endpt.read(endpt.wMaxPacketSize, 500)
43+
from_device = rng_chardev.read(64) if rng_chardev else endpt.read(64, 500)
3544
count = count+1
36-
print(":".join("{:02x}".format(b) for b in from_device), end="")
37-
print(" KBps {0:.2f}".format((int((count * 64) / (int(time.time()) - start_time))) / 1024 ))
45+
print(from_device, end="")
46+
print("\t{0:.2f} KB/s".format((int((count * 64) / (int(time.time()) - start_time))) / 1024 ))
3847
except KeyboardInterrupt:
3948
exit(0)
4049
else:
41-
print(":".join("{:02x}".format(b) for b in endpt.read(endpt.wMaxPacketSize, 500)))
50+
from_device = rng_chardev.read(64) if rng_chardev else endpt.read(64, 500)
51+
print(from_device)

0 commit comments

Comments
 (0)
Please sign in to comment.