authorgravatar for 124872+jedisct1@users.noreply.github.comFrank Denis <124872+jedisct1@users.noreply.github.com> 2026-10-15 21:21:33+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
log76789ff59c8a63d75ffcb556cc05713ebbea4c0c
treeb77850dd04b5a0ebb2ab7a8da2a6c06cda51bbf4
parent705a6b4efb7b19feb7d2ee86f2ac6e56653cb923

std.crypto: remove secureZero() calls doing more harm than good

Calling secureZero() on secrets that fit in a register can create copies of that didn't originally exist: https://00f.net/2026/10/06/zeroization-1/ Remove the wipes where this currently happens. We need a better solution to this.

10 files changed, 6 insertions(+), 30 deletions(-)

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 }