@@ -472,3 +472,195 @@ macro_rules! blake2_mac_impl {
472472 }
473473 } ;
474474}
475+
476+ /// Creates a buffered wrapper around block-level "core" type which implements variable output size traits
477+ /// with output size selected at run time.
478+ #[ macro_export]
479+ macro_rules! buffer_rt_variable {
480+ (
481+ $( #[ $attr: meta] ) *
482+ $vis: vis struct $name: ident( $core_ty: ty) ;
483+ exclude: SerializableState ;
484+ ) => {
485+ $( #[ $attr] ) *
486+ $vis struct $name {
487+ core: $core_ty,
488+ buffer: digest:: block_api:: Buffer <$core_ty>,
489+ output_size: u8 ,
490+ }
491+
492+ impl core:: fmt:: Debug for $name {
493+ #[ inline]
494+ fn fmt( & self , f: & mut core:: fmt:: Formatter <' _>) -> core:: fmt:: Result {
495+ f. write_str( concat!( stringify!( $name) , " { ... }" ) )
496+ }
497+ }
498+
499+ impl digest:: crypto_common:: AlgorithmName for $name {
500+ #[ inline]
501+ fn write_alg_name( f: & mut core:: fmt:: Formatter <' _>) -> Result <( ) , core:: fmt:: Error > {
502+ <$core_ty as digest:: crypto_common:: AlgorithmName >:: write_alg_name( f)
503+ }
504+ }
505+
506+ impl Clone for $name {
507+ #[ inline]
508+ fn clone( & self ) -> Self {
509+ Self {
510+ core: Clone :: clone( & self . core) ,
511+ buffer: Clone :: clone( & self . buffer) ,
512+ output_size: self . output_size,
513+ }
514+ }
515+ }
516+
517+ impl digest:: Reset for $name {
518+ #[ inline]
519+ fn reset( & mut self ) {
520+ let size = self . output_size. into( ) ;
521+ self . core = <$core_ty as digest:: block_api:: VariableOutputCore >:: new( size) . unwrap( ) ;
522+ self . buffer. reset( ) ;
523+ }
524+ }
525+
526+ impl digest:: block_api:: BlockSizeUser for $name {
527+ type BlockSize = <$core_ty as digest:: crypto_common:: BlockSizeUser >:: BlockSize ;
528+ }
529+
530+ impl digest:: HashMarker for $name { }
531+
532+ impl digest:: Update for $name {
533+ #[ inline]
534+ fn update( & mut self , data: & [ u8 ] ) {
535+ let Self { core, buffer, .. } = self ;
536+ buffer. digest_blocks( data, |blocks| {
537+ digest:: block_api:: UpdateCore :: update_blocks( core, blocks) ;
538+ } ) ;
539+ }
540+ }
541+
542+ impl $name {
543+ /// Maximum size of output hash in bytes.
544+ pub const MAX_OUTPUT_SIZE : usize = <
545+ <$core_ty as digest:: block_api:: OutputSizeUser >:: OutputSize
546+ as digest:: typenum:: Unsigned
547+ >:: USIZE ;
548+
549+ /// Create new hasher instance with the given output size in bytes.
550+ ///
551+ /// It will return `Err(InvalidOutputSize)` in case if hasher can not return
552+ /// hash of the specified output size.
553+ #[ inline]
554+ pub fn new( output_size: usize ) -> Result <Self , digest:: InvalidOutputSize > {
555+ let output_size = u8 :: try_from( output_size) . map_err( |_| digest:: InvalidOutputSize ) ?;
556+ let buffer = Default :: default ( ) ;
557+ let core = <$core_ty as digest:: block_api:: VariableOutputCore >:: new( output_size. into( ) ) ?;
558+ Ok ( Self {
559+ core,
560+ buffer,
561+ output_size,
562+ } )
563+ }
564+
565+ /// Get output size in bytes of the hasher instance provided to the `new` method
566+ #[ inline]
567+ pub fn output_size( & self ) -> usize {
568+ self . output_size. into( )
569+ }
570+
571+ /// Write result into the output buffer.
572+ ///
573+ /// Returns `Err(InvalidOutputSize)` if `out` size is not equal to
574+ /// `self.output_size()`.
575+ #[ inline]
576+ pub fn finalize_variable( mut self , out: & mut [ u8 ] ) -> Result <( ) , digest:: InvalidBufferSize > {
577+ self . finalize_dirty( out)
578+ }
579+
580+ /// Write result into the output buffer and reset the hasher state.
581+ ///
582+ /// Returns `Err(InvalidOutputSize)` if `out` size is not equal to
583+ /// `self.output_size()`.
584+ #[ inline]
585+ pub fn finalize_variable_reset(
586+ & mut self ,
587+ out: & mut [ u8 ] ,
588+ ) -> Result <( ) , digest:: InvalidBufferSize > {
589+ self . finalize_dirty( out) ?;
590+ digest:: Reset :: reset( self ) ;
591+ Ok ( ( ) )
592+ }
593+
594+ #[ inline]
595+ fn finalize_dirty( & mut self , out: & mut [ u8 ] ) -> Result <( ) , digest:: InvalidBufferSize > {
596+ let Self {
597+ core,
598+ buffer,
599+ output_size,
600+ } = self ;
601+ let size_u8 = u8 :: try_from( out. len( ) ) . map_err( |_| digest:: InvalidBufferSize ) ?;
602+
603+ let max_size = Self :: MAX_OUTPUT_SIZE ;
604+ if out. len( ) > max_size || size_u8 != * output_size {
605+ return Err ( digest:: InvalidBufferSize ) ;
606+ }
607+ let mut full_res = Default :: default ( ) ;
608+ digest:: block_api:: VariableOutputCore :: finalize_variable_core( core, buffer, & mut full_res) ;
609+ let n = out. len( ) ;
610+ let m = full_res. len( ) - n;
611+ use digest:: block_api:: TruncSide :: { Left , Right } ;
612+ let side = <$core_ty as digest:: block_api:: VariableOutputCore >:: TRUNC_SIDE ;
613+ match side {
614+ Left => out. copy_from_slice( & full_res[ ..n] ) ,
615+ Right => out. copy_from_slice( & full_res[ m..] ) ,
616+ }
617+ Ok ( ( ) )
618+ }
619+ }
620+
621+ impl Drop for $name {
622+ #[ inline]
623+ fn drop( & mut self ) {
624+ #[ cfg( feature = "zeroize" ) ]
625+ {
626+ use digest:: zeroize:: Zeroize ;
627+ self . buffer. zeroize( ) ;
628+ self . output_size. zeroize( ) ;
629+ }
630+ }
631+ }
632+
633+ #[ cfg( feature = "zeroize" ) ]
634+ impl digest:: zeroize:: ZeroizeOnDrop for $name { }
635+ } ;
636+
637+ (
638+ $( #[ $attr: meta] ) *
639+ $vis: vis struct $name: ident( $core_ty: ty) ;
640+ ) => {
641+ buffer_rt_variable!(
642+ $( #[ $attr] ) *
643+ $vis struct $name( $core_ty) ;
644+ exclude: SerializableState ;
645+ ) ;
646+
647+ impl digest:: crypto_common:: hazmat:: SerializableState for $name {
648+ type SerializedStateSize = digest:: typenum:: Add1 <digest:: typenum:: Sum <
649+ <$core_ty as digest:: crypto_common:: hazmat:: SerializableState >:: SerializedStateSize ,
650+ <$core_ty as digest:: block_api:: BlockSizeUser >:: BlockSize ,
651+ >>;
652+
653+ #[ inline]
654+ fn serialize( & self ) -> digest:: crypto_common:: hazmat:: SerializedState <Self > {
655+ todo!( )
656+ }
657+
658+ #[ inline]
659+ fn deserialize(
660+ serialized_state: & digest:: crypto_common:: hazmat:: SerializedState <Self >,
661+ ) -> Result <Self , digest:: crypto_common:: hazmat:: DeserializeStateError > {
662+ todo!( )
663+ }
664+ }
665+ } ;
666+ }
0 commit comments