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 {...@@ -46,6 +46,7 @@ pub const auth = struct {
46/// Core functions, that should rarely be used directly by applications.46/// Core functions, that should rarely be used directly by applications.
47pub const core = struct {47pub const core = struct {
48 pub const aes = @import("crypto/aes.zig");48 pub const aes = @import("crypto/aes.zig");
49 pub const Ascon = @import("crypto/ascon.zig").State;
49 pub const Gimli = @import("crypto/gimli.zig").State;50 pub const Gimli = @import("crypto/gimli.zig").State;
50 pub const Xoodoo = @import("crypto/xoodoo.zig").State;51 pub const Xoodoo = @import("crypto/xoodoo.zig").State;
5152
...@@ -205,7 +206,9 @@ test {...@@ -205,7 +206,9 @@ test {
205 _ = auth.siphash;206 _ = auth.siphash;
206207
207 _ = core.aes;208 _ = core.aes;
209 _ = core.Ascon;
208 _ = core.Gimli;210 _ = core.Gimli;
211 _ = core.Xoodoo;
209 _ = core.modes;212 _ = core.modes;
210213
211 _ = dh.X25519;214 _ = 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 @@...@@ -1,9 +1,11 @@
1const std = @import("std");1const std = @import("std");
2const crypto = std.crypto;
2const debug = std.debug;3const debug = std.debug;
3const mem = std.mem;4const mem = std.mem;
4const math = std.math;5const math = std.math;
5const testing = std.testing;6const testing = std.testing;
6const AuthenticationError = std.crypto.errors.AuthenticationError;7const Ascon = crypto.core.Ascon(.Big);
8const AuthenticationError = crypto.errors.AuthenticationError;
79
8/// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks.10/// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks.
9/// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf11/// 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 {...@@ -25,90 +27,26 @@ pub const IsapA128A = struct {
25 const iv2 = [_]u8{ 0x02, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };27 const iv2 = [_]u8{ 0x02, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };
26 const iv3 = [_]u8{ 0x03, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };28 const iv3 = [_]u8{ 0x03, 0x80, 0x40, 0x01, 0x0c, 0x01, 0x06, 0x0c };
2729
28 const Block = [5]u64;30 st: Ascon,
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 }
9231
93 fn absorb(isap: *IsapA128A, m: []const u8) void {32 fn absorb(isap: *IsapA128A, m: []const u8) void {
94 var block = &isap.block;
95 var i: usize = 0;33 var i: usize = 0;
96 while (true) : (i += 8) {34 while (true) : (i += 8) {
97 const left = m.len - i;35 const left = m.len - i;
98 if (left >= 8) {36 if (left >= 8) {
99 block[0] ^= mem.readIntBig(u64, m[i..][0..8]);37 isap.st.addBytes(m[i..][0..8]);
100 isap.p12();38 isap.st.permute();
101 if (left == 8) {39 if (left == 8) {
102 block[0] ^= 0x8000000000000000;40 isap.st.addByte(0x80, 0);
103 isap.p12();41 isap.st.permute();
104 break;42 break;
105 }43 }
106 } else {44 } else {
107 var padded = [_]u8{0} ** 8;45 var padded = [_]u8{0} ** 8;
108 mem.copy(u8, padded[0..left], m[i..]);46 mem.copy(u8, padded[0..left], m[i..]);
109 padded[left] = 0x80;47 padded[left] = 0x80;
110 block[0] ^= mem.readIntBig(u64, padded[0..]);48 isap.st.addBytes(&padded);
111 isap.p12();49 isap.st.permute();
112 break;50 break;
113 }51 }
114 }52 }
...@@ -116,65 +54,59 @@ pub const IsapA128A = struct {...@@ -116,65 +54,59 @@ pub const IsapA128A = struct {
11654
117 fn trickle(k: [16]u8, iv: [8]u8, y: []const u8, comptime out_len: usize) [out_len]u8 {55 fn trickle(k: [16]u8, iv: [8]u8, y: []const u8, comptime out_len: usize) [out_len]u8 {
118 var isap = IsapA128A{56 var isap = IsapA128A{
119 .block = Block{57 .st = Ascon.initFromWords(.{
120 mem.readIntBig(u64, k[0..8]),58 mem.readIntBig(u64, k[0..8]),
121 mem.readIntBig(u64, k[8..16]),59 mem.readIntBig(u64, k[8..16]),
122 mem.readIntBig(u64, iv[0..8]),60 mem.readIntBig(u64, iv[0..8]),
123 0,61 0,
124 0,62 0,
125 },63 }),
126 };64 };
127 isap.p12();65 isap.st.permute();
12866
129 var i: usize = 0;67 var i: usize = 0;
130 while (i < y.len * 8 - 1) : (i += 1) {68 while (i < y.len * 8 - 1) : (i += 1) {
131 const cur_byte_pos = i / 8;69 const cur_byte_pos = i / 8;
132 const cur_bit_pos = @truncate(u3, 7 - (i % 8));70 const cur_bit_pos = @truncate(u3, 7 - (i % 8));
133 const cur_bit = @as(u64, ((y[cur_byte_pos] >> cur_bit_pos) & 1) << 7);71 const cur_bit = ((y[cur_byte_pos] >> cur_bit_pos) & 1) << 7;
134 isap.block[0] ^= cur_bit << 56;72 isap.st.addByte(cur_bit, 0);
135 isap.p1();73 isap.st.permuteR(1);
136 }74 }
137 const cur_bit = @as(u64, (y[y.len - 1] & 1) << 7);75 const cur_bit = (y[y.len - 1] & 1) << 7;
138 isap.block[0] ^= cur_bit << 56;76 isap.st.addByte(cur_bit, 0);
139 isap.p12();77 isap.st.permute();
14078
141 var out: [out_len]u8 = undefined;79 var out: [out_len]u8 = undefined;
142 var j: usize = 0;80 isap.st.extractBytes(&out);
143 while (j < out_len) : (j += 8) {81 isap.st.secureZero();
144 mem.writeIntBig(u64, out[j..][0..8], isap.block[j / 8]);
145 }
146 std.crypto.utils.secureZero(u64, &isap.block);
147 return out;82 return out;
148 }83 }
14984
150 fn mac(c: []const u8, ad: []const u8, npub: [16]u8, key: [16]u8) [16]u8 {85 fn mac(c: []const u8, ad: []const u8, npub: [16]u8, key: [16]u8) [16]u8 {
151 var isap = IsapA128A{86 var isap = IsapA128A{
152 .block = Block{87 .st = Ascon.initFromWords(.{
153 mem.readIntBig(u64, npub[0..8]),88 mem.readIntBig(u64, npub[0..8]),
154 mem.readIntBig(u64, npub[8..16]),89 mem.readIntBig(u64, npub[8..16]),
155 mem.readIntBig(u64, iv1[0..]),90 mem.readIntBig(u64, iv1[0..]),
156 0,91 0,
157 0,92 0,
158 },93 }),
159 };94 };
160 isap.p12();95 isap.st.permute();
16196
162 isap.absorb(ad);97 isap.absorb(ad);
163 isap.block[4] ^= 1;98 isap.st.addByte(1, Ascon.block_bytes - 1);
164 isap.absorb(c);99 isap.absorb(c);
165100
166 var y: [16]u8 = undefined;101 var y: [16]u8 = undefined;
167 mem.writeIntBig(u64, y[0..8], isap.block[0]);102 isap.st.extractBytes(&y);
168 mem.writeIntBig(u64, y[8..16], isap.block[1]);
169 const nb = trickle(key, iv2, y[0..], 16);103 const nb = trickle(key, iv2, y[0..], 16);
170 isap.block[0] = mem.readIntBig(u64, nb[0..8]);104 isap.st.setBytes(&nb);
171 isap.block[1] = mem.readIntBig(u64, nb[8..16]);105 isap.st.permute();
172 isap.p12();
173106
174 var tag: [16]u8 = undefined;107 var tag: [16]u8 = undefined;
175 mem.writeIntBig(u64, tag[0..8], isap.block[0]);108 isap.st.extractBytes(&tag);
176 mem.writeIntBig(u64, tag[8..16], isap.block[1]);109 isap.st.secureZero();
177 std.crypto.utils.secureZero(u64, &isap.block);
178 return tag;110 return tag;
179 }111 }
180112
...@@ -183,34 +115,31 @@ pub const IsapA128A = struct {...@@ -183,34 +115,31 @@ pub const IsapA128A = struct {
183115
184 const nb = trickle(key, iv3, npub[0..], 24);116 const nb = trickle(key, iv3, npub[0..], 24);
185 var isap = IsapA128A{117 var isap = IsapA128A{
186 .block = Block{118 .st = Ascon.initFromWords(.{
187 mem.readIntBig(u64, nb[0..8]),119 mem.readIntBig(u64, nb[0..8]),
188 mem.readIntBig(u64, nb[8..16]),120 mem.readIntBig(u64, nb[8..16]),
189 mem.readIntBig(u64, nb[16..24]),121 mem.readIntBig(u64, nb[16..24]),
190 mem.readIntBig(u64, npub[0..8]),122 mem.readIntBig(u64, npub[0..8]),
191 mem.readIntBig(u64, npub[8..16]),123 mem.readIntBig(u64, npub[8..16]),
192 },124 }),
193 };125 };
194 isap.p6();126 isap.st.permuteR(6);
195127
196 var i: usize = 0;128 var i: usize = 0;
197 while (true) : (i += 8) {129 while (true) : (i += 8) {
198 const left = in.len - i;130 const left = in.len - i;
199 if (left >= 8) {131 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]);
201 if (left == 8) {133 if (left == 8) {
202 break;134 break;
203 }135 }
204 isap.p6();136 isap.st.permuteR(6);
205 } else {137 } else {
206 var pad = [_]u8{0} ** 8;138 isap.st.xorBytes(out[i..], in[i..]);
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]);
210 break;139 break;
211 }140 }
212 }141 }
213 std.crypto.utils.secureZero(u64, &isap.block);142 isap.st.secureZero();
214 }143 }
215144
216 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {145 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 {...@@ -220,12 +149,9 @@ pub const IsapA128A = struct {
220149
221 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 {150 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 {
222 var computed_tag = mac(c, ad, npub, key);151 var computed_tag = mac(c, ad, npub, key);
223 var acc: u8 = 0;152 const res = crypto.utils.timingSafeEql([tag_length]u8, computed_tag, tag);
224 for (computed_tag) |_, j| {153 crypto.utils.secureZero(u8, &computed_tag);
225 acc |= (computed_tag[j] ^ tag[j]);154 if (!res) {
226 }
227 std.crypto.utils.secureZero(u8, &computed_tag);
228 if (acc != 0) {
229 return error.AuthenticationFailed;155 return error.AuthenticationFailed;
230 }156 }
231 xor(m, c, npub, key);157 xor(m, c, npub, key);
lib/std/rand.zig+2-1
...@@ -18,8 +18,9 @@ const maxInt = std.math.maxInt;...@@ -18,8 +18,9 @@ const maxInt = std.math.maxInt;
18pub const DefaultPrng = Xoshiro256;18pub const DefaultPrng = Xoshiro256;
1919
20/// Cryptographically secure random numbers.20/// Cryptographically secure random numbers.
21pub const DefaultCsprng = Xoodoo;21pub const DefaultCsprng = Ascon;
2222
23pub const Ascon = @import("rand/Ascon.zig");
23pub const Isaac64 = @import("rand/Isaac64.zig");24pub const Isaac64 = @import("rand/Isaac64.zig");
24pub const Xoodoo = @import("rand/Xoodoo.zig");25pub const Xoodoo = @import("rand/Xoodoo.zig");
25pub const Pcg = @import("rand/Pcg.zig");26pub 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}