authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-10-19 00:56:14+02:00
committergravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-10-19 00:56:14+02:00
logfd0dd6c2a8f98fdb8413f04f90cebe704f429f49
tree0e5114396550b9f5e0d77bf9e564080c3ae525a3
parent705a6b4efb7b19feb7d2ee86f2ac6e56653cb923
parent9552ca4c2e9a478fa9187b1eaf64d82c7c2d2b84

Merge pull request 'std.crypto: remove secureZero() calls doing more harm than good' (#37264) from jedisct1/zig:removezero into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/37264 Reviewed-by: Andrew Kelley <andrew@ziglang.org>

11 files changed, 8 insertions(+), 30 deletions(-)

lib/std/crypto.zig+2
......@@ -417,6 +417,8 @@ test "issue #4532: no index out of bounds" {
417417
418418/// Sets a slice to zeroes.
419419/// Prevents the store from being optimized out.
420/// Does not clear copies in registers or elsewhere in memory.
421/// Wiping a value held in registers can force an extra copy into memory.
420422pub fn secureZero(comptime T: type, s: []volatile T) void {
421423 @memset(s, std.mem.zeroes(T));
422424}
lib/std/crypto/aegis.zig+2-4
......@@ -351,10 +351,9 @@ fn Aegis128XGeneric(comptime degree: u7, comptime tag_bits: u9) type {
351351 if (m.len % block_length != 0) {
352352 state.decLast(m[i..], c[i..]);
353353 }
354 var computed_tag = state.finalize(tag_bits, ad.len, m.len);
354 const computed_tag = state.finalize(tag_bits, ad.len, m.len);
355355 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
356356 if (!verify) {
357 crypto.secureZero(u8, &computed_tag);
358357 @memset(m, undefined);
359358 return error.AuthenticationFailed;
360359 }
......@@ -646,10 +645,9 @@ fn Aegis256XGeneric(comptime degree: u7, comptime tag_bits: u9) type {
646645 if (m.len % block_length != 0) {
647646 state.decLast(m[i..], c[i..]);
648647 }
649 var computed_tag = state.finalize(tag_bits, ad.len, m.len);
648 const computed_tag = state.finalize(tag_bits, ad.len, m.len);
650649 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
651650 if (!verify) {
652 crypto.secureZero(u8, &computed_tag);
653651 @memset(m, undefined);
654652 return error.AuthenticationFailed;
655653 }
lib/std/crypto/aes_ccm.zig-10
......@@ -91,9 +91,6 @@ fn AesCcm(comptime BlockCipher: type, comptime tag_len: usize, comptime nonce_le
9191 for (tag, mac_result[0..tag_length], s0[0..tag_length]) |*t, mac_byte, s_byte| {
9292 t.* = mac_byte ^ s_byte;
9393 }
94
95 crypto.secureZero(u8, &mac_result);
96 crypto.secureZero(u8, &s0);
9794 }
9895
9996 // Encrypt the plaintext using CTR mode (starting from counter = 1)
......@@ -153,16 +150,9 @@ fn AesCcm(comptime BlockCipher: type, comptime tag_len: usize, comptime nonce_le
153150 // Constant-time tag comparison
154151 const valid = crypto.timing_safe.eql([tag_length]u8, expected_mac, tag);
155152 if (!valid) {
156 crypto.secureZero(u8, &expected_mac);
157 crypto.secureZero(u8, &mac_result);
158 crypto.secureZero(u8, &s0);
159153 crypto.secureZero(u8, m);
160154 return error.AuthenticationFailed;
161155 }
162
163 crypto.secureZero(u8, &expected_mac);
164 crypto.secureZero(u8, &mac_result);
165 crypto.secureZero(u8, &s0);
166156 }
167157 }
168158
lib/std/crypto/aes_gcm.zig-1
......@@ -101,7 +101,6 @@ fn AesGcm(comptime Aes: anytype) type {
101101
102102 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
103103 if (!verify) {
104 crypto.secureZero(u8, &computed_tag);
105104 @memset(m, undefined);
106105 return error.AuthenticationFailed;
107106 }
lib/std/crypto/aes_gcm_siv.zig-1
......@@ -218,7 +218,6 @@ fn AesGcmSiv(comptime Aes: anytype) type {
218218 // Verify tag
219219 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
220220 if (!verify) {
221 crypto.secureZero(u8, &computed_tag);
222221 @memset(m, undefined);
223222 return error.AuthenticationFailed;
224223 }
lib/std/crypto/aes_ocb.zig+1-2
......@@ -236,10 +236,9 @@ fn AesOcb(comptime Aes: anytype) type {
236236 }
237237 var e = xorBlocks(xorBlocks(sum, offset), lx.dol);
238238 aes_enc_ctx.encrypt(&e, &e);
239 var computed_tag = xorBlocks(e, hash(aes_enc_ctx, &lx, ad));
239 const computed_tag = xorBlocks(e, hash(aes_enc_ctx, &lx, ad));
240240 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
241241 if (!verify) {
242 crypto.secureZero(u8, &computed_tag);
243242 @memset(m, undefined);
244243 return error.AuthenticationFailed;
245244 }
lib/std/crypto/aes_siv.zig-2
......@@ -211,7 +211,6 @@ fn AesSiv(comptime Aes: anytype) type {
211211 // Verify tag
212212 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
213213 if (!verify) {
214 crypto.secureZero(u8, &computed_tag);
215214 @memset(m, undefined);
216215 return error.AuthenticationFailed;
217216 }
......@@ -290,7 +289,6 @@ fn AesSiv(comptime Aes: anytype) type {
290289 // Verify tag
291290 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
292291 if (!verify) {
293 crypto.secureZero(u8, &computed_tag);
294292 @memset(m, undefined);
295293 return error.AuthenticationFailed;
296294 }
lib/std/crypto/bcrypt.zig+2-3
......@@ -556,8 +556,7 @@ const pbkdf_prf = struct {
556556 std.mem.writeInt(u32, out[4 * i ..][0..4], v, .little);
557557 }
558558
559 // zap
560 crypto.secureZero(u32, &cdata);
559 // Clear the key schedule, which is already stored in memory.
561560 crypto.secureZero(u32, &state.subkeys);
562561
563562 return out;
......@@ -611,8 +610,8 @@ pub fn opensshKdf(pass: []const u8, salt: []const u8, key: []u8, rounds: u32) !v
611610 key[dest] = tmp[i];
612611 } else amt;
613612 }
613 // Unlike these buffers, tmp2 can stay in registers; wiping it can force an extra copy into memory.
614614 crypto.secureZero(u8, &tmp);
615 crypto.secureZero(u8, &tmp2);
616615 crypto.secureZero(u8, &sha2pass);
617616}
618617
lib/std/crypto/chacha20.zig-1
......@@ -711,7 +711,6 @@ fn ChaChaPoly1305(comptime rounds_nb: usize) type {
711711
712712 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
713713 if (!verify) {
714 crypto.secureZero(u8, &computed_tag);
715714 @memset(m, undefined);
716715 return error.AuthenticationFailed;
717716 }
lib/std/crypto/isap.zig+1-5
......@@ -78,7 +78,6 @@ pub const IsapA128A = struct {
7878
7979 var out: [out_len]u8 = undefined;
8080 isap.st.extractBytes(&out);
81 isap.st.secureZero();
8281 return out;
8382 }
8483
......@@ -106,7 +105,6 @@ pub const IsapA128A = struct {
106105
107106 var tag: [16]u8 = undefined;
108107 isap.st.extractBytes(&tag);
109 isap.st.secureZero();
110108 return tag;
111109 }
112110
......@@ -139,7 +137,6 @@ pub const IsapA128A = struct {
139137 break;
140138 }
141139 }
142 isap.st.secureZero();
143140 }
144141
145142 pub fn encrypt(c: []u8, tag: *[tag_length]u8, m: []const u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) void {
......@@ -157,10 +154,9 @@ pub const IsapA128A = struct {
157154 ///
158155 /// Contents of `m` are undefined if an error is returned.
159156 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 {
160 var computed_tag = mac(c, ad, npub, key);
157 const computed_tag = mac(c, ad, npub, key);
161158 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
162159 if (!verify) {
163 crypto.secureZero(u8, &computed_tag);
164160 @memset(m, undefined);
165161 return error.AuthenticationFailed;
166162 }
lib/std/crypto/salsa20.zig-1
......@@ -436,7 +436,6 @@ pub const XSalsa20Poly1305 = struct {
436436
437437 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
438438 if (!verify) {
439 crypto.secureZero(u8, &computed_tag);
440439 @memset(m, undefined);
441440 return error.AuthenticationFailed;
442441 }