Skip to content
This repository has been archived by the owner on Feb 17, 2024. It is now read-only.

Random #86

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 67 additions & 46 deletions src/hal/random.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,42 @@ 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
/// Wrapper around the Ascon CSPRNG
///
/// The rng collects its entropy from 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));
// The init function includes just a assertion. One
// must make sure the processor is in the right state
// to create random numbers using the ROSC.
rand_init();

// Get `secret_seed_length` random bytes from the ROSC ...
// We seed the RNG with random bits from the ROSC...
var b: [secret_seed_length]u8 = undefined;
rosc(&b);
var i: usize = 0;
blk: while (true) {
var n = rand_rosc();
for (0..4) |_| {
if (i >= secret_seed_length) {
break :blk;
}

b[i] = @intCast(n & 0xff);
n >>= 8;
i += 1;
}
}

return @This(){ .state = std.rand.Ascon.init(b) };
}
Expand All @@ -48,40 +54,55 @@ pub const Ascon = struct {

/// 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;
}
// fill the buffer with random bytes
self.state.fill(buf);
self.counter += buf.len;

// then mix new entropy from the rosc into the state
const n = rand_rosc();
self.state.addEntropy(&std.mem.toBytes(n));
}
};

/// 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 } });
pub fn rand_init() void {
// 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));
}

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 = @as(u8, @intCast(peripherals.ROSC.RANDOMBIT.read().RANDOMBIT));
var j: usize = 0;
while (j < 7) : (j += 1) {
r = (r << 1) | @as(u8, @intCast(peripherals.ROSC.RANDOMBIT.read().RANDOMBIT));
}
buffer[i] = r;
/// Create a random `u32` value.
///
/// The function uses the RANDOMBIT register of the ROSC as its source, i. e.,
/// the system clocks _MUST_ run from the XOSC and/or PLLs.
pub fn rand_rosc() u32 {
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 r: u32 = 0;
for (0..32) |_| {
var r1: u32 = 0;
var r2: u32 = 0;
while (true) {
r1 = @as(u32, @intCast(peripherals.ROSC.RANDOMBIT.read().RANDOMBIT));
// insert delay
asm volatile (
\\nop
\\nop
\\nop
\\nop
\\nop
\\nop
\\nop
\\nop
);
r2 = @as(u32, @intCast(peripherals.ROSC.RANDOMBIT.read().RANDOMBIT));
if (r1 != r2) break;
}
r = (r << 1) | r1; // r1 is just one bit so we can append it to the end
}
};
return r;
}