Skip to content

Commit

Permalink
std.rand: set DefaultCsprng to Gimli, and require a larger seed
Browse files Browse the repository at this point in the history
`DefaultCsprng` is documented as a cryptographically secure RNG.

While `ISAAC` is a CSPRNG, the variant we have, `ISAAC64` is not.
A 64 bit seed is a bit small to satisfy that claim.

We also saw it being used with the current date as a seed, that
also defeats the point of a CSPRNG.

Set `DefaultCsprng` to `Gimli` instead of `ISAAC64`, rename
the parameter from `init_s` to `secret_seed` + add a comment to
clarify what kind of seed is expected here.

Instead of directly touching the internals of the Gimli implementation
(which can change/be architecture-specific), add an `init()` function
to the state.

Our Gimli-based CSPRNG was also not backtracking resistant. Gimli
is a permutation; it can be reverted. So, if the state was ever leaked,
future secrets, but also all the previously generated ones could be
recovered. Clear the rate after a squeeze in order to prevent this.

Finally, a dumb test was added just to exercise `DefaultCsprng` since
we don't use it anywhere.
  • Loading branch information
jedisct1 committed Oct 10, 2020
1 parent a31b70c commit 15b6410
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 10 deletions.
9 changes: 9 additions & 0 deletions lib/std/crypto/gimli.zig
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ pub const State = struct {

const Self = @This();

pub fn init(initial_state: [State.BLOCKBYTES]u8) Self {
var data: [BLOCKBYTES / 4]u32 = undefined;
var i: usize = 0;
while (i < State.BLOCKBYTES) : (i += 4) {
data[i / 4] = mem.readIntLittle(u32, initial_state[i..][0..4]);
}
return Self{ .data = data };
}

/// TODO follow the span() convention instead of having this and `toSliceConst`
pub fn toSlice(self: *Self) []u8 {
return mem.sliceAsBytes(self.data[0..]);
Expand Down
36 changes: 26 additions & 10 deletions lib/std/rand.zig
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const maxInt = std.math.maxInt;
pub const DefaultPrng = Xoroshiro128;

/// Cryptographically secure random numbers.
pub const DefaultCsprng = Isaac64;
pub const DefaultCsprng = Gimli;

pub const Random = struct {
fillFn: fn (r: *Random, buf: []u8) void,
Expand Down Expand Up @@ -749,29 +749,35 @@ pub const Gimli = struct {
random: Random,
state: std.crypto.core.Gimli,

pub fn init(init_s: u64) Gimli {
pub const secret_seed_length = 32;

/// The seed must be uniform, secret and `secret_seed_length` bytes long.
/// It can be generated using `std.crypto.randomBytes()`.
pub fn init(secret_seed: [secret_seed_length]u8) Gimli {
var initial_state: [std.crypto.core.Gimli.BLOCKBYTES]u8 = undefined;
mem.copy(u8, initial_state[0..secret_seed_length], &secret_seed);
mem.set(u8, initial_state[secret_seed_length..], 0);
var self = Gimli{
.random = Random{ .fillFn = fill },
.state = std.crypto.core.Gimli{
.data = [_]u32{0} ** (std.crypto.gimli.State.BLOCKBYTES / 4),
},
.state = std.crypto.core.Gimli.init(initial_state),
};
self.state.data[0] = @truncate(u32, init_s >> 32);
self.state.data[1] = @truncate(u32, init_s);
return self;
}

fn fill(r: *Random, buf: []u8) void {
const self = @fieldParentPtr(Gimli, "random", r);

self.state.squeeze(buf);
if (buf.len != 0) {
self.state.squeeze(buf);
} else {
self.state.permute();
}
mem.set(u8, self.state.toSlice()[0..std.crypto.core.Gimli.RATE], 0);
}
};

// ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html
//
// CSPRNG
//
// Follows the general idea of the implementation from here with a few shortcuts.
// https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html
pub const Isaac64 = struct {
Expand Down Expand Up @@ -1139,6 +1145,16 @@ fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void {
}
}

test "CSPRNG" {
var secret_seed: [DefaultCsprng.secret_seed_length]u8 = undefined;
try std.crypto.randomBytes(&secret_seed);
var csprng = DefaultCsprng.init(secret_seed);
const a = csprng.random.int(u64);
const b = csprng.random.int(u64);
const c = csprng.random.int(u64);
assert(a ^ b ^ c != 0);
}

test "" {
std.meta.refAllDecls(@This());
}

0 comments on commit 15b6410

Please sign in to comment.