@@ -3671,8 +3671,9 @@ pub struct SecretBuffer {
36713671
36723672/// Owned secret bytes extracted from [`SecretBuffer`].
36733673///
3674- /// This wrapper keeps redacted formatting and best-effort drop-time cleanup
3675- /// after a [`SecretBuffer`] is consumed for owned interop. Use
3674+ /// This wrapper keeps redacted formatting, best-effort spare-capacity clearing
3675+ /// at construction time, and best-effort full wipe on drop after a
3676+ /// [`SecretBuffer`] is consumed for owned interop. Use
36763677/// [`Self::into_exposed_unprotected_vec_caller_must_zeroize`] only when a raw
36773678/// `Vec<u8>` is unavoidable and the caller will handle cleanup.
36783679#[ cfg( feature = "alloc" ) ]
@@ -3771,8 +3772,9 @@ impl AsMut<[u8]> for ExposedSecretVec {
37713772
37723773/// Owned secret UTF-8 text extracted from [`SecretBuffer`].
37733774///
3774- /// This wrapper keeps redacted formatting and best-effort drop-time cleanup
3775- /// after a [`SecretBuffer`] is consumed for string interop. Use
3775+ /// This wrapper keeps redacted formatting, best-effort spare-capacity clearing
3776+ /// at construction time, and best-effort full wipe on drop after a
3777+ /// [`SecretBuffer`] is consumed for string interop. Use
37763778/// [`Self::into_exposed_unprotected_string_caller_must_zeroize`] only when a
37773779/// raw `String` is unavoidable and the caller will handle cleanup.
37783780#[ cfg( feature = "alloc" ) ]
@@ -3785,6 +3787,9 @@ impl ExposedSecretString {
37853787 /// Wraps an owned UTF-8 string as exposed secret text.
37863788 #[ must_use]
37873789 pub fn from_string ( text : alloc:: string:: String ) -> Self {
3790+ let mut bytes = text. into_bytes ( ) ;
3791+ wipe_vec_spare_capacity ( & mut bytes) ;
3792+ let text = string_from_validated_secret_bytes ( bytes) ;
37883793 Self { text }
37893794 }
37903795
@@ -5084,7 +5089,11 @@ fn constant_time_eq_same_len(left: &[u8], right: &[u8]) -> bool {
50845089 diff = unsafe { core:: ptr:: read_volatile ( & raw const diff) } ;
50855090 }
50865091 ct_error_gate_barrier ( diff, 0 ) ;
5087- diff == 0
5092+ // SAFETY: `diff` is an initialized local `u8`; this final volatile read
5093+ // keeps the public equality comparison dependent on a post-barrier load of
5094+ // the accumulated value.
5095+ let result = unsafe { core:: ptr:: read_volatile ( & raw const diff) } ;
5096+ result == 0
50885097}
50895098
50905099#[ cfg( feature = "alloc" ) ]
@@ -7973,7 +7982,10 @@ fn ct_decode_padded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, De
79737982 wipe_bytes ( buffer) ;
79747983 return Err ( DecodeError :: InvalidInput ) ;
79757984 }
7976- report_ct_error ( invalid_byte, invalid_padding) ?;
7985+ if let Err ( err) = report_ct_error ( invalid_byte, invalid_padding) {
7986+ wipe_bytes ( buffer) ;
7987+ return Err ( err) ;
7988+ }
79777989 Ok ( write)
79787990}
79797991
@@ -8137,7 +8149,10 @@ fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize,
81378149 wipe_bytes ( buffer) ;
81388150 return Err ( DecodeError :: InvalidInput ) ;
81398151 }
8140- report_ct_error ( invalid_byte, invalid_padding) ?;
8152+ if let Err ( err) = report_ct_error ( invalid_byte, invalid_padding) {
8153+ wipe_bytes ( buffer) ;
8154+ return Err ( err) ;
8155+ }
81418156 Ok ( write)
81428157}
81438158
@@ -8184,15 +8199,30 @@ fn read_tail_or_mark_invalid<'a>(
81848199}
81858200
81868201#[ inline( never) ]
8202+ #[ allow( unsafe_code) ]
81878203fn ct_decode_alphabet_byte < A : Alphabet > ( byte : u8 ) -> ( u8 , u8 ) {
81888204 let mut decoded = 0u8 ;
81898205 let mut valid = 0u8 ;
81908206 let mut candidate = 0u8 ;
81918207
81928208 while candidate < 64 {
8193- let matches = ct_mask_eq_u8 ( byte, A :: ENCODE [ candidate as usize ] ) ;
8194- decoded |= candidate & matches;
8195- valid |= matches;
8209+ let matches = core:: hint:: black_box ( ct_mask_eq_u8 (
8210+ core:: hint:: black_box ( byte) ,
8211+ core:: hint:: black_box ( A :: ENCODE [ candidate as usize ] ) ,
8212+ ) ) ;
8213+ decoded = core:: hint:: black_box (
8214+ core:: hint:: black_box ( decoded) | core:: hint:: black_box ( candidate & matches) ,
8215+ ) ;
8216+ // SAFETY: `decoded` is an initialized local `u8`; the volatile read is
8217+ // an optimizer barrier for the fixed 64-iteration alphabet scan and
8218+ // does not access caller memory.
8219+ decoded = unsafe { core:: ptr:: read_volatile ( & raw const decoded) } ;
8220+ valid =
8221+ core:: hint:: black_box ( core:: hint:: black_box ( valid) | core:: hint:: black_box ( matches) ) ;
8222+ // SAFETY: `valid` is an initialized local `u8`; the volatile read is an
8223+ // optimizer barrier for the fixed 64-iteration alphabet scan and does
8224+ // not access caller memory.
8225+ valid = unsafe { core:: ptr:: read_volatile ( & raw const valid) } ;
81968226 candidate += 1 ;
81978227 }
81988228
0 commit comments