Random number generator (#46)

wch-ch32v003
David Sugar 1 year ago committed by GitHub
parent fa78bbfc4a
commit 975e5e446c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -69,6 +69,7 @@ pub const Examples = struct {
squarewave: *microzig.EmbeddedExecutable, squarewave: *microzig.EmbeddedExecutable,
//uart_pins: microzig.EmbeddedExecutable, //uart_pins: microzig.EmbeddedExecutable,
flash_program: *microzig.EmbeddedExecutable, flash_program: *microzig.EmbeddedExecutable,
random: *microzig.EmbeddedExecutable,
pub fn init(b: *Builder, optimize: std.builtin.OptimizeMode) Examples { pub fn init(b: *Builder, optimize: std.builtin.OptimizeMode) Examples {
var ret: Examples = undefined; var ret: Examples = undefined;

@ -0,0 +1,69 @@
//! Example that generates a 4 byte random number every second and outputs the result over UART
const std = @import("std");
const microzig = @import("microzig");
const rp2040 = microzig.hal;
const flash = rp2040.flash;
const time = rp2040.time;
const gpio = rp2040.gpio;
const clocks = rp2040.clocks;
const rand = rp2040.rand;
const led = 25;
const uart_id = 0;
const baud_rate = 115200;
const uart_tx_pin = 0;
const uart_rx_pin = 1;
pub fn panic(message: []const u8, _: ?*std.builtin.StackTrace, _: ?usize) noreturn {
std.log.err("panic: {s}", .{message});
@breakpoint();
while (true) {}
}
pub const std_options = struct {
pub const log_level = .debug;
pub const logFn = rp2040.uart.log;
};
pub fn main() !void {
gpio.reset();
gpio.init(led);
gpio.set_direction(led, .out);
gpio.put(led, 1);
const uart = rp2040.uart.UART.init(uart_id, .{
.baud_rate = baud_rate,
.tx_pin = uart_tx_pin,
.rx_pin = uart_rx_pin,
.clock_config = rp2040.clock_config,
});
var ascon = rand.Ascon.init();
var rng = ascon.random();
rp2040.uart.init_logger(uart);
var buffer: [8]u8 = undefined;
var dist: [256]usize = .{0} ** 256;
var counter: usize = 0;
while (true) {
rng.bytes(buffer[0..]);
counter += 8;
for (buffer) |byte| {
dist[@intCast(usize, byte)] += 1;
}
std.log.info("Generate random number: {any}", .{buffer});
if (counter % 256 == 0) {
var i: usize = 0;
std.log.info("Distribution:", .{});
while (i < 256) : (i += 1) {
std.log.info("{} -> {}, {d:2}%", .{ i, dist[i], @intToFloat(f32, dist[i]) / @intToFloat(f32, counter) });
}
}
time.sleep_ms(1000);
}
}

@ -16,6 +16,7 @@ pub const irq = @import("hal/irq.zig");
pub const rom = @import("hal/rom.zig"); pub const rom = @import("hal/rom.zig");
pub const flash = @import("hal/flash.zig"); pub const flash = @import("hal/flash.zig");
pub const pio = @import("hal/pio.zig"); pub const pio = @import("hal/pio.zig");
pub const rand = @import("hal/random.zig");
pub const clock_config = clocks.GlobalConfiguration.init(.{ pub const clock_config = clocks.GlobalConfiguration.init(.{
.ref = .{ .source = .src_xosc }, .ref = .{ .source = .src_xosc },

@ -0,0 +1,87 @@
//! Random number generator (RNG) using the Ascon CSPRNG
const std = @import("std");
const assert = std.debug.assert;
const Random = std.rand.Random;
const microzig = @import("microzig");
const peripherals = microzig.chip.peripherals;
/// Wrapper around the Ascon CSPRNG with automatic reseed using the ROSC
///
/// ## Usage
///
/// ```zig
/// var ascon = Ascon.init();
/// var rng = ascon.random();
/// ```
///
/// _WARNING_: This might not meet the requirements of randomness
/// for security systems because the ROSC as entropy source can be
/// compromised. However, it promises at least equal distribution.
pub const Ascon = struct {
state: std.rand.Ascon,
counter: usize = 0,
const reseed_threshold = 4096;
const secret_seed_length = std.rand.Ascon.secret_seed_length;
pub fn init() @This() {
// Ensure that the system clocks run from the XOSC and/or PLLs
const ref_src = peripherals.CLOCKS.CLK_REF_CTRL.read().SRC.value;
const sys_clk_src = peripherals.CLOCKS.CLK_SYS_CTRL.read().SRC.value;
const aux_src = peripherals.CLOCKS.CLK_SYS_CTRL.read().AUXSRC.value;
assert((ref_src != .rosc_clksrc_ph and sys_clk_src == .clk_ref) or
(sys_clk_src == .clksrc_clk_sys_aux and aux_src != .rosc_clksrc));
// Get `secret_seed_length` random bytes from the ROSC ...
var b: [secret_seed_length]u8 = undefined;
rosc(&b);
return @This(){ .state = std.rand.Ascon.init(b) };
}
/// Returns a `std.rand.Random` structure backed by the current RNG
pub fn random(self: *@This()) Random {
return Random.init(self, fill);
}
/// Fills the buffer with random bytes
pub fn fill(self: *@This(), buf: []u8) void {
// Reseed every `secret_seed_length` bytes
if (self.counter > reseed_threshold) {
var b: [secret_seed_length]u8 = undefined;
rosc(&b);
self.state.addEntropy(&b);
self.counter = 0;
}
self.state.fill(buf);
self.counter += buf.len;
}
/// Fill the buffer with up to buffer.len random bytes
///
/// rand uses the RANDOMBIT register of the ROSC as its source, i. e.,
/// the system clocks _MUST_ run from the XOSC and/or PLLs.
///
/// _WARNING_: This function does not meet the requirements of randomness
/// for security systems because it can be compromised, but it may be useful
/// in less critical applications.
fn rosc(buffer: []u8) void {
const rosc_state = peripherals.ROSC.CTRL.read().ENABLE.value;
// Enable the ROSC so it generates random bits for us
peripherals.ROSC.CTRL.modify(.{ .ENABLE = .{ .value = .ENABLE } });
defer peripherals.ROSC.CTRL.modify(.{ .ENABLE = .{ .value = rosc_state } });
var i: usize = 0;
while (i < buffer.len) : (i += 1) {
// We poll RANDOMBIT eight times per cycle to build a random byte
var r: u8 = @intCast(u8, peripherals.ROSC.RANDOMBIT.read().RANDOMBIT);
var j: usize = 0;
while (j < 7) : (j += 1) {
r = (r << 1) | @intCast(u8, peripherals.ROSC.RANDOMBIT.read().RANDOMBIT);
}
buffer[i] = r;
}
}
};
Loading…
Cancel
Save