From 1c8e940d57fb346b06b5a63a887ed514879e841e Mon Sep 17 00:00:00 2001 From: saleel Date: Thu, 18 Jun 2026 13:03:34 +0400 Subject: [PATCH] fix: zero padding/length region in digest_var MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `digest_var` copied the BoundedVec's entire backing store (`for i in 0..N`) into the padded message, then only overwrote the 0x80 marker and the length bytes. Bytes in the backing store beyond `len()` are unconstrained witness data, so any non-zero value there landed in the SHA padding region and silently altered the digest — the padding region was only ever zero by convention, never by constraint. Gate the copy on `i < msg_length` so the padding/length region is forced to zero regardless of the witness, matching noir-lang/sha256 v0.3.0 which already ignores input bytes past the message length. Adds a regression test (`test_dirty_padding_ignored`) that hashes "abc" from a BoundedVec whose tail is filled with 0xff via `from_parts_unchecked`; it fails on the old code and passes with the fix. --- src/lib.nr | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/lib.nr b/src/lib.nr index 04a83c2..664464e 100644 --- a/src/lib.nr +++ b/src/lib.nr @@ -140,13 +140,13 @@ fn digest_var(msg: BoundedVec) -> [u8; 64] * SHA512_BLOCK_SIZE ]; + let msg_length = msg.len(); + let msg_text = msg.storage(); for i in 0..N { - padded_msg[i] = msg_text[i]; + padded_msg[i] = if i < msg_length { msg_text[i] } else { 0 }; } - let msg_length = msg.len(); - let num_used_blocks = (msg_length + SHA512_LENGTH_PARAMETER_BYTES + SHA512_BLOCK_SIZE) / SHA512_BLOCK_SIZE; @@ -285,6 +285,23 @@ pub mod sha512 { assert_eq(result_var, expected); } + #[test] + fn test_dirty_padding_ignored() { + let mut dirty: [u8; 256] = [0xff; 256]; + dirty[0] = 0x61; // 'a' + dirty[1] = 0x62; // 'b' + dirty[2] = 0x63; // 'c' + let result_var = sha512_var(BoundedVec::::from_parts_unchecked(dirty, 3)); + let expected: [u8; 64] = [ + 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, + 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6, + 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, + 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, + 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f, + ]; + assert_eq(result_var, expected); + } + } pub mod sha384 {