From 76789ff59c8a63d75ffcb556cc05713ebbea4c0c Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Thu, 15 Oct 2026 21:21:33 +0200 Subject: [PATCH] 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. --- lib/std/crypto/aegis.zig | 6 ++---- lib/std/crypto/aes_ccm.zig | 10 ---------- lib/std/crypto/aes_gcm.zig | 1 - lib/std/crypto/aes_gcm_siv.zig | 1 - lib/std/crypto/aes_ocb.zig | 3 +-- lib/std/crypto/aes_siv.zig | 2 -- lib/std/crypto/bcrypt.zig | 5 ++--- lib/std/crypto/chacha20.zig | 1 - lib/std/crypto/isap.zig | 6 +----- lib/std/crypto/salsa20.zig | 1 - 10 files changed, 6 insertions(+), 30 deletions(-) diff --git a/lib/std/crypto/aegis.zig b/lib/std/crypto/aegis.zig index fa8cbe66038c5beb2ff07de158cef8a343ee895a..92eb6d678c9555c9a7138e1c160232df04ebfcb3 100644 --- a/lib/std/crypto/aegis.zig +++ b/lib/std/crypto/aegis.zig @@ -351,10 +351,9 @@ fn Aegis128XGeneric(comptime degree: u7, comptime tag_bits: u9) type { if (m.len % block_length != 0) { state.decLast(m[i..], c[i..]); } - var computed_tag = state.finalize(tag_bits, ad.len, m.len); + const computed_tag = state.finalize(tag_bits, ad.len, m.len); const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); if (!verify) { - crypto.secureZero(u8, &computed_tag); @memset(m, undefined); return error.AuthenticationFailed; } @@ -646,10 +645,9 @@ fn Aegis256XGeneric(comptime degree: u7, comptime tag_bits: u9) type { if (m.len % block_length != 0) { state.decLast(m[i..], c[i..]); } - var computed_tag = state.finalize(tag_bits, ad.len, m.len); + const computed_tag = state.finalize(tag_bits, ad.len, m.len); const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); if (!verify) { - crypto.secureZero(u8, &computed_tag); @memset(m, undefined); return error.AuthenticationFailed; } diff --git a/lib/std/crypto/aes_ccm.zig b/lib/std/crypto/aes_ccm.zig index 69a8f776eacfa0b587900bfd5fb1ec2508e2dd44..9743a6df703ac8ac710ea81e36edc36d83630d36 100644 --- a/lib/std/crypto/aes_ccm.zig +++ b/lib/std/crypto/aes_ccm.zig @@ -91,9 +91,6 @@ fn AesCcm(comptime BlockCipher: type, comptime tag_len: usize, comptime nonce_le for (tag, mac_result[0..tag_length], s0[0..tag_length]) |*t, mac_byte, s_byte| { t.* = mac_byte ^ s_byte; } - - crypto.secureZero(u8, &mac_result); - crypto.secureZero(u8, &s0); } // 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 // Constant-time tag comparison const valid = crypto.timing_safe.eql([tag_length]u8, expected_mac, tag); if (!valid) { - crypto.secureZero(u8, &expected_mac); - crypto.secureZero(u8, &mac_result); - crypto.secureZero(u8, &s0); crypto.secureZero(u8, m); return error.AuthenticationFailed; } - - crypto.secureZero(u8, &expected_mac); - crypto.secureZero(u8, &mac_result); - crypto.secureZero(u8, &s0); } } diff --git a/lib/std/crypto/aes_gcm.zig b/lib/std/crypto/aes_gcm.zig index d4e3e3168571ea55438b295c5be787a7f8e54e0b..76e686b0552dfcbf576654592b512916c7574581 100644 --- a/lib/std/crypto/aes_gcm.zig +++ b/lib/std/crypto/aes_gcm.zig @@ -101,7 +101,6 @@ fn AesGcm(comptime Aes: anytype) type { const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); if (!verify) { - crypto.secureZero(u8, &computed_tag); @memset(m, undefined); return error.AuthenticationFailed; } diff --git a/lib/std/crypto/aes_gcm_siv.zig b/lib/std/crypto/aes_gcm_siv.zig index 956c328fec181422a3955d16d971d11f62fbd713..1175708f1e74236abcd308ea21e1ec1d03ca47b1 100644 --- a/lib/std/crypto/aes_gcm_siv.zig +++ b/lib/std/crypto/aes_gcm_siv.zig @@ -218,7 +218,6 @@ fn AesGcmSiv(comptime Aes: anytype) type { // Verify tag const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); if (!verify) { - crypto.secureZero(u8, &computed_tag); @memset(m, undefined); return error.AuthenticationFailed; } diff --git a/lib/std/crypto/aes_ocb.zig b/lib/std/crypto/aes_ocb.zig index b83400e5c3bbd2443f03130f3fcb0fb4501d59db..b694eda00f31eb16a1b8afcf54cf4e8e07896f5e 100644 --- a/lib/std/crypto/aes_ocb.zig +++ b/lib/std/crypto/aes_ocb.zig @@ -236,10 +236,9 @@ fn AesOcb(comptime Aes: anytype) type { } var e = xorBlocks(xorBlocks(sum, offset), lx.dol); aes_enc_ctx.encrypt(&e, &e); - var computed_tag = xorBlocks(e, hash(aes_enc_ctx, &lx, ad)); + const computed_tag = xorBlocks(e, hash(aes_enc_ctx, &lx, ad)); const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); if (!verify) { - crypto.secureZero(u8, &computed_tag); @memset(m, undefined); return error.AuthenticationFailed; } diff --git a/lib/std/crypto/aes_siv.zig b/lib/std/crypto/aes_siv.zig index c60e361462b10e68e996d5eb4ae5edb76078a56b..b1e61d430202f5c2a9b691d064814c10ecb7ef88 100644 --- a/lib/std/crypto/aes_siv.zig +++ b/lib/std/crypto/aes_siv.zig @@ -211,7 +211,6 @@ fn AesSiv(comptime Aes: anytype) type { // Verify tag const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); if (!verify) { - crypto.secureZero(u8, &computed_tag); @memset(m, undefined); return error.AuthenticationFailed; } @@ -290,7 +289,6 @@ fn AesSiv(comptime Aes: anytype) type { // Verify tag const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); if (!verify) { - crypto.secureZero(u8, &computed_tag); @memset(m, undefined); return error.AuthenticationFailed; } diff --git a/lib/std/crypto/bcrypt.zig b/lib/std/crypto/bcrypt.zig index 2ebf9319e61b2aab7d97a44ae7992231355de24d..813bb2ec41b6f15c68e5ec7274f215b07512bb4b 100644 --- a/lib/std/crypto/bcrypt.zig +++ b/lib/std/crypto/bcrypt.zig @@ -556,8 +556,7 @@ const pbkdf_prf = struct { std.mem.writeInt(u32, out[4 * i ..][0..4], v, .little); } - // zap - crypto.secureZero(u32, &cdata); + // Clear the key schedule, which is already stored in memory. crypto.secureZero(u32, &state.subkeys); return out; @@ -611,8 +610,8 @@ pub fn opensshKdf(pass: []const u8, salt: []const u8, key: []u8, rounds: u32) !v key[dest] = tmp[i]; } else amt; } + // Unlike these buffers, tmp2 can stay in registers; wiping it can force an extra copy into memory. crypto.secureZero(u8, &tmp); - crypto.secureZero(u8, &tmp2); crypto.secureZero(u8, &sha2pass); } diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig index 2da1bf5e2b99f3df6c08bbe04f6306ca35d43b32..7c1220e34ef689aba77083c5605fb919dab93abb 100644 --- a/lib/std/crypto/chacha20.zig +++ b/lib/std/crypto/chacha20.zig @@ -711,7 +711,6 @@ fn ChaChaPoly1305(comptime rounds_nb: usize) type { const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); if (!verify) { - crypto.secureZero(u8, &computed_tag); @memset(m, undefined); return error.AuthenticationFailed; } diff --git a/lib/std/crypto/isap.zig b/lib/std/crypto/isap.zig index 40aeb608db50f12a492faafee9717cc5ff4b4798..072bf1aee855d6d5db24fc12c96b5be76f2d6d39 100644 --- a/lib/std/crypto/isap.zig +++ b/lib/std/crypto/isap.zig @@ -78,7 +78,6 @@ pub const IsapA128A = struct { var out: [out_len]u8 = undefined; isap.st.extractBytes(&out); - isap.st.secureZero(); return out; } @@ -106,7 +105,6 @@ pub const IsapA128A = struct { var tag: [16]u8 = undefined; isap.st.extractBytes(&tag); - isap.st.secureZero(); return tag; } @@ -139,7 +137,6 @@ pub const IsapA128A = struct { break; } } - isap.st.secureZero(); } 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 { /// /// Contents of `m` are undefined if an error is returned. 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 { - var computed_tag = mac(c, ad, npub, key); + const computed_tag = mac(c, ad, npub, key); const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); if (!verify) { - crypto.secureZero(u8, &computed_tag); @memset(m, undefined); return error.AuthenticationFailed; } diff --git a/lib/std/crypto/salsa20.zig b/lib/std/crypto/salsa20.zig index 3d4ec106063cd16f820be464df9e053c21d376b0..98f5f242ff7f7aee33944da113c603b18814f267 100644 --- a/lib/std/crypto/salsa20.zig +++ b/lib/std/crypto/salsa20.zig @@ -436,7 +436,6 @@ pub const XSalsa20Poly1305 = struct { const verify = crypto.timing_safe.eql([tag_length]u8, computed_tag, tag); if (!verify) { - crypto.secureZero(u8, &computed_tag); @memset(m, undefined); return error.AuthenticationFailed; } -- 2.54.0