Random number generator (#46)
This commit is contained in:
parent
fa78bbfc4a
commit
975e5e446c
|
@ -69,6 +69,7 @@ pub const Examples = struct {
|
|||
squarewave: *microzig.EmbeddedExecutable,
|
||||
//uart_pins: microzig.EmbeddedExecutable,
|
||||
flash_program: *microzig.EmbeddedExecutable,
|
||||
random: *microzig.EmbeddedExecutable,
|
||||
|
||||
pub fn init(b: *Builder, optimize: std.builtin.OptimizeMode) Examples {
|
||||
var ret: Examples = undefined;
|
||||
|
|
69
examples/random.zig
Normal file
69
examples/random.zig
Normal file
|
@ -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 flash = @import("hal/flash.zig");
|
||||
pub const pio = @import("hal/pio.zig");
|
||||
pub const rand = @import("hal/random.zig");
|
||||
|
||||
pub const clock_config = clocks.GlobalConfiguration.init(.{
|
||||
.ref = .{ .source = .src_xosc },
|
||||
|
|
87
src/hal/random.zig
Normal file
87
src/hal/random.zig
Normal file
|
@ -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…
Reference in New Issue
Block a user