authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2023-02-08 14:23:48+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-13 02:22:24-05:00
logf62e3b8c0dd232bddb559405b00c1c1c4815f359
tree3c3b2d6bc3ad5290db72f2bf545ff71a20f24a02
parenta8edd67d3d87b585ebbea226bb916ee1f7263458

std.crypto: add the Ascon permutation

Ascon has been selected as new standard for lightweight cryptography in the NIST Lightweight Cryptography competition. Ascon won over Gimli and Xoodoo. The permutation is unlikely to change. However, NIST may tweak the constructions (XOF, hash, authenticated encryption) before standardizing them. For that reason, implementations of those are better maintained outside the standard library for now. In fact, we already had an Ascon implementation in Zig: `std.crypto.aead.isap` is based on it. While the implementation was here, there was no public API to access it directly. So: - The Ascon permutation is now available as `std.crypto.core.Ascon`, with everything needed to use it in AEADs and other Ascon-based constructions - The ISAP implementation now uses std.crypto.core.Ascon instead of keeping a private copy - The default CSPRNG replaces Xoodoo with Ascon. And instead of an ad-hoc construction, it's using the XOFa mode of the NIST submission.

5 files changed, 317 insertions(+), 115 deletions(-)

lib/std/crypto.zig+3
......@@ -46,6 +46,7 @@ pub const auth = struct {
4646/// Core functions, that should rarely be used directly by applications.
4747pub const core = struct {
4848 pub const aes = @import("crypto/aes.zig");
49 pub const Ascon = @import("crypto/ascon.zig").State;
4950 pub const Gimli = @import("crypto/gimli.zig").State;
5051 pub const Xoodoo = @import("crypto/xoodoo.zig").State;
5152
......@@ -205,7 +206,9 @@ test {
205206 _ = auth.siphash;
206207
207208 _ = core.aes;
209 _ = core.Ascon;
208210 _ = core.Gimli;
211 _ = core.Xoodoo;
209212 _ = core.modes;
210213
211214 _ = dh.X25519;
lib/std/crypto/ascon.zig created+227
......@@ -0,0 +1,227 @@
1//! Ascon is a 320-bit permutation, selected as new standard for lightweight cryptography
2//! in the NIST Lightweight Cryptography competition (2019–2023).
3//! https://csrc.nist.gov/News/2023/lightweight-cryptography-nist-selects-ascon
4//!
5//! The permutation is compact, and optimized for timing and side channel resistance,
6//! making it a good choice for embedded applications.
7//!
8//! It is not meant to be used directly, but as a building block for symmetric cryptography.
9
10const std = @import("std");
11const builtin = std.builtin;
12const debug = std.debug;
13const mem = std.mem;
14const testing = std.testing;
15const rotr = std.math.rotr;
16
17/// An Ascon state.
18///
19/// The state is represented as 5 64-bit words.
20///
21/// The NIST submission (v1.2) serializes these words as big-endian,
22/// but software implementations are free to use native endianness.
23pub fn State(comptime endian: builtin.Endian) type {
24 return struct {
25 const Self = @This();
26
27 /// Number of bytes in the state.
28 pub const block_bytes = 40;
29
30 const Block = [5]u64;
31
32 st: Block,
33
34 /// Initialize the state from a slice of bytes.
35 pub fn init(initial_state: [block_bytes]u8) Self {
36 var state = Self{ .st = undefined };
37 mem.copy(u8, state.asBytes(), &initial_state);
38 state.endianSwap();
39 return state;
40 }
41
42 /// Initialize the state from u64 words in native endianness.
43 pub fn initFromWords(initial_state: [5]u64) Self {
44 var state = Self{ .st = initial_state };
45 return state;
46 }
47
48 /// Initialize the state for Ascon XOF
49 pub fn initXof() Self {
50 return Self{ .st = Block{
51 0xb57e273b814cd416,
52 0x2b51042562ae2420,
53 0x66a3a7768ddf2218,
54 0x5aad0a7a8153650c,
55 0x4f3e0e32539493b6,
56 } };
57 }
58
59 /// Initialize the state for Ascon XOFa
60 pub fn initXofA() Self {
61 return Self{ .st = Block{
62 0x44906568b77b9832,
63 0xcd8d6cae53455532,
64 0xf7b5212756422129,
65 0x246885e1de0d225b,
66 0xa8cb5ce33449973f,
67 } };
68 }
69
70 /// A representation of the state as bytes. The byte order is architecture-dependent.
71 pub fn asBytes(self: *Self) *[block_bytes]u8 {
72 return mem.asBytes(&self.st);
73 }
74
75 /// Byte-swap the entire state if the architecture doesn't match the required endianness.
76 pub fn endianSwap(self: *Self) void {
77 for (self.st) |*w| {
78 w.* = mem.toNative(u64, w.*, endian);
79 }
80 }
81
82 /// Set bytes starting at the beginning of the state.
83 pub fn setBytes(self: *Self, bytes: []const u8) void {
84 var i: usize = 0;
85 while (i + 8 <= bytes.len) : (i += 8) {
86 self.st[i / 8] = mem.readInt(u64, bytes[i..][0..8], endian);
87 }
88 if (i < bytes.len) {
89 var padded = [_]u8{0} ** 8;
90 mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
91 self.st[i / 8] = mem.readInt(u64, padded[0..], endian);
92 }
93 }
94
95 /// XOR a byte into the state at a given offset.
96 pub fn addByte(self: *Self, byte: u8, offset: usize) void {
97 const z = switch (endian) {
98 .Big => 64 - 8 - 8 * @truncate(u6, offset % 8),
99 .Little => 8 * @truncate(u6, offset % 8),
100 };
101 self.st[offset / 8] ^= @as(u64, byte) << z;
102 }
103
104 /// XOR bytes into the beginning of the state.
105 pub fn addBytes(self: *Self, bytes: []const u8) void {
106 var i: usize = 0;
107 while (i + 8 <= bytes.len) : (i += 8) {
108 self.st[i / 8] ^= mem.readInt(u64, bytes[i..][0..8], endian);
109 }
110 if (i < bytes.len) {
111 var padded = [_]u8{0} ** 8;
112 mem.copy(u8, padded[0 .. bytes.len - i], bytes[i..]);
113 self.st[i / 8] ^= mem.readInt(u64, padded[0..], endian);
114 }
115 }
116
117 /// Extract the first bytes of the state.
118 pub fn extractBytes(self: *Self, out: []u8) void {
119 var i: usize = 0;
120 while (i + 8 <= out.len) : (i += 8) {
121 mem.writeInt(u64, out[i..][0..8], self.st[i / 8], endian);
122 }
123 if (i < out.len) {
124 var padded = [_]u8{0} ** 8;
125 mem.writeInt(u64, padded[0..], self.st[i / 8], endian);
126 mem.copy(u8, out[i..], padded[0 .. out.len - i]);
127 }
128 }
129
130 /// XOR the first bytes of the state into a slice of bytes.
131 pub fn xorBytes(self: *Self, out: []u8, in: []const u8) void {
132 debug.assert(out.len == in.len);
133
134 var i: usize = 0;
135 while (i + 8 <= in.len) : (i += 8) {
136 const x = mem.readIntNative(u64, in[i..][0..8]) ^ mem.nativeTo(u64, self.st[i / 8], endian);
137 mem.writeIntNative(u64, out[i..][0..8], x);
138 }
139 if (i < in.len) {
140 var padded = [_]u8{0} ** 8;
141 mem.copy(u8, padded[0 .. in.len - i], in[i..]);
142 const x = mem.readIntNative(u64, &padded) ^ mem.nativeTo(u64, self.st[i / 8], endian);
143 mem.writeIntNative(u64, &padded, x);
144 mem.copy(u8, out[i..], padded[0 .. in.len - i]);
145 }
146 }
147
148 /// Set the words storing the bytes of a given range to zero.
149 pub fn clear(self: *Self, from: usize, to: usize) void {
150 mem.set(u64, self.st[from / 8 .. (to + 7) / 8], 0);
151 }
152
153 /// Clear the entire state, disabling compiler optimizations.
154 pub fn secureZero(self: *Self) void {
155 std.crypto.utils.secureZero(u64, &self.st);
156 }
157
158 /// Apply a reduced-round permutation to the state.
159 pub inline fn permuteR(state: *Self, comptime rounds: u4) void {
160 const rks = [12]u64{ 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b };
161 inline for (rks[rks.len - rounds ..]) |rk| {
162 state.round(rk);
163 }
164 }
165
166 /// Apply a full-round permutation to the state.
167 pub inline fn permute(state: *Self) void {
168 state.permuteR(12);
169 }
170
171 // Core Ascon permutation.
172 inline fn round(state: *Self, rk: u64) void {
173 const x = &state.st;
174 x[2] ^= rk;
175
176 x[0] ^= x[4];
177 x[4] ^= x[3];
178 x[2] ^= x[1];
179 var t: Block = .{
180 x[0] ^ (~x[1] & x[2]),
181 x[1] ^ (~x[2] & x[3]),
182 x[2] ^ (~x[3] & x[4]),
183 x[3] ^ (~x[4] & x[0]),
184 x[4] ^ (~x[0] & x[1]),
185 };
186 t[1] ^= t[0];
187 t[3] ^= t[2];
188 t[0] ^= t[4];
189
190 x[2] = t[2] ^ rotr(u64, t[2], 6 - 1);
191 x[3] = t[3] ^ rotr(u64, t[3], 17 - 10);
192 x[4] = t[4] ^ rotr(u64, t[4], 41 - 7);
193 x[0] = t[0] ^ rotr(u64, t[0], 28 - 19);
194 x[1] = t[1] ^ rotr(u64, t[1], 61 - 39);
195 x[2] = t[2] ^ rotr(u64, x[2], 1);
196 x[3] = t[3] ^ rotr(u64, x[3], 10);
197 x[4] = t[4] ^ rotr(u64, x[4], 7);
198 x[0] = t[0] ^ rotr(u64, x[0], 19);
199 x[1] = t[1] ^ rotr(u64, x[1], 39);
200 x[2] = ~x[2];
201 }
202 };
203}
204
205test "ascon" {
206 const Ascon = State(.Big);
207 const bytes = [_]u8{0x01} ** Ascon.block_bytes;
208 var st = Ascon.init(bytes);
209 var out: [Ascon.block_bytes]u8 = undefined;
210 st.permute();
211 st.extractBytes(&out);
212 const expected1 = [_]u8{ 148, 147, 49, 226, 218, 221, 208, 113, 186, 94, 96, 10, 183, 219, 119, 150, 169, 206, 65, 18, 215, 97, 78, 106, 118, 81, 211, 150, 52, 17, 117, 64, 216, 45, 148, 240, 65, 181, 90, 180 };
213 try testing.expectEqualSlices(u8, &expected1, &out);
214 st.clear(0, 10);
215 st.extractBytes(&out);
216 const expected2 = [_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 206, 65, 18, 215, 97, 78, 106, 118, 81, 211, 150, 52, 17, 117, 64, 216, 45, 148, 240, 65, 181, 90, 180 };
217 try testing.expectEqualSlices(u8, &expected2, &out);
218 st.addByte(1, 5);
219 st.addByte(2, 5);
220 st.extractBytes(&out);
221 const expected3 = [_]u8{ 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 169, 206, 65, 18, 215, 97, 78, 106, 118, 81, 211, 150, 52, 17, 117, 64, 216, 45, 148, 240, 65, 181, 90, 180 };
222 try testing.expectEqualSlices(u8, &expected3, &out);
223 st.addBytes(&bytes);
224 st.extractBytes(&out);
225 const expected4 = [_]u8{ 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 168, 207, 64, 19, 214, 96, 79, 107, 119, 80, 210, 151, 53, 16, 116, 65, 217, 44, 149, 241, 64, 180, 91, 181 };
226 try testing.expectEqualSlices(u8, &expected4, &out);
227}
lib/std/crypto/isap.zig+40-114
......@@ -1,9 +1,11 @@
11const std = @import("std");
2const crypto = std.crypto;
23const debug = std.debug;
34const mem = std.mem;
45const math = std.math;
56const testing = std.testing;
6const AuthenticationError = std.crypto.errors.AuthenticationError;
7const Ascon = crypto.core.Ascon(.Big);
8const AuthenticationError = crypto.errors.AuthenticationError;
79
810/// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks.
911/// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf
......@@ -25,90 +27,26 @@ pub const IsapA128A = struct {
2527 const iv2 = [_]u8{ 0x02, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };
2628 const iv3 = [_]u8{ 0x03, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };
2729
28 const Block = [5]u64;
29
30 block: Block,
31
32 fn round(isap: *IsapA128A, rk: u64) void {
33 var x = &isap.block;
34 x[2] ^= rk;
35 x[0] ^= x[4];
36 x[4] ^= x[3];
37 x[2] ^= x[1];
38 var t = x.*;
39 x[0] = t[0] ^ ((~t[1]) & t[2]);
40 x[2] = t[2] ^ ((~t[3]) & t[4]);
41 x[4] = t[4] ^ ((~t[0]) & t[1]);
42 x[1] = t[1] ^ ((~t[2]) & t[3]);
43 x[3] = t[3] ^ ((~t[4]) & t[0]);
44 x[1] ^= x[0];
45 t[1] = x[1];
46 x[1] = math.rotr(u64, x[1], 39);
47 x[3] ^= x[2];
48 t[2] = x[2];
49 x[2] = math.rotr(u64, x[2], 1);
50 t[4] = x[4];
51 t[2] ^= x[2];
52 x[2] = math.rotr(u64, x[2], 5);
53 t[3] = x[3];
54 t[1] ^= x[1];
55 x[3] = math.rotr(u64, x[3], 10);
56 x[0] ^= x[4];
57 x[4] = math.rotr(u64, x[4], 7);
58 t[3] ^= x[3];
59 x[2] ^= t[2];
60 x[1] = math.rotr(u64, x[1], 22);
61 t[0] = x[0];
62 x[2] = ~x[2];
63 x[3] = math.rotr(u64, x[3], 7);
64 t[4] ^= x[4];
65 x[4] = math.rotr(u64, x[4], 34);
66 x[3] ^= t[3];
67 x[1] ^= t[1];
68 x[0] = math.rotr(u64, x[0], 19);
69 x[4] ^= t[4];
70 t[0] ^= x[0];
71 x[0] = math.rotr(u64, x[0], 9);
72 x[0] ^= t[0];
73 }
74
75 fn p12(isap: *IsapA128A) void {
76 const rks = [12]u64{ 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b };
77 inline for (rks) |rk| {
78 isap.round(rk);
79 }
80 }
81
82 fn p6(isap: *IsapA128A) void {
83 const rks = [6]u64{ 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b };
84 inline for (rks) |rk| {
85 isap.round(rk);
86 }
87 }
88
89 fn p1(isap: *IsapA128A) void {
90 isap.round(0x4b);
91 }
30 st: Ascon,
9231
9332 fn absorb(isap: *IsapA128A, m: []const u8) void {
94 var block = &isap.block;
9533 var i: usize = 0;
9634 while (true) : (i += 8) {
9735 const left = m.len - i;
9836 if (left >= 8) {
99 block[0] ^= mem.readIntBig(u64, m[i..][0..8]);
100 isap.p12();
37 isap.st.addBytes(m[i..][0..8]);
38 isap.st.permute();
10139 if (left == 8) {
102 block[0] ^= 0x8000000000000000;
103 isap.p12();
40 isap.st.addByte(0x80, 0);
41 isap.st.permute();
10442 break;
10543 }
10644 } else {
10745 var padded = [_]u8{0} ** 8;
10846 mem.copy(u8, padded[0..left], m[i..]);
10947 padded[left] = 0x80;
110 block[0] ^= mem.readIntBig(u64, padded[0..]);
111 isap.p12();
48 isap.st.addBytes(&padded);
49 isap.st.permute();
11250 break;
11351 }
11452 }
......@@ -116,65 +54,59 @@ pub const IsapA128A = struct {
11654
11755 fn trickle(k: [16]u8, iv: [8]u8, y: []const u8, comptime out_len: usize) [out_len]u8 {
11856 var isap = IsapA128A{
119 .block = Block{
57 .st = Ascon.initFromWords(.{
12058 mem.readIntBig(u64, k[0..8]),
12159 mem.readIntBig(u64, k[8..16]),
12260 mem.readIntBig(u64, iv[0..8]),
12361 0,
12462 0,
125 },
63 }),
12664 };
127 isap.p12();
65 isap.st.permute();
12866
12967 var i: usize = 0;
13068 while (i < y.len * 8 - 1) : (i += 1) {
13169 const cur_byte_pos = i / 8;
13270 const cur_bit_pos = @truncate(u3, 7 - (i % 8));
133 const cur_bit = @as(u64, ((y[cur_byte_pos] >> cur_bit_pos) & 1) << 7);
134 isap.block[0] ^= cur_bit << 56;
135 isap.p1();
71 const cur_bit = ((y[cur_byte_pos] >> cur_bit_pos) & 1) << 7;
72 isap.st.addByte(cur_bit, 0);
73 isap.st.permuteR(1);
13674 }
137 const cur_bit = @as(u64, (y[y.len - 1] & 1) << 7);
138 isap.block[0] ^= cur_bit << 56;
139 isap.p12();
75 const cur_bit = (y[y.len - 1] & 1) << 7;
76 isap.st.addByte(cur_bit, 0);
77 isap.st.permute();
14078
14179 var out: [out_len]u8 = undefined;
142 var j: usize = 0;
143 while (j < out_len) : (j += 8) {
144 mem.writeIntBig(u64, out[j..][0..8], isap.block[j / 8]);
145 }
146 std.crypto.utils.secureZero(u64, &isap.block);
80 isap.st.extractBytes(&out);
81 isap.st.secureZero();
14782 return out;
14883 }
14984
15085 fn mac(c: []const u8, ad: []const u8, npub: [16]u8, key: [16]u8) [16]u8 {
15186 var isap = IsapA128A{
152 .block = Block{
87 .st = Ascon.initFromWords(.{
15388 mem.readIntBig(u64, npub[0..8]),
15489 mem.readIntBig(u64, npub[8..16]),
15590 mem.readIntBig(u64, iv1[0..]),
15691 0,
15792 0,
158 },
93 }),
15994 };
160 isap.p12();
95 isap.st.permute();
16196
16297 isap.absorb(ad);
163 isap.block[4] ^= 1;
98 isap.st.addByte(1, Ascon.block_bytes - 1);
16499 isap.absorb(c);
165100
166101 var y: [16]u8 = undefined;
167 mem.writeIntBig(u64, y[0..8], isap.block[0]);
168 mem.writeIntBig(u64, y[8..16], isap.block[1]);
102 isap.st.extractBytes(&y);
169103 const nb = trickle(key, iv2, y[0..], 16);
170 isap.block[0] = mem.readIntBig(u64, nb[0..8]);
171 isap.block[1] = mem.readIntBig(u64, nb[8..16]);
172 isap.p12();
104 isap.st.setBytes(&nb);
105 isap.st.permute();
173106
174107 var tag: [16]u8 = undefined;
175 mem.writeIntBig(u64, tag[0..8], isap.block[0]);
176 mem.writeIntBig(u64, tag[8..16], isap.block[1]);
177 std.crypto.utils.secureZero(u64, &isap.block);
108 isap.st.extractBytes(&tag);
109 isap.st.secureZero();
178110 return tag;
179111 }
180112
......@@ -183,34 +115,31 @@ pub const IsapA128A = struct {
183115
184116 const nb = trickle(key, iv3, npub[0..], 24);
185117 var isap = IsapA128A{
186 .block = Block{
118 .st = Ascon.initFromWords(.{
187119 mem.readIntBig(u64, nb[0..8]),
188120 mem.readIntBig(u64, nb[8..16]),
189121 mem.readIntBig(u64, nb[16..24]),
190122 mem.readIntBig(u64, npub[0..8]),
191123 mem.readIntBig(u64, npub[8..16]),
192 },
124 }),
193125 };
194 isap.p6();
126 isap.st.permuteR(6);
195127
196128 var i: usize = 0;
197129 while (true) : (i += 8) {
198130 const left = in.len - i;
199131 if (left >= 8) {
200 mem.writeIntNative(u64, out[i..][0..8], mem.bigToNative(u64, isap.block[0]) ^ mem.readIntNative(u64, in[i..][0..8]));
132 isap.st.xorBytes(out[i..][0..8], in[i..][0..8]);
201133 if (left == 8) {
202134 break;
203135 }
204 isap.p6();
136 isap.st.permuteR(6);
205137 } else {
206 var pad = [_]u8{0} ** 8;
207 mem.copy(u8, pad[0..left], in[i..][0..left]);
208 mem.writeIntNative(u64, pad[i..][0..8], mem.bigToNative(u64, isap.block[0]) ^ mem.readIntNative(u64, pad[i..][0..8]));
209 mem.copy(u8, out[i..][0..left], pad[0..left]);
138 isap.st.xorBytes(out[i..], in[i..]);
210139 break;
211140 }
212141 }
213 std.crypto.utils.secureZero(u64, &isap.block);
142 isap.st.secureZero();
214143 }
215144
216145 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
......@@ -220,12 +149,9 @@ pub const IsapA128A = struct {
220149
221150 pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void {
222151 var computed_tag = mac(c, ad, npub, key);
223 var acc: u8 = 0;
224 for (computed_tag) |_, j| {
225 acc |= (computed_tag[j] ^ tag[j]);
226 }
227 std.crypto.utils.secureZero(u8, &computed_tag);
228 if (acc != 0) {
152 const res = crypto.utils.timingSafeEql([tag_length]u8, computed_tag, tag);
153 crypto.utils.secureZero(u8, &computed_tag);
154 if (!res) {
229155 return error.AuthenticationFailed;
230156 }
231157 xor(m, c, npub, key);
lib/std/rand.zig+2-1
......@@ -18,8 +18,9 @@ const maxInt = std.math.maxInt;
1818pub const DefaultPrng = Xoshiro256;
1919
2020/// Cryptographically secure random numbers.
21pub const DefaultCsprng = Xoodoo;
21pub const DefaultCsprng = Ascon;
2222
23pub const Ascon = @import("rand/Ascon.zig");
2324pub const Isaac64 = @import("rand/Isaac64.zig");
2425pub const Xoodoo = @import("rand/Xoodoo.zig");
2526pub const Pcg = @import("rand/Pcg.zig");
lib/std/rand/Ascon.zig created+45
......@@ -0,0 +1,45 @@
1//! CSPRNG based on the Ascon XOFa construction
2
3const std = @import("std");
4const min = std.math.min;
5const mem = std.mem;
6const Random = std.rand.Random;
7const Self = @This();
8
9state: std.crypto.core.Ascon(.Little),
10
11const rate = 8;
12pub const secret_seed_length = 32;
13
14/// The seed must be uniform, secret and `secret_seed_length` bytes long.
15pub fn init(secret_seed: [secret_seed_length]u8) Self {
16 var state = std.crypto.core.Ascon(.Little).initXofA();
17 var i: usize = 0;
18 while (i + rate <= secret_seed.len) : (i += rate) {
19 state.addBytes(secret_seed[i..][0..rate]);
20 state.permuteR(8);
21 }
22 const left = secret_seed.len - i;
23 if (left > 0) state.addBytes(secret_seed[i..]);
24 state.addByte(0x80, left);
25 state.permute();
26 return Self{ .state = state };
27}
28
29pub fn random(self: *Self) Random {
30 return Random.init(self, fill);
31}
32
33pub fn fill(self: *Self, buf: []u8) void {
34 var i: usize = 0;
35 while (true) {
36 const left = buf.len - i;
37 const n = min(left, rate);
38 self.state.extractBytes(buf[i..][0..n]);
39 if (left == 0) break;
40 self.state.permuteR(8);
41 i += n;
42 }
43 self.state.clear(0, rate);
44 self.state.permuteR(8);
45}