First
This commit is contained in:
commit
52ed7af980
|
@ -0,0 +1,19 @@
|
|||
# Makefile for TrueRNG C implementation
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -std=c99 -O2
|
||||
TARGET = truerng
|
||||
SOURCE = main.c
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(SOURCE)
|
||||
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCE)
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
|
||||
install: $(TARGET)
|
||||
sudo cp $(TARGET) /usr/local/bin/
|
||||
|
||||
.PHONY: all clean install
|
|
@ -0,0 +1,64 @@
|
|||
# Performance Comparison: C vs Python TrueRNG Implementation
|
||||
|
||||
## Test Results
|
||||
|
||||
### Final Corrected Results (After Fixing C Implementation)
|
||||
|
||||
### C Version Performance
|
||||
- **Execution time:** 2.27 seconds
|
||||
- **Data read:** 1,048,576 bytes (1 MB - full dataset)
|
||||
- **Read rate:** 462.60 KB/s
|
||||
- **Total bits processed:** 8,388,608
|
||||
|
||||
### Python Version Performance
|
||||
- **Execution time:** 2.27 seconds
|
||||
- **Data read:** 1,048,576 bytes (1 MB - full dataset)
|
||||
- **Read rate:** 462.47 KB/s
|
||||
- **Total bits processed:** 8,388,608
|
||||
|
||||
## Performance Analysis
|
||||
|
||||
### Speed Comparison
|
||||
- **C version completed in:** 2.27 seconds
|
||||
- **Python version completed in:** 2.27 seconds
|
||||
- **Speed difference:** **Effectively identical** for this I/O-bound task
|
||||
|
||||
### Key Discoveries
|
||||
|
||||
1. **I/O Bottleneck**: Both implementations perform identically because the task is **I/O-bound**, not CPU-bound. The serial communication at ~462 KB/s is the limiting factor.
|
||||
|
||||
2. **Initial Bug**: The C version initially appeared faster (0.29 seconds) because it was **incorrectly stopping early** due to improper handling of partial reads from the serial device.
|
||||
|
||||
3. **Proper Serial Handling**: The fix required implementing a read loop that continues until the full block size is received, matching Python's pyserial behavior.
|
||||
|
||||
4. **Identical Functionality**: After correction, both versions process exactly the same amount of data with identical performance characteristics.
|
||||
|
||||
### Technical Issues Resolved
|
||||
|
||||
1. **Partial Read Handling**: C's `read()` system call can return fewer bytes than requested, requiring a loop to accumulate the full block size.
|
||||
|
||||
2. **Serial Port Configuration**: Proper termios settings with `VMIN=1, VTIME=0` for blocking reads.
|
||||
|
||||
3. **Error Handling**: Distinguishing between partial reads (continue) vs. actual errors (abort).
|
||||
|
||||
### Performance Implications
|
||||
|
||||
**For I/O-bound tasks like serial communication:**
|
||||
- Language choice has **minimal impact** on performance
|
||||
- Hardware communication speed dominates execution time
|
||||
- Proper protocol handling is more important than language efficiency
|
||||
|
||||
**For CPU-intensive tasks:**
|
||||
- C would likely show significant performance advantages
|
||||
- Bit manipulation, mathematical operations, and memory management favor compiled languages
|
||||
|
||||
### Lessons Learned
|
||||
|
||||
1. **Measure Correctly**: Initial performance differences were due to bugs, not language efficiency
|
||||
2. **Understand Bottlenecks**: Serial I/O at 462 KB/s dominates any CPU processing overhead
|
||||
3. **Proper Equivalence**: Both implementations must handle the same data volume for fair comparison
|
||||
4. **System API Differences**: C requires more explicit handling of partial I/O operations
|
||||
|
||||
## Conclusion
|
||||
|
||||
For this **I/O-bound TrueRNG application**, both C and Python implementations achieve **identical performance** (2.27 seconds for 1MB). The bottleneck is the serial communication hardware, not the programming language. The C implementation provides equivalent functionality with more explicit control over system resources, while Python offers simpler, more abstract I/O handling through the pyserial library.
|
|
@ -0,0 +1,99 @@
|
|||
# TrueRNG C Implementation
|
||||
|
||||
This is a C equivalent of the Python `main.py` script for reading data from TrueRNG devices.
|
||||
|
||||
## Overview
|
||||
|
||||
The program reads random data from a TrueRNG device via serial port, counts the number of ones and zeros in the data, and saves the raw data to a binary file called `random.bin`.
|
||||
|
||||
## Supported Devices
|
||||
|
||||
- TrueRNG (PID: 04D8, HID: F5FE)
|
||||
- TrueRNGpro (PID: 16D0, HID: 0AA0)
|
||||
- TrueRNGproV2 (PID: 04D8, HID: EBB5)
|
||||
|
||||
## Requirements
|
||||
|
||||
- Linux system with termios support
|
||||
- GCC compiler
|
||||
- TrueRNG device connected via USB
|
||||
- Appropriate permissions to access the serial device (may need root or proper udev rules)
|
||||
|
||||
## Building
|
||||
|
||||
```bash
|
||||
make
|
||||
```
|
||||
|
||||
Or manually:
|
||||
```bash
|
||||
gcc -Wall -Wextra -std=c99 -O2 -o truerng main.c
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
./truerng
|
||||
```
|
||||
|
||||
The program will:
|
||||
1. Automatically detect connected TrueRNG devices
|
||||
2. Read 1024 blocks of 1024 bytes each (1MB total)
|
||||
3. Count ones and zeros in real-time
|
||||
4. Display read rate in KB/s
|
||||
5. Save raw data to `random.bin`
|
||||
6. Display final statistics
|
||||
|
||||
## Configuration
|
||||
|
||||
You can modify the following constants in [`main.c`](main.c:32):
|
||||
|
||||
- `BLOCKSIZE`: Number of bytes to read per block (default: 1024)
|
||||
- `NUMLOOPS`: Number of blocks to read (default: 1024)
|
||||
|
||||
## Permissions
|
||||
|
||||
On Linux, you may need to set proper permissions for the serial device:
|
||||
|
||||
```bash
|
||||
sudo chmod 666 /dev/ttyUSB0 # Replace with your device
|
||||
```
|
||||
|
||||
Or add your user to the dialout group:
|
||||
```bash
|
||||
sudo usermod -a -G dialout $USER
|
||||
```
|
||||
|
||||
## Differences from Python Version
|
||||
|
||||
The C implementation:
|
||||
- Uses native Linux serial port APIs instead of pyserial
|
||||
- Implements USB device detection via sysfs
|
||||
- Uses lookup tables for efficient bit counting
|
||||
- Provides equivalent functionality with better performance
|
||||
|
||||
## Output
|
||||
|
||||
Sample output:
|
||||
```
|
||||
TrueRNG Counting Ones vs Zeros Example
|
||||
http://ubld.it
|
||||
==================================================
|
||||
TrueRNG Found
|
||||
Using com port: /dev/ttyUSB0
|
||||
Block Size: 1024 Bytes
|
||||
Number of loops: 1024
|
||||
Total size: 1048576 Bytes
|
||||
Writing to: random.bin
|
||||
==================================================
|
||||
1048576 Bytes Read at 45.23 Kbytes/s
|
||||
|
||||
Results
|
||||
=======
|
||||
Total Ones : 4194234
|
||||
Total Zeros: 4194314
|
||||
|
||||
Total Bits : 8388608
|
||||
|
||||
There are 80 more zeros in the Capture!
|
||||
=======
|
|
@ -0,0 +1,579 @@
|
|||
/*
|
||||
* TrueRNG Read - C Implementation
|
||||
* Equivalent to main.py
|
||||
*
|
||||
* Requires: gcc, Linux system with termios support
|
||||
* Compile with: gcc -o truerng main.c
|
||||
*
|
||||
* On Linux - may need to be root or set /dev/tty port permissions to 666
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <getopt.h>
|
||||
|
||||
// Define CRTSCTS if not available
|
||||
#ifndef CRTSCTS
|
||||
#define CRTSCTS 020000000000
|
||||
#endif
|
||||
|
||||
// Constants
|
||||
#define BLOCKSIZE 1024
|
||||
#define NUMLOOPS 1024
|
||||
#define MAX_PATH 512
|
||||
#define MAX_PORTS 20
|
||||
|
||||
// TrueRNG Device IDs
|
||||
#define TRUERNG_PID "04D8"
|
||||
#define TRUERNG_HID "F5FE"
|
||||
#define TRUERNGPRO_PID "16D0"
|
||||
#define TRUERNGPRO_HID "0AA0"
|
||||
#define TRUERNGPROV2_PID "04D8"
|
||||
#define TRUERNGPROV2_HID "EBB5"
|
||||
|
||||
// Output formats
|
||||
enum Format { BINARY, HEX, BASE64, DECIMAL };
|
||||
|
||||
// Command-line options structure
|
||||
struct Options {
|
||||
long long bytes;
|
||||
enum Format format;
|
||||
char *output_file;
|
||||
int quiet;
|
||||
int verbose;
|
||||
int help;
|
||||
};
|
||||
|
||||
// Global options
|
||||
struct Options options;
|
||||
|
||||
// Global variables
|
||||
static int ones_in_byte[256];
|
||||
|
||||
// Output format functions
|
||||
void output_binary(unsigned char *buffer, int bytes_read, FILE *out) {
|
||||
fwrite(buffer, 1, bytes_read, out);
|
||||
}
|
||||
|
||||
void output_hex(unsigned char *buffer, int bytes_read, FILE *out, int piped) {
|
||||
for (int i = 0; i < bytes_read; i++) {
|
||||
fprintf(out, "%02x", buffer[i]);
|
||||
}
|
||||
// Always add newline for text formats (standard Linux practice)
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
|
||||
void output_decimal(unsigned char *buffer, int bytes_read, FILE *out, int piped) {
|
||||
for (int i = 0; i < bytes_read; i++) {
|
||||
fprintf(out, "%d", buffer[i]);
|
||||
}
|
||||
// Always add newline for text formats (standard Linux practice)
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
|
||||
void output_base64(unsigned char *buffer, int bytes_read, FILE *out, int piped) {
|
||||
static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
char encoded[5];
|
||||
for (int i = 0; i < bytes_read; i += 3) {
|
||||
encoded[0] = b64[buffer[i] >> 2];
|
||||
|
||||
if (i + 1 < bytes_read) {
|
||||
encoded[1] = b64[((buffer[i] & 0x03) << 4) | (buffer[i+1] >> 4)];
|
||||
} else {
|
||||
encoded[1] = b64[(buffer[i] & 0x03) << 4];
|
||||
}
|
||||
|
||||
if (i + 2 < bytes_read) {
|
||||
encoded[2] = b64[((buffer[i+1] & 0x0f) << 2) | (buffer[i+2] >> 6)];
|
||||
encoded[3] = b64[buffer[i+2] & 0x3f];
|
||||
} else if (i + 1 < bytes_read) {
|
||||
encoded[2] = b64[(buffer[i+1] & 0x0f) << 2];
|
||||
encoded[3] = '=';
|
||||
} else {
|
||||
encoded[2] = '=';
|
||||
encoded[3] = '=';
|
||||
}
|
||||
|
||||
fwrite(encoded, 1, 4, out);
|
||||
}
|
||||
// Always add newline for text formats (standard Linux practice)
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
|
||||
void print_usage(void) {
|
||||
printf("TrueRNG - True Random Number Generator\n");
|
||||
printf("Usage: truerng [OPTIONS]\n\n");
|
||||
printf("Options:\n");
|
||||
printf(" -n, --bytes <N> Number of bytes to generate (default: 1048576)\n");
|
||||
printf(" -f, --format <FORMAT> Output format: binary, hex, base64, decimal (default: binary when piped, interactive otherwise)\n");
|
||||
printf(" -o, --output <FILE> Output filename (ignored in piped mode)\n");
|
||||
printf(" -q, --quiet Suppress statistics/progress\n");
|
||||
printf(" -v, --verbose Show detailed device information\n");
|
||||
printf(" -h, --help Show this help message\n");
|
||||
printf("\nExamples:\n");
|
||||
printf(" truerng -n 1024 -f hex # Interactive mode with hex output\n");
|
||||
printf(" truerng -n 1024 | xxd # Piped mode to hex viewer\n");
|
||||
printf(" truerng -o random.dat -q # Save to file quietly\n");
|
||||
}
|
||||
|
||||
int parse_arguments(int argc, char *argv[]) {
|
||||
static const char *opt_string = "n:f:o:qvh";
|
||||
static struct option long_options[] = {
|
||||
{"bytes", required_argument, 0, 'n'},
|
||||
{"format", required_argument, 0, 'f'},
|
||||
{"output", required_argument, 0, 'o'},
|
||||
{"quiet", no_argument, 0, 'q'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
options.bytes = 1048576; // Default 1MB
|
||||
options.format = BINARY;
|
||||
options.output_file = NULL;
|
||||
options.quiet = 0;
|
||||
options.verbose = 0;
|
||||
options.help = 0;
|
||||
|
||||
int opt;
|
||||
int option_index = 0;
|
||||
while ((opt = getopt_long(argc, argv, opt_string, long_options, &option_index)) != -1) {
|
||||
switch (opt) {
|
||||
case 'n':
|
||||
options.bytes = atol(optarg);
|
||||
if (options.bytes <= 0) {
|
||||
fprintf(stderr, "Error: Bytes must be positive\n");
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
if (strcmp(optarg, "binary") == 0) options.format = BINARY;
|
||||
else if (strcmp(optarg, "hex") == 0) options.format = HEX;
|
||||
else if (strcmp(optarg, "base64") == 0) options.format = BASE64;
|
||||
else if (strcmp(optarg, "decimal") == 0) options.format = DECIMAL;
|
||||
else {
|
||||
fprintf(stderr, "Error: Invalid format '%s'\n", optarg);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
options.output_file = optarg;
|
||||
break;
|
||||
case 'q':
|
||||
options.quiet = 1;
|
||||
break;
|
||||
case 'v':
|
||||
options.verbose = 1;
|
||||
break;
|
||||
case 'h':
|
||||
options.help = 1;
|
||||
return 0;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int is_piped(void) {
|
||||
return !isatty(STDOUT_FILENO);
|
||||
}
|
||||
|
||||
// Function prototypes
|
||||
void init_ones_lookup_table(void);
|
||||
int find_truerng_port(char* port_path, int piped, int verbose);
|
||||
int setup_serial_port(const char* port_path);
|
||||
int read_usb_device_info(const char* port_name, char* vid, char* pid);
|
||||
double get_time_diff(struct timeval start, struct timeval end);
|
||||
int parse_arguments(int argc, char *argv[]);
|
||||
void print_usage(void);
|
||||
int is_piped(void);
|
||||
void output_binary(unsigned char *buffer, int bytes_read, FILE *out);
|
||||
void output_hex(unsigned char *buffer, int bytes_read, FILE *out, int piped);
|
||||
void output_decimal(unsigned char *buffer, int bytes_read, FILE *out, int piped);
|
||||
void output_base64(unsigned char *buffer, int bytes_read, FILE *out, int piped);
|
||||
|
||||
// Initialize lookup table for counting ones in a byte
|
||||
void init_ones_lookup_table(void) {
|
||||
int i, count;
|
||||
for (i = 0; i < 256; i++) {
|
||||
count = 0;
|
||||
int temp = i;
|
||||
while (temp) {
|
||||
count += temp & 1;
|
||||
temp >>= 1;
|
||||
}
|
||||
ones_in_byte[i] = count;
|
||||
}
|
||||
}
|
||||
|
||||
// Read USB device info from sysfs
|
||||
int read_usb_device_info(const char* port_name, char* vid, char* pid) {
|
||||
char path[MAX_PATH];
|
||||
FILE *fp;
|
||||
|
||||
// Try to read idVendor first (works for both ttyUSB and ttyACM devices)
|
||||
snprintf(path, sizeof(path), "/sys/class/tty/%s/device/../idVendor", port_name);
|
||||
fp = fopen(path, "r");
|
||||
if (fp) {
|
||||
if (fgets(vid, 8, fp) != NULL) {
|
||||
// Remove newline if present
|
||||
int len = strlen(vid);
|
||||
if (len > 0 && vid[len-1] == '\n') {
|
||||
vid[len-1] = '\0';
|
||||
}
|
||||
} else {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
fclose(fp);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Try to read idProduct
|
||||
snprintf(path, sizeof(path), "/sys/class/tty/%s/device/../idProduct", port_name);
|
||||
fp = fopen(path, "r");
|
||||
if (fp) {
|
||||
if (fgets(pid, 8, fp) != NULL) {
|
||||
// Remove newline if present
|
||||
int len = strlen(pid);
|
||||
if (len > 0 && pid[len-1] == '\n') {
|
||||
pid[len-1] = '\0';
|
||||
}
|
||||
} else {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
fclose(fp);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Find TrueRNG device port
|
||||
// Returns: 0=failure, 1=TrueRNGproV2, 2=TrueRNGpro, 3=TrueRNG
|
||||
int find_truerng_port(char* port_path, int piped, int verbose) {
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
char vid[8], pid[8];
|
||||
int device_found = 0;
|
||||
|
||||
dir = opendir("/dev");
|
||||
if (dir == NULL) {
|
||||
perror("Cannot open /dev directory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
// Look for ttyUSB* or ttyACM* devices
|
||||
if (strncmp(entry->d_name, "ttyUSB", 6) == 0 ||
|
||||
strncmp(entry->d_name, "ttyACM", 6) == 0) {
|
||||
|
||||
if (read_usb_device_info(entry->d_name, vid, pid)) {
|
||||
// Convert to uppercase for comparison
|
||||
for (int i = 0; vid[i]; i++) vid[i] = toupper(vid[i]);
|
||||
for (int i = 0; pid[i]; i++) pid[i] = toupper(pid[i]);
|
||||
|
||||
// Check for TrueRNGproV2
|
||||
if (strcmp(vid, TRUERNGPROV2_PID) == 0 && strcmp(pid, TRUERNGPROV2_HID) == 0) {
|
||||
snprintf(port_path, MAX_PATH, "/dev/%s", entry->d_name);
|
||||
if (!piped || verbose) {
|
||||
printf("TrueRNGproV2 Found\n");
|
||||
}
|
||||
device_found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for TrueRNGpro
|
||||
if (strcmp(vid, TRUERNGPRO_PID) == 0 && strcmp(pid, TRUERNGPRO_HID) == 0) {
|
||||
snprintf(port_path, MAX_PATH, "/dev/%s", entry->d_name);
|
||||
if (!piped || verbose) {
|
||||
printf("TrueRNGpro Found\n");
|
||||
}
|
||||
device_found = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
// Check for TrueRNG
|
||||
if (strcmp(vid, TRUERNG_PID) == 0 && strcmp(pid, TRUERNG_HID) == 0) {
|
||||
snprintf(port_path, MAX_PATH, "/dev/%s", entry->d_name);
|
||||
if (!piped || verbose) {
|
||||
printf("TrueRNG Found\n");
|
||||
}
|
||||
device_found = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return device_found;
|
||||
}
|
||||
|
||||
// Setup serial port
|
||||
int setup_serial_port(const char* port_path) {
|
||||
int fd;
|
||||
struct termios tty;
|
||||
|
||||
fd = open(port_path, O_RDWR | O_NOCTTY);
|
||||
if (fd < 0) {
|
||||
printf("Port Not Usable!\n");
|
||||
printf("Do you have permissions set to read %s ?\n", port_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get current port settings
|
||||
if (tcgetattr(fd, &tty) != 0) {
|
||||
printf("Error getting port attributes\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set baud rate (most TrueRNG devices use default settings)
|
||||
cfsetospeed(&tty, B9600);
|
||||
cfsetispeed(&tty, B9600);
|
||||
|
||||
// 8N1 mode
|
||||
tty.c_cflag &= ~PARENB; // No parity
|
||||
tty.c_cflag &= ~CSTOPB; // 1 stop bit
|
||||
tty.c_cflag &= ~CSIZE; // Clear size bits
|
||||
tty.c_cflag |= CS8; // 8 data bits
|
||||
tty.c_cflag &= ~CRTSCTS; // No hardware flow control
|
||||
tty.c_cflag |= CREAD | CLOCAL; // Enable reading and ignore modem controls
|
||||
|
||||
// Raw input mode
|
||||
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||||
tty.c_oflag &= ~OPOST;
|
||||
|
||||
// Set for blocking reads - wait for data indefinitely
|
||||
tty.c_cc[VMIN] = 1; // Block until at least 1 character is received
|
||||
tty.c_cc[VTIME] = 0; // No timeout
|
||||
|
||||
// Apply settings
|
||||
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
|
||||
printf("Error setting port attributes\n");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Flush input buffer
|
||||
tcflush(fd, TCIFLUSH);
|
||||
|
||||
// Set DTR
|
||||
int status;
|
||||
ioctl(fd, TIOCMGET, &status);
|
||||
status |= TIOCM_DTR;
|
||||
ioctl(fd, TIOCMSET, &status);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Calculate time difference in seconds
|
||||
double get_time_diff(struct timeval start, struct timeval end) {
|
||||
return (end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec) / 1000000.0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// Parse arguments first
|
||||
if (parse_arguments(argc, argv)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Handle help
|
||||
if (options.help) {
|
||||
print_usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Determine mode: interactive (no args) vs piped (has args or actual piping)
|
||||
int interactive_mode = (argc == 1);
|
||||
int piped_mode = !interactive_mode || is_piped();
|
||||
|
||||
// Set default format based on mode
|
||||
if (interactive_mode && options.format == BINARY) {
|
||||
// Interactive mode defaults to hex for readability
|
||||
options.format = HEX;
|
||||
}
|
||||
|
||||
// Print headers based on mode
|
||||
if (interactive_mode && !options.quiet) {
|
||||
// Interactive mode: always show headers unless quiet
|
||||
printf("TrueRNG - True Random Number Generator\n");
|
||||
printf("==================================================\n");
|
||||
} else if (piped_mode && options.verbose && !options.quiet) {
|
||||
// Piped mode: only show headers if verbose
|
||||
printf("TrueRNG - True Random Number Generator\n");
|
||||
printf("Mode: Piped (Verbose)\n");
|
||||
printf("==================================================\n");
|
||||
}
|
||||
|
||||
char port_path[MAX_PATH];
|
||||
int serial_fd;
|
||||
FILE *fp;
|
||||
unsigned char buffer[BLOCKSIZE];
|
||||
long long totalbytes = 0;
|
||||
long long totalones = 0;
|
||||
long long totalzeros = 0;
|
||||
struct timeval starttime, currenttime;
|
||||
double elapsed_time, rate;
|
||||
|
||||
// Initialize lookup table
|
||||
init_ones_lookup_table();
|
||||
|
||||
// Find TrueRNG device
|
||||
if (!find_truerng_port(port_path, piped_mode, options.verbose)) {
|
||||
fprintf(stderr, "TrueRNG Not Found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Setup output
|
||||
if (piped_mode && !options.output_file) {
|
||||
fp = stdout;
|
||||
} else if (options.output_file) {
|
||||
fp = fopen(options.output_file, "w");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "Error Opening File: %s\n", options.output_file);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
fp = stdout;
|
||||
}
|
||||
|
||||
// Setup serial port
|
||||
serial_fd = setup_serial_port(port_path);
|
||||
if (serial_fd < 0) {
|
||||
if (options.output_file && fp != stdout) fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (interactive_mode && !options.quiet) {
|
||||
printf("Using port: %s\n", port_path);
|
||||
printf("Generating: %lld bytes\n", options.bytes);
|
||||
if (options.output_file) {
|
||||
printf("Output file: %s\n", options.output_file);
|
||||
}
|
||||
printf("Starting data collection...\n");
|
||||
} else if (piped_mode && options.verbose && !options.quiet) {
|
||||
printf("Using port: %s\n", port_path);
|
||||
printf("Generating: %lld bytes\n", options.bytes);
|
||||
printf("Starting data collection...\n");
|
||||
}
|
||||
|
||||
// Get start time
|
||||
gettimeofday(&starttime, NULL);
|
||||
|
||||
// Main loop - read until we have enough bytes
|
||||
while (totalbytes < options.bytes) {
|
||||
long long remaining = options.bytes - totalbytes;
|
||||
int blocksize = (remaining < BLOCKSIZE) ? remaining : BLOCKSIZE;
|
||||
|
||||
int bytes_in_buffer = 0;
|
||||
while (bytes_in_buffer < blocksize) {
|
||||
ssize_t bytes_read = read(serial_fd, buffer + bytes_in_buffer, blocksize - bytes_in_buffer);
|
||||
if (bytes_read < 0) {
|
||||
fprintf(stderr, "Read Failed!!! Error: %s\n", strerror(errno));
|
||||
goto cleanup;
|
||||
} else if (bytes_read == 0) {
|
||||
fprintf(stderr, "\nNo data received, device may have stopped\n");
|
||||
goto cleanup;
|
||||
}
|
||||
bytes_in_buffer += bytes_read;
|
||||
}
|
||||
|
||||
// Update total bytes
|
||||
totalbytes += blocksize;
|
||||
|
||||
// Count ones and zeros
|
||||
for (int i = 0; i < blocksize; i++) {
|
||||
int ones = ones_in_byte[buffer[i]];
|
||||
totalones += ones;
|
||||
totalzeros += (8 - ones);
|
||||
}
|
||||
|
||||
// Output data
|
||||
switch (options.format) {
|
||||
case BINARY:
|
||||
output_binary(buffer, blocksize, fp);
|
||||
break;
|
||||
case HEX:
|
||||
output_hex(buffer, blocksize, fp, piped_mode);
|
||||
break;
|
||||
case BASE64:
|
||||
output_base64(buffer, blocksize, fp, piped_mode);
|
||||
break;
|
||||
case DECIMAL:
|
||||
output_decimal(buffer, blocksize, fp, piped_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
// Display progress only in interactive mode (and verbose piped mode)
|
||||
gettimeofday(¤ttime, NULL);
|
||||
elapsed_time = get_time_diff(starttime, currenttime);
|
||||
if (interactive_mode && !options.quiet) {
|
||||
if (elapsed_time > 0) {
|
||||
rate = (double)totalbytes / (elapsed_time * 1000.0);
|
||||
printf("%lld/%lld Bytes Read at %6.2f Kbytes/s\r",
|
||||
totalbytes, options.bytes, rate);
|
||||
fflush(stdout);
|
||||
}
|
||||
} else if (piped_mode && options.verbose && !options.quiet && elapsed_time > 0) {
|
||||
rate = (double)totalbytes / (elapsed_time * 1000.0);
|
||||
printf("\r%lld/%lld Bytes Read at %6.2f Kbytes/s",
|
||||
totalbytes, options.bytes, rate);
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
// Print results only in interactive mode or piped verbose mode
|
||||
if ((interactive_mode && !options.quiet) || (piped_mode && options.verbose)) {
|
||||
gettimeofday(¤ttime, NULL);
|
||||
double total_time = get_time_diff(starttime, currenttime);
|
||||
|
||||
printf("\n\nResults\n");
|
||||
printf("=======\n");
|
||||
printf("Total time: %.2f seconds\n", total_time);
|
||||
printf("Total Ones: %lld\n", totalones);
|
||||
printf("Total Zeros: %lld\n", totalzeros);
|
||||
printf("Total Bits: %lld\n", totalbytes * 8);
|
||||
|
||||
if (totalones == totalzeros) {
|
||||
printf("Equal number of ones and zeros\n");
|
||||
} else if (totalones > totalzeros) {
|
||||
printf("Extra ones: %lld\n", totalones - totalzeros);
|
||||
} else {
|
||||
printf("Extra zeros: %lld\n", totalzeros - totalones);
|
||||
}
|
||||
printf("=======\n");
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
close(serial_fd);
|
||||
if (options.output_file && fp != stdout) fclose(fp);
|
||||
|
||||
// Reset terminal min setting
|
||||
char stty_cmd[MAX_PATH + 20];
|
||||
snprintf(stty_cmd, sizeof(stty_cmd), "stty -F %s min 1", port_path);
|
||||
system(stty_cmd); // Ignore failure
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,174 @@
|
|||
|
||||
#!/usr/bin/python
|
||||
|
||||
# TrueRNG Read - Simple Example
|
||||
# Chris K Cockrum
|
||||
# 7/9/2021
|
||||
#
|
||||
# Requires Python 2.7 or 3.7+, pyserial
|
||||
# On Linux - may need to be root or set /dev/tty port permissions to 666
|
||||
#
|
||||
# Python available here: https://www.python.org/
|
||||
# Install Pyserial package with: python -m pip install pyserial
|
||||
|
||||
import serial
|
||||
import time
|
||||
import os
|
||||
from serial.tools import list_ports
|
||||
|
||||
# Number of Bytes to Capture per Block
|
||||
blocksize=1024
|
||||
|
||||
# Number of Blocks to Capture
|
||||
numloops=1024
|
||||
|
||||
# Print our header
|
||||
print('TrueRNG Counting Ones vs Zeros Example')
|
||||
print('http://ubld.it')
|
||||
print('==================================================')
|
||||
|
||||
import serial.tools.list_ports
|
||||
|
||||
# ubld.it TrueRNG
|
||||
TrueRNGpid="04D8"
|
||||
TrueRNGhid="F5FE"
|
||||
|
||||
# ubld.it TrueRNGpro
|
||||
TrueRNGpropid="16D0"
|
||||
TrueRNGprohid="0AA0"
|
||||
|
||||
# ubld.it TrueRNGproV2
|
||||
TrueRNGproV2pid="04D8"
|
||||
TrueRNGproV2hid="EBB5"
|
||||
|
||||
# Set default of None for com port
|
||||
rng_com_port = None
|
||||
|
||||
ports = list(serial.tools.list_ports.comports())
|
||||
|
||||
for p in ports:
|
||||
if(rng_com_port == None):
|
||||
if TrueRNGproV2pid and TrueRNGproV2hid in p.hwid:
|
||||
rng_com_port = p.device
|
||||
print('TrueRNGproV2 Found')
|
||||
break
|
||||
if TrueRNGpropid and TrueRNGprohid in p.hwid:
|
||||
rng_com_port = p.device
|
||||
print('TrueRNGpro Found')
|
||||
break
|
||||
if TrueRNGpid and TrueRNGhid in p.hwid:
|
||||
rng_com_port = p.device
|
||||
print('TrueRNG Found')
|
||||
break
|
||||
|
||||
if rng_com_port == None:
|
||||
print('TrueRNG Not Found')
|
||||
exit()
|
||||
|
||||
# Print which port we're using
|
||||
print('Using com port: ' + str(rng_com_port))
|
||||
|
||||
# Print block size and number of loops
|
||||
print('Block Size: ' + str(blocksize) + ' Bytes')
|
||||
print('Number of loops: ' + str(numloops))
|
||||
print('Total size: ' + str(blocksize * numloops) + ' Bytes')
|
||||
print('Writing to: random.bin')
|
||||
print('==================================================')
|
||||
|
||||
# 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
|
||||
totalzeros=0
|
||||
|
||||
# Generate look-up table for number of 1's in a byte
|
||||
ones_in_byte = [0] * 256
|
||||
for n in range(256):
|
||||
ones_in_byte[n] = bin(n).count("1")
|
||||
|
||||
totalones=0
|
||||
totalzeros=0
|
||||
|
||||
print('Starting data collection...')
|
||||
starttime=time.time()
|
||||
# Loop
|
||||
for _ in range(numloops):
|
||||
|
||||
# Try to read the port and record the time before and after
|
||||
try:
|
||||
x=ser.read(blocksize) # read bytes from serial port
|
||||
except:
|
||||
print('Read Failed!!!')
|
||||
break
|
||||
|
||||
# Update total bytes read
|
||||
totalbytes +=len(x)
|
||||
|
||||
# Count ones
|
||||
for n in range(len(x)):
|
||||
totalones=totalones + ones_in_byte[x[n]]
|
||||
totalzeros=totalzeros + 8-ones_in_byte[x[n]]
|
||||
|
||||
# If we were able to open the file, write to disk
|
||||
if fp !=0:
|
||||
fp.write(x)
|
||||
|
||||
# Calculate the rate
|
||||
timenow=time.time()
|
||||
if(timenow!=starttime):
|
||||
rate=float(totalbytes) / ((timenow-starttime)*1000.0)
|
||||
|
||||
print(str(totalbytes) + ' Bytes Read at ' + '{:6.2f}'.format(rate) + ' Kbytes/s',end='\r')
|
||||
|
||||
endtime=time.time()
|
||||
total_time = endtime - starttime
|
||||
|
||||
print('\n\nResults')
|
||||
print( '=======')
|
||||
print('Total execution time: {:.2f} seconds'.format(total_time))
|
||||
print('Total Ones : ' + str(totalones))
|
||||
print('Total Zeros: ' + str(totalzeros) + '\n')
|
||||
print('Total Bits : ' + str(totalbytes*8))
|
||||
|
||||
if (totalones==totalzeros):
|
||||
print('\nEqual Number of Ones and Zeros in Capture!')
|
||||
if (totalones>totalzeros):
|
||||
print('\nThere are ' + str(totalones-totalzeros) + ' more ones in the Capture!')
|
||||
else:
|
||||
print('\nThere are ' + str(totalzeros-totalones) + ' more zeros in the Capture!')
|
||||
|
||||
print( '=======')
|
||||
|
||||
# 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')
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
6b5a1ff13961684786b40997b5d4fe5ff573b9fadf6ea798911ba5d628129501078309b22a0ccfd8c34035e2a7574d7aa94b28fc155d8d19d7164bb68a4bba4d95a6809a08c9cbd698792a49c66cbad2603222523a48eda4f3a726b2b331063a1af6fb9fe2cd4d4a81231096046e185839c6ae4f542fc51f81fee77277f906dcf031014228719589e1c09647a6909bcf6770b6cd66559356858f1b31b338b0f894de5ddfc1048c7bd38faf31b83649dbdc5fdf70b4a980b01c0860df564d615500f0d35454a4c7b1d249f368558a7ecdbb7a7036899ad305bc6b6ee5c11eac0b6890d0e82f5c5f7af765d0b159901972dd8cd6a99013868b82b3e816b62b1abd996a6d7167f8441bc6319189968df6e0e106425ed9502fbaa3caeba77971c9f8853072436ba8e5ffb3ff0db175c0d1bec45eb435b23bf0310a9a466b3f9a50675520b75bfc32d2adb3713f2e1b449e4c9a20a3ed960e5a473f9535b0ab2fc490f1d47bf853ca64512ddf520ef3ab4fa45cda19f70adc21344e3fa56693c4c04ebb0684ef0fcf431a28105c417f33c9f6f7a3f941becd61df8b2e4a73268d5df67d86b2a074d37c0c5ef8a16d11542a260341e407fc026a27b5defbd6cd9503c46fde1e140555da641e5157816d9075a6cb0543eaa039388e9fa4640f32e818b3cebf3a218b3cbfb1133690b9b49b0080da077312b6670309d0d616717d5638c68eea3db0c31e3c448bfa027c8e52a37bf3c0dad2152904263510a25af39db48326fd7a731ce105588b2dc26e9bf9229fc71a1587800740a598e6d842aa5e76c62acf88d481bc2bd9ede29fc05f03787afbb7cf9c030c896797c7bfa6b43a6b54b76c661146e78bc214a28459f94d4bc43bbfdaf74f665252370e0c51c438d94437e4cdbe26ce469d8e5ea2eec00ca6aae65b4a6e683d3cfd6d53b6bca388318b6228731dfc6c9a376a7277b8b73e26d9d8a892def68f248949576b1c4aedf6cd404c88191b4335993ad995a87f11490003271404ed58543bbf74123e8d2a23b09655720b79cfbf62dd2f7f8e1463da65034c429a637dcdfcebc8f08764c4ea612c40f62d6ce2b0eae6a42caf5d620bdef28450325370e159cb58b7554fc6d7ea2e10e2c24d378ab536fd71deafa10177683c0644343f1410a388606aad7550f505c45261d5bda7e5a3902a75ffd476b013960676e16b9780b8086dd6b5795dfb37926e023a00b6911513dacb5e2a8f019a8f1c115e840a4e2c6f103d1458270135fccab4205e3101debf84a5127ebc31b03a4eb4c2e14fa07baea02f40f7b021606c8af0789156b19818bc47a521ca3107c9b1cc78c8f0151c6b730013dbd1a3049f2ed38fe6aebbf601a633dfdd21ddae0f018c6ab6b724a21da6e95e7f3e3ba5ba84650cfbb4821707d524882665068464bdd83ad7a91442b35a01ee7651aa
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue