24
24
// The goal for this implementation is to drive the overhead as close to zero
25
25
// as possible.
26
26
27
- use crate :: {
28
- c, cpu, debug,
29
- endian:: { ArrayEncoding , BigEndian } ,
30
- polyfill,
31
- } ;
27
+ use crate :: { c, cpu, debug, endian:: BigEndian , polyfill} ;
32
28
use core:: num:: Wrapping ;
29
+ use zerocopy:: { AsBytes , FromBytes } ;
33
30
34
31
mod sha1;
35
32
mod sha2;
@@ -114,7 +111,9 @@ impl BlockContext {
114
111
115
112
Digest {
116
113
algorithm : self . algorithm ,
117
- value : ( self . algorithm . format_output ) ( self . state ) ,
114
+ // SAFETY: Invariant on this field promises that this is the correct
115
+ // format function for this algorithm's block size.
116
+ value : unsafe { ( self . algorithm . format_output ) ( self . state ) } ,
118
117
}
119
118
}
120
119
}
@@ -248,8 +247,11 @@ impl Digest {
248
247
impl AsRef < [ u8 ] > for Digest {
249
248
#[ inline( always) ]
250
249
fn as_ref ( & self ) -> & [ u8 ] {
251
- let as64 = unsafe { & self . value . as64 } ;
252
- & as64. as_byte_array ( ) [ ..self . algorithm . output_len ]
250
+ let data = ( & self . value as * const Output ) . cast :: < u8 > ( ) ;
251
+ // SAFETY: So long as `self.algorithm` is the correct algorithm, all
252
+ // code initializes all bytes of `self.value` in the range `[0,
253
+ // self.algorithm.output_len)`.
254
+ unsafe { core:: slice:: from_raw_parts ( data, self . algorithm . output_len ) }
253
255
}
254
256
}
255
257
@@ -270,7 +272,9 @@ pub struct Algorithm {
270
272
len_len : usize ,
271
273
272
274
block_data_order : unsafe extern "C" fn ( state : & mut State , data : * const u8 , num : c:: size_t ) ,
273
- format_output : fn ( input : State ) -> Output ,
275
+ // INVARIANT: This is always set to the correct output for the algorithm's
276
+ // block size.
277
+ format_output : unsafe fn ( input : State ) -> Output ,
274
278
275
279
initial_state : State ,
276
280
@@ -474,14 +478,38 @@ pub const MAX_OUTPUT_LEN: usize = 512 / 8;
474
478
/// algorithms in this module.
475
479
pub const MAX_CHAINING_LEN : usize = MAX_OUTPUT_LEN ;
476
480
477
- fn sha256_format_output ( input : State ) -> Output {
481
+ fn sha256_format_output ( input : State ) -> Output
482
+ where
483
+ [ BigEndian < u32 > ; 256 / 8 / core:: mem:: size_of :: < BigEndian < u32 > > ( ) ] : FromBytes ,
484
+ [ BigEndian < u64 > ; 512 / 8 / core:: mem:: size_of :: < BigEndian < u64 > > ( ) ] : AsBytes ,
485
+ {
486
+ // SAFETY: There are two cases:
487
+ // - The union is initialized as `as32`, in which case this is trivially
488
+ // sound.
489
+ // - The union is initialized as `as64`. In this case, the `as64` variant is
490
+ // longer than the `as32` variant, so all bytes of `as32` are initialized
491
+ // as they are in the prefix of `as64`. Since `as64`'s type is `AsBytes`
492
+ // (see the where bound on this function), all of its bytes are
493
+ // initialized (ie, none are padding). Since `as32`'s type is `FromBytes`,
494
+ // any initialized sequence of bytes constitutes a valid instance of the
495
+ // type, so this is sound.
478
496
let input = unsafe { & input. as32 } ;
479
497
Output {
480
498
as32 : input. map ( BigEndian :: from) ,
481
499
}
482
500
}
483
501
484
- fn sha512_format_output ( input : State ) -> Output {
502
+ /// # Safety
503
+ ///
504
+ /// The caller must ensure that all bytes of `State` have been initialized.
505
+ unsafe fn sha512_format_output ( input : State ) -> Output
506
+ where
507
+ [ BigEndian < u64 > ; 512 / 8 / core:: mem:: size_of :: < BigEndian < u64 > > ( ) ] : FromBytes ,
508
+ {
509
+ // SAFETY: Caller has promised that all bytes are initialized. Since
510
+ // `input.as64` is `FromBytes`, we know that this is sufficient to guarantee
511
+ // that the input has been initialized to a valid instance of the type of
512
+ // `input.as64`.
485
513
let input = unsafe { & input. as64 } ;
486
514
Output {
487
515
as64 : input. map ( BigEndian :: from) ,
0 commit comments