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" {...@@ -417,6 +417,8 @@ test "issue #4532: no index out of bounds" {
417417
418/// Sets a slice to zeroes.418/// Sets a slice to zeroes.
419/// Prevents the store from being optimized out.419/// 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.
420pub fn secureZero(comptime T: type, s: []volatile T) void {422pub fn secureZero(comptime T: type, s: []volatile T) void {
421 @memset(s, std.mem.zeroes(T));423 @memset(s, std.mem.zeroes(T));
422}424}
lib/std/crypto/aegis.zig+2-4
...@@ -351,10 +351,9 @@ fn Aegis128XGeneric(comptime degree: u7, comptime tag_bits: u9) type {...@@ -351,10 +351,9 @@ fn Aegis128XGeneric(comptime degree: u7, comptime tag_bits: u9) type {
351 if (m.len % block_length != 0) {351 if (m.len % block_length != 0) {
352 state.decLast(m[i..], c[i..]);352 state.decLast(m[i..], c[i..]);
353 }353 }
354 var computed_tag = state.finalize(tag_bits, ad.len, m.len);354 const computed_tag = state.finalize(tag_bits, ad.len, m.len);
355 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);355 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
356 if (!verify) {356 if (!verify) {
357 crypto.secureZero(u8, &computed_tag);
358 @memset(m, undefined);357 @memset(m, undefined);
359 return error.AuthenticationFailed;358 return error.AuthenticationFailed;
360 }359 }
...@@ -646,10 +645,9 @@ fn Aegis256XGeneric(comptime degree: u7, comptime tag_bits: u9) type {...@@ -646,10 +645,9 @@ fn Aegis256XGeneric(comptime degree: u7, comptime tag_bits: u9) type {
646 if (m.len % block_length != 0) {645 if (m.len % block_length != 0) {
647 state.decLast(m[i..], c[i..]);646 state.decLast(m[i..], c[i..]);
648 }647 }
649 var computed_tag = state.finalize(tag_bits, ad.len, m.len);648 const computed_tag = state.finalize(tag_bits, ad.len, m.len);
650 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);649 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
651 if (!verify) {650 if (!verify) {
652 crypto.secureZero(u8, &computed_tag);
653 @memset(m, undefined);651 @memset(m, undefined);
654 return error.AuthenticationFailed;652 return error.AuthenticationFailed;
655 }653 }
lib/std/crypto/aes_ccm.zig-10
...@@ -91,9 +91,6 @@ fn AesCcm(comptime BlockCipher: type, comptime tag_len: usize, comptime nonce_le...@@ -91,9 +91,6 @@ fn AesCcm(comptime BlockCipher: type, comptime tag_len: usize, comptime nonce_le
91 for (tag, mac_result[0..tag_length], s0[0..tag_length]) |*t, mac_byte, s_byte| {91 for (tag, mac_result[0..tag_length], s0[0..tag_length]) |*t, mac_byte, s_byte| {
92 t.* = mac_byte ^ s_byte;92 t.* = mac_byte ^ s_byte;
93 }93 }
94
95 crypto.secureZero(u8, &mac_result);
96 crypto.secureZero(u8, &s0);
97 }94 }
9895
99 // Encrypt the plaintext using CTR mode (starting from counter = 1)96 // 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...@@ -153,16 +150,9 @@ fn AesCcm(comptime BlockCipher: type, comptime tag_len: usize, comptime nonce_le
153 // Constant-time tag comparison150 // Constant-time tag comparison
154 const valid = crypto.timing_safe.eql([tag_length]u8, expected_mac, tag);151 const valid = crypto.timing_safe.eql([tag_length]u8, expected_mac, tag);
155 if (!valid) {152 if (!valid) {
156 crypto.secureZero(u8, &expected_mac);
157 crypto.secureZero(u8, &mac_result);
158 crypto.secureZero(u8, &s0);
159 crypto.secureZero(u8, m);153 crypto.secureZero(u8, m);
160 return error.AuthenticationFailed;154 return error.AuthenticationFailed;
161 }155 }
162
163 crypto.secureZero(u8, &expected_mac);
164 crypto.secureZero(u8, &mac_result);
165 crypto.secureZero(u8, &s0);
166 }156 }
167 }157 }
168158
lib/std/crypto/aes_gcm.zig-1
...@@ -101,7 +101,6 @@ fn AesGcm(comptime Aes: anytype) type {...@@ -101,7 +101,6 @@ fn AesGcm(comptime Aes: anytype) type {
101101
102 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);102 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
103 if (!verify) {103 if (!verify) {
104 crypto.secureZero(u8, &computed_tag);
105 @memset(m, undefined);104 @memset(m, undefined);
106 return error.AuthenticationFailed;105 return error.AuthenticationFailed;
107 }106 }
lib/std/crypto/aes_gcm_siv.zig-1
...@@ -218,7 +218,6 @@ fn AesGcmSiv(comptime Aes: anytype) type {...@@ -218,7 +218,6 @@ fn AesGcmSiv(comptime Aes: anytype) type {
218 // Verify tag218 // Verify tag
219 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);219 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
220 if (!verify) {220 if (!verify) {
221 crypto.secureZero(u8, &computed_tag);
222 @memset(m, undefined);221 @memset(m, undefined);
223 return error.AuthenticationFailed;222 return error.AuthenticationFailed;
224 }223 }
lib/std/crypto/aes_ocb.zig+1-2
...@@ -236,10 +236,9 @@ fn AesOcb(comptime Aes: anytype) type {...@@ -236,10 +236,9 @@ fn AesOcb(comptime Aes: anytype) type {
236 }236 }
237 var e = xorBlocks(xorBlocks(sum, offset), lx.dol);237 var e = xorBlocks(xorBlocks(sum, offset), lx.dol);
238 aes_enc_ctx.encrypt(&e, &e);238 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));
240 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);240 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
241 if (!verify) {241 if (!verify) {
242 crypto.secureZero(u8, &computed_tag);
243 @memset(m, undefined);242 @memset(m, undefined);
244 return error.AuthenticationFailed;243 return error.AuthenticationFailed;
245 }244 }
lib/std/crypto/aes_siv.zig-2
...@@ -211,7 +211,6 @@ fn AesSiv(comptime Aes: anytype) type {...@@ -211,7 +211,6 @@ fn AesSiv(comptime Aes: anytype) type {
211 // Verify tag211 // Verify tag
212 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);212 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
213 if (!verify) {213 if (!verify) {
214 crypto.secureZero(u8, &computed_tag);
215 @memset(m, undefined);214 @memset(m, undefined);
216 return error.AuthenticationFailed;215 return error.AuthenticationFailed;
217 }216 }
...@@ -290,7 +289,6 @@ fn AesSiv(comptime Aes: anytype) type {...@@ -290,7 +289,6 @@ fn AesSiv(comptime Aes: anytype) type {
290 // Verify tag289 // Verify tag
291 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);290 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
292 if (!verify) {291 if (!verify) {
293 crypto.secureZero(u8, &computed_tag);
294 @memset(m, undefined);292 @memset(m, undefined);
295 return error.AuthenticationFailed;293 return error.AuthenticationFailed;
296 }294 }
lib/std/crypto/bcrypt.zig+2-3
...@@ -556,8 +556,7 @@ const pbkdf_prf = struct {...@@ -556,8 +556,7 @@ const pbkdf_prf = struct {
556 std.mem.writeInt(u32, out[4 * i ..][0..4], v, .little);556 std.mem.writeInt(u32, out[4 * i ..][0..4], v, .little);
557 }557 }
558558
559 // zap559 // Clear the key schedule, which is already stored in memory.
560 crypto.secureZero(u32, &cdata);
561 crypto.secureZero(u32, &state.subkeys);560 crypto.secureZero(u32, &state.subkeys);
562561
563 return out;562 return out;
...@@ -611,8 +610,8 @@ pub fn opensshKdf(pass: []const u8, salt: []const u8, key: []u8, rounds: u32) !v...@@ -611,8 +610,8 @@ pub fn opensshKdf(pass: []const u8, salt: []const u8, key: []u8, rounds: u32) !v
611 key[dest] = tmp[i];610 key[dest] = tmp[i];
612 } else amt;611 } else amt;
613 }612 }
613 // Unlike these buffers, tmp2 can stay in registers; wiping it can force an extra copy into memory.
614 crypto.secureZero(u8, &tmp);614 crypto.secureZero(u8, &tmp);
615 crypto.secureZero(u8, &tmp2);
616 crypto.secureZero(u8, &sha2pass);615 crypto.secureZero(u8, &sha2pass);
617}616}
618617
lib/std/crypto/chacha20.zig-1
...@@ -711,7 +711,6 @@ fn ChaChaPoly1305(comptime rounds_nb: usize) type {...@@ -711,7 +711,6 @@ fn ChaChaPoly1305(comptime rounds_nb: usize) type {
711711
712 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);712 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
713 if (!verify) {713 if (!verify) {
714 crypto.secureZero(u8, &computed_tag);
715 @memset(m, undefined);714 @memset(m, undefined);
716 return error.AuthenticationFailed;715 return error.AuthenticationFailed;
717 }716 }
lib/std/crypto/isap.zig+1-5
...@@ -78,7 +78,6 @@ pub const IsapA128A = struct {...@@ -78,7 +78,6 @@ pub const IsapA128A = struct {
7878
79 var out: [out_len]u8 = undefined;79 var out: [out_len]u8 = undefined;
80 isap.st.extractBytes(&out);80 isap.st.extractBytes(&out);
81 isap.st.secureZero();
82 return out;81 return out;
83 }82 }
8483
...@@ -106,7 +105,6 @@ pub const IsapA128A = struct {...@@ -106,7 +105,6 @@ pub const IsapA128A = struct {
106105
107 var tag: [16]u8 = undefined;106 var tag: [16]u8 = undefined;
108 isap.st.extractBytes(&tag);107 isap.st.extractBytes(&tag);
109 isap.st.secureZero();
110 return tag;108 return tag;
111 }109 }
112110
...@@ -139,7 +137,6 @@ pub const IsapA128A = struct {...@@ -139,7 +137,6 @@ pub const IsapA128A = struct {
139 break;137 break;
140 }138 }
141 }139 }
142 isap.st.secureZero();
143 }140 }
144141
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 {142 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 {...@@ -157,10 +154,9 @@ pub const IsapA128A = struct {
157 ///154 ///
158 /// Contents of `m` are undefined if an error is returned.155 /// Contents of `m` are undefined if an error is returned.
159 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 {156 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);
161 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);158 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
162 if (!verify) {159 if (!verify) {
163 crypto.secureZero(u8, &computed_tag);
164 @memset(m, undefined);160 @memset(m, undefined);
165 return error.AuthenticationFailed;161 return error.AuthenticationFailed;
166 }162 }
lib/std/crypto/salsa20.zig-1
...@@ -436,7 +436,6 @@ pub const XSalsa20Poly1305 = struct {...@@ -436,7 +436,6 @@ pub const XSalsa20Poly1305 = struct {
436436
437 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);437 const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag);
438 if (!verify) {438 if (!verify) {
439 crypto.secureZero(u8, &computed_tag);
440 @memset(m, undefined);439 @memset(m, undefined);
441 return error.AuthenticationFailed;440 return error.AuthenticationFailed;
442 }441 }