55// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
66// of this source tree.
77
8- use core:: ops :: Add ;
8+ use core:: convert :: TryFrom ;
99
10- use digest:: core_api:: BlockSizeUser ;
10+ use digest:: core_api:: { Block , BlockSizeUser } ;
1111use digest:: { Digest , FixedOutputReset } ;
12- use generic_array:: sequence:: Concat ;
13- use generic_array:: typenum:: { Unsigned , U1 , U2 } ;
12+ use generic_array:: typenum:: { IsLess , NonZero , Unsigned , U65536 } ;
1413use generic_array:: { ArrayLength , GenericArray } ;
1514
16- use crate :: util:: i2osp;
1715use crate :: { Error , Result } ;
1816
19- // Computes ceil(x / y)
20- fn div_ceil ( x : usize , y : usize ) -> usize {
21- let additive = ( x % y != 0 ) as usize ;
22- x / y + additive
23- }
24-
2517fn xor < L : ArrayLength < u8 > > ( x : GenericArray < u8 , L > , y : GenericArray < u8 , L > ) -> GenericArray < u8 , L > {
2618 x. into_iter ( ) . zip ( y) . map ( |( x1, x2) | x1 ^ x2) . collect ( )
2719}
2820
2921/// Corresponds to the expand_message_xmd() function defined in
3022/// <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt>
31- pub fn expand_message_xmd <
32- ' a ,
33- H : BlockSizeUser + Digest + FixedOutputReset ,
34- L : ArrayLength < u8 > ,
35- M : IntoIterator < Item = & ' a [ u8 ] > ,
36- D : ArrayLength < u8 > + Add < U1 > ,
37- > (
38- msg : M ,
39- dst : GenericArray < u8 , D > ,
23+ pub fn expand_message_xmd < H : BlockSizeUser + Digest + FixedOutputReset , L : ArrayLength < u8 > > (
24+ msg : & [ & [ u8 ] ] ,
25+ dst : & [ u8 ] ,
4026) -> Result < GenericArray < u8 , L > >
4127where
42- <D as Add < U1 > >:: Output : ArrayLength < u8 > ,
28+ // Constraint set by `expand_message_xmd`:
29+ // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#section-5.4.1-6
30+ L : NonZero + IsLess < U65536 > ,
4331{
44- let digest_len = H :: OutputSize :: USIZE ;
45- let ell = div_ceil ( L :: USIZE , digest_len) ;
46- if ell > 255 {
32+ // DST, a byte string of at most 255 bytes.
33+ let dst_len = u8:: try_from ( dst. len ( ) ) . map_err ( |_| Error :: HashToCurveError ) ?;
34+
35+ // b_in_bytes, b / 8 for b the output size of H in bits.
36+ let b_in_bytes = H :: OutputSize :: to_usize ( ) ;
37+
38+ // Constraint set by `expand_message_xmd`:
39+ // https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-13.html#section-5.4.1-4
40+ if b_in_bytes > H :: BlockSize :: USIZE {
4741 return Err ( Error :: HashToCurveError ) ;
4842 }
49- let dst_prime = dst. concat ( i2osp :: < U1 > ( D :: USIZE ) ?) ;
50- let z_pad = i2osp :: < H :: BlockSize > ( 0 ) ?;
51- let l_i_b_str = i2osp :: < U2 > ( L :: USIZE ) ?;
5243
53- let mut h = H :: new ( ) ;
44+ // ell = ceil(len_in_bytes / b_in_bytes)
45+ // ABORT if ell > 255
46+ let ell = u8:: try_from ( ( L :: USIZE + b_in_bytes - 1 ) / b_in_bytes)
47+ . map_err ( |_| Error :: HashToCurveError ) ?;
48+
49+ let mut hash = H :: new ( ) ;
5450
51+ // b_0 = H(msg_prime)
5552 // msg_prime = Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime
56- Digest :: update ( & mut h, z_pad) ;
57- for bytes in msg {
58- Digest :: update ( & mut h, bytes)
53+ // Z_pad = I2OSP(0, s_in_bytes)
54+ // s_in_bytes, the input block size of H, measured in bytes
55+ Digest :: update ( & mut hash, Block :: < H > :: default ( ) ) ;
56+ for msg in msg {
57+ Digest :: update ( & mut hash, msg) ;
5958 }
60- Digest :: update ( & mut h, l_i_b_str) ;
61- Digest :: update ( & mut h, i2osp :: < U1 > ( 0 ) ?) ;
62- Digest :: update ( & mut h, & dst_prime) ;
59+ // l_i_b_str = I2OSP(len_in_bytes, 2)
60+ Digest :: update ( & mut hash, L :: U16 . to_be_bytes ( ) ) ;
61+ Digest :: update ( & mut hash, [ 0 ] ) ;
62+ // DST_prime = DST || I2OSP(len(DST), 1)
63+ Digest :: update ( & mut hash, dst) ;
64+ Digest :: update ( & mut hash, [ dst_len] ) ;
65+ let b_0 = hash. finalize_reset ( ) ;
6366
64- // b[0]
65- let b_0 = h. finalize_reset ( ) ;
6667 let mut b_i = GenericArray :: default ( ) ;
6768
6869 let mut uniform_bytes = GenericArray :: default ( ) ;
6970
70- for ( i, chunk) in ( 1 ..( ell + 1 ) ) . zip ( uniform_bytes. chunks_mut ( digest_len) ) {
71- Digest :: update ( & mut h, xor ( b_0. clone ( ) , b_i. clone ( ) ) ) ;
72- Digest :: update ( & mut h, i2osp :: < U1 > ( i) ?) ;
73- Digest :: update ( & mut h, & dst_prime) ;
74- b_i = h. finalize_reset ( ) ;
75- chunk. copy_from_slice ( & b_i[ ..digest_len. min ( chunk. len ( ) ) ] ) ;
71+ // b_1 = H(b_0 || I2OSP(1, 1) || DST_prime)
72+ // for i in (2, ..., ell):
73+ for ( i, chunk) in ( 1 ..( ell + 1 ) ) . zip ( uniform_bytes. chunks_mut ( b_in_bytes) ) {
74+ // b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
75+ Digest :: update ( & mut hash, xor ( b_0. clone ( ) , b_i. clone ( ) ) ) ;
76+ Digest :: update ( & mut hash, [ i] ) ;
77+ // DST_prime = DST || I2OSP(len(DST), 1)
78+ Digest :: update ( & mut hash, dst) ;
79+ Digest :: update ( & mut hash, [ dst_len] ) ;
80+ b_i = hash. finalize_reset ( ) ;
81+ // uniform_bytes = b_1 || ... || b_ell
82+ // return substr(uniform_bytes, 0, len_in_bytes)
83+ chunk. copy_from_slice ( & b_i[ ..b_in_bytes. min ( chunk. len ( ) ) ] ) ;
7684 }
7785
7886 Ok ( uniform_bytes)
8189#[ cfg( test) ]
8290mod tests {
8391 use generic_array:: typenum:: { U128 , U32 } ;
84- use generic_array:: GenericArray ;
8592
8693 struct Params {
8794 msg : & ' static str ,
@@ -91,6 +98,8 @@ mod tests {
9198
9299 #[ test]
93100 fn test_expand_message_xmd ( ) {
101+ const DST : [ u8 ; 27 ] = * b"QUUX-V01-CS02-with-expander" ;
102+
94103 // Test vectors taken from Section K.1 of https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
95104 let test_vectors: alloc:: vec:: Vec < Params > = alloc:: vec![
96105 Params {
@@ -190,20 +199,13 @@ mod tests {
190199 378fba044a31f5cb44583a892f5969dcd73b3fa128816e",
191200 } ,
192201 ] ;
193- let dst = GenericArray :: from ( * b"QUUX-V01-CS02-with-expander" ) ;
194202
195203 for tv in test_vectors {
196204 let uniform_bytes = match tv. len_in_bytes {
197- 32 => super :: expand_message_xmd :: < sha2:: Sha256 , U32 , _ , _ > (
198- Some ( tv. msg . as_bytes ( ) ) ,
199- dst,
200- )
201- . map ( |bytes| bytes. to_vec ( ) ) ,
202- 128 => super :: expand_message_xmd :: < sha2:: Sha256 , U128 , _ , _ > (
203- Some ( tv. msg . as_bytes ( ) ) ,
204- dst,
205- )
206- . map ( |bytes| bytes. to_vec ( ) ) ,
205+ 32 => super :: expand_message_xmd :: < sha2:: Sha256 , U32 > ( & [ tv. msg . as_bytes ( ) ] , & DST )
206+ . map ( |bytes| bytes. to_vec ( ) ) ,
207+ 128 => super :: expand_message_xmd :: < sha2:: Sha256 , U128 > ( & [ tv. msg . as_bytes ( ) ] , & DST )
208+ . map ( |bytes| bytes. to_vec ( ) ) ,
207209 _ => unimplemented ! ( ) ,
208210 }
209211 . unwrap ( ) ;
0 commit comments