Skip to content

Commit c642ef6

Browse files
리뷰 반영
- 비공개 mlkem compress 함수 fill(0) 제거 (성능) - ct conditional_copy_bytes fill(0) 제거 (constant-time 가정 위반 가능) - core traits의 _out 함수 docstring에 출력 버퍼 zeroize 명시 - sha3 테스트 주석에서 이름과 변경 이력 제거 - 릴리즈 노트의 완료된 TODO 항목을 changelog로 이동
1 parent ef214a6 commit c642ef6

7 files changed

Lines changed: 23 additions & 14 deletions

File tree

alpha_0.1.2_release_notes.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
* Check out Megan's email May 13 about KeyMaterial: "I was wondering if there might be scope for a closure based
1313
approach that could
1414
guarantee encapsulation of the state change from safe to hazardous back to safe again."
15-
* Anywhere that you have an `_out(.. out: &mut [u8])`, start by zeroizing it with .fill(0); .. a good task for Claude?
16-
And should be documented in the style guide?
1715
* Go back to previous algs and apply memory optimization tricks like internal functions. And add a docs section "Memory
1816
Usage" that measures with valgrind.
1917
* Ensure that all crates have `#![forbid(missing_docs)]`
@@ -54,5 +52,7 @@
5452

5553
* ML-DSA
5654
* Low-Memory ML-DSA -- runs in about 1/10th of the usual memory (~ 30 kb of stack) with only minor performance impact.
55+
* All public `*_out(.., out: &mut [u8])` functions now begin by zeroizing the entire output buffer with `.fill(0)`,
56+
preventing exposure of stale data in oversized output buffers or on early error returns.
5757
* Github issues resolved:
5858
* #2, or whatever

crypto/core/src/traits.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub trait Hash : Default {
3030

3131
/// A static one-shot API that hashes the provided data into the provided output slice.
3232
/// `data` can be of any length, including zero bytes.
33+
/// The entire output buffer is zeroized before the hash output is written.
3334
/// The return value is the number of bytes written.
3435
fn hash_out(self, data: &[u8], output: &mut [u8]) -> usize;
3536

@@ -50,6 +51,8 @@ pub trait Hash : Default {
5051
/// If the provided buffer is smaller than the hash's output length, the output will be truncated.
5152
/// If the provided buffor is larger than the hash's output length, the output will be placed in
5253
/// the first [Hash::output_len] bytes.
54+
/// The entire output buffer is zeroized before the hash output is written, so any bytes past
55+
/// [Hash::output_len] will be 0.
5356
///
5457
/// The return value is the number of bytes written.
5558
fn do_final_out(self, output: &mut [u8]) -> usize;
@@ -65,6 +68,7 @@ pub trait Hash : Default {
6568
/// The same as [Hash::do_final_out], but allows for supplying a partial byte as the last input.
6669
/// Assumes that the input is in the least significant bits (big endian).
6770
/// will be placed in the first [Hash::output_len] bytes.
71+
/// The entire output buffer is zeroized before the hash output is written.
6872
/// The return value is the number of bytes written.
6973
fn do_final_partial_bits_out(
7074
self,
@@ -208,6 +212,7 @@ pub trait KEMPublicKey<const PK_LEN: usize> : PartialEq + Eq + Clone + Debug + D
208212
/// Write it out to bytes in its standard encoding.
209213
fn encode(&self) -> [u8; PK_LEN];
210214
/// Write it out to bytes in its standard encoding.
215+
/// The entire output buffer is zeroized before the encoding is written.
211216
fn encode_out(&self, out: &mut [u8; PK_LEN]) -> usize;
212217
/// Read it in from bytes in its standard encoding.
213218
fn from_bytes(bytes: &[u8]) -> Result<Self, KEMError>;
@@ -218,6 +223,7 @@ pub trait KEMPrivateKey<const SK_LEN: usize> : PartialEq + Eq + Clone + Secret +
218223
/// Write it out to bytes in its standard encoding.
219224
fn encode(&self) -> [u8; SK_LEN];
220225
/// Write it out to bytes in its standard encoding.
226+
/// The entire output buffer is zeroized before the encoding is written.
221227
fn encode_out(&self, out: &mut [u8; SK_LEN]) -> usize;
222228
/// Read it in from bytes in its standard encoding.
223229
fn from_bytes(bytes: &[u8]) -> Result<Self, KEMError>;
@@ -293,6 +299,8 @@ pub trait MAC: Sized {
293299
/// Depending on the underlying MAC implementation, NIST may require that the library enforce
294300
/// a minimum length on the mac output value. See documentation for the underlying implementation
295301
/// to see conditions under which it throws [MACError::InvalidLength].
302+
///
303+
/// The entire output buffer is zeroized before the MAC value is written.
296304
fn mac_out(self, data: &[u8],out: &mut [u8]) -> Result<usize, MACError>;
297305

298306
/// One-shot API that verifies a MAC for the provided data.
@@ -318,6 +326,8 @@ pub trait MAC: Sized {
318326
/// Depending on the underlying MAC implementation, NIST may require that the library enforce
319327
/// a minimum length on the mac output value. See documentation for the underlying implementation
320328
/// to see conditions under which it throws [MACError::InvalidLength].
329+
///
330+
/// The entire output buffer is zeroized before the MAC value is written.
321331
fn do_final_out(self, out: &mut [u8]) -> Result<usize, MACError>;
322332

323333
/// Internally, this will re-compute the MAC value and then compare it to the provided mac value
@@ -392,6 +402,7 @@ pub trait RNG : Default {
392402
fn next_bytes(&mut self, len: usize) -> Result<Vec<u8>, RNGError>;
393403

394404
/// Returns the number of bytes written.
405+
/// The entire output buffer is zeroized before the random bytes are written.
395406
fn next_bytes_out(&mut self, out: &mut [u8]) -> Result<usize, RNGError>;
396407

397408
fn fill_keymaterial_out(&mut self, out: &mut impl KeyMaterialTrait) -> Result<usize, RNGError>;
@@ -443,6 +454,7 @@ pub trait PHSignature<
443454
/// might throw an error, ignore the provided ctx value, or append the ctx to the msg in a non-standard way.
444455
fn sign_ph(sk: &SK, ph: &[u8; PH_LEN], ctx: Option<&[u8]>) -> Result<[u8; SIG_LEN], SignatureError>;
445456
/// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
457+
/// The entire output buffer is zeroized before the signature is written.
446458
fn sign_ph_out(sk: &SK, ph: &[u8; PH_LEN], ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN]) -> Result<usize, SignatureError>;
447459
/// On success, returns Ok(())
448460
/// On failure, returns Err([SignatureError::SignatureVerificationFailed]); may also return other types of [SignatureError] as appropriate (such as for invalid-length inputs).
@@ -501,6 +513,7 @@ pub trait Signature<
501513
fn sign(sk: &SK, msg: &[u8], ctx: Option<&[u8]>) -> Result<[u8; SIG_LEN], SignatureError>;
502514

503515
/// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
516+
/// The entire output buffer is zeroized before the signature is written.
504517
fn sign_out(sk: &SK, msg: &[u8], ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN]) -> Result<usize, SignatureError>;
505518

506519
/* streaming signing API */
@@ -516,6 +529,7 @@ pub trait Signature<
516529
fn sign_final(self) -> Result<[u8; SIG_LEN], SignatureError>;
517530

518531
/// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer.
532+
/// The entire output buffer is zeroized before the signature is written.
519533
fn sign_final_out(self, output: &mut [u8; SIG_LEN]) -> Result<usize, SignatureError>;
520534

521535
/// On success, returns Ok(())
@@ -543,6 +557,7 @@ pub trait SignaturePublicKey<const PK_LEN: usize> : PartialEq + Eq + Clone + Deb
543557
/// Write it out to bytes in its standard encoding.
544558
fn encode(&self) -> [u8; PK_LEN];
545559
/// Write it out to bytes in its standard encoding.
560+
/// The entire output buffer is zeroized before the encoding is written.
546561
fn encode_out(&self, out: &mut [u8; PK_LEN]) -> usize;
547562
/// Read it in from bytes in its standard encoding.
548563
fn from_bytes(bytes: &[u8]) -> Result<Self, SignatureError>;
@@ -553,6 +568,7 @@ pub trait SignaturePrivateKey<const SK_LEN: usize> : PartialEq + Eq + Clone + Se
553568
/// Write it out to bytes in its standard encoding.
554569
fn encode(&self) -> [u8; SK_LEN];
555570
/// Write it out to bytes in its standard encoding.
571+
/// The entire output buffer is zeroized before the encoding is written.
556572
fn encode_out(&self, out: &mut [u8; SK_LEN]) -> usize;
557573
/// Read it in from bytes in its standard encoding.
558574
fn from_bytes(bytes: &[u8]) -> Result<Self, SignatureError>;
@@ -583,6 +599,7 @@ pub trait XOF : Default {
583599

584600
/// A static one-shot API that digests the input data and produces `result_len` bytes of output.
585601
/// Fills the provided output slice.
602+
/// The entire output buffer is zeroized before the output is written.
586603
fn hash_xof_out(self, data: &[u8], output: &mut [u8]) -> usize;
587604

588605
fn absorb(&mut self, data: &[u8]);
@@ -599,13 +616,16 @@ pub trait XOF : Default {
599616

600617
/// Can be called multiple times.
601618
/// Fills the provided output slice.
619+
/// The entire output buffer is zeroized before the output is written.
602620
fn squeeze_out(&mut self, output: &mut [u8]) -> usize;
603621

604622
/// Squeezes a partial byte from the XOF.
605623
/// Output will be in the top `num_bits` bits of the returned u8 (ie Big Endian).
606624
/// This is a final call and consumes self.
607625
fn squeeze_partial_byte_final(self, num_bits: usize) -> Result<u8, HashError>;
608626

627+
/// The same as [XOF::squeeze_partial_byte_final], but writes into the provided output byte.
628+
/// The output byte is zeroized before the result is written.
609629
fn squeeze_partial_byte_final_out(
610630
self,
611631
num_bits: usize,

crypto/mlkem/src/matrix.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,8 +171,6 @@ impl<const k: usize> Vector<k>
171171
// let mut s = self.clone();
172172
// s.conditional_sub_q();
173173

174-
out.fill(0);
175-
176174
let mut idx = 0;
177175
match du {
178176
10 => { // MLKEM512 and MLKEM 768

crypto/mlkem/src/polynomial.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,6 @@ impl Polynomial {
127127
// each of the N i16's will take dv bits
128128
debug_assert_eq!(out.len(), N * (dv as usize) / 8);
129129

130-
out.fill(0);
131-
132130
let mut t = [0u8; 8];
133131
let mut idx = 0;
134132

crypto/mlkem_lowmemory/src/polynomial.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,6 @@ impl Polynomial {
150150
// each of the N i16's will take dv bits
151151
debug_assert_eq!(out.len(), N * (dv as usize) / 8);
152152

153-
out.fill(0);
154-
155153
let mut t = [0u8; 8];
156154
let mut idx = 0;
157155

crypto/sha3/tests/sha3_tests.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,7 @@ mod sha3_tests {
6666
assert_eq!(&out, b"\x58\x4c\xc7\x02\xc2\x22\x9a\x0a\xbc\x78\x9b\xfa\x64\xb4\x27\x1f\xb8\xf0\xbb\x78\x67\x15\x88\xb9\xef\x1d\x09\x3e\xa3\xd4\x72\x58\x4c\x6d\x43\xb5\x68\x33\x59\x47\x2f\x44\x1b\x33\x85\x6f\x68\x28\x59\xf0\xc3\x95\x4b\x56\x80\x8f\xd1\xfb\xa0\xb5\x9c\x9d\x19\x54");
6767
assert_eq!(bytes_written, 64);
6868

69-
// Q. T. Felix NOTE: With the application of zeroize, the entire output buffer is pre-initialized to 0,
70-
// so the bytes after the digest length are now also 0.
71-
// Previously, the contract was to leave the trailing bytes untouched,
72-
// but this has been changed to fill them with zeros to prevent exposure of stale data.
69+
// check that the bytes of an oversized output buffer past the digest length get zeroized.
7370
let mut out = DUMMY_SEED_512.clone();
7471
SHA3_256::new().hash_out(DUMMY_SEED_512, &mut out);
7572
assert!(out[32..].iter().all(|&b| b == 0));

crypto/utils/src/ct.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,6 @@ pub fn conditional_copy_bytes<const LEN: usize>(
292292
out: &mut [u8; LEN],
293293
take_a: bool,
294294
) {
295-
out.fill(0);
296-
297295
// we want the behaviour of
298296
// if take_a { 0xFF } else { 0x00 }
299297
// but without using any branches that could leak timing signals

0 commit comments

Comments
 (0)