5
5
// License, Version 2.0 found in the LICENSE-APACHE file in the root directory
6
6
// of this source tree.
7
7
8
- use core:: ops :: Add ;
8
+ use core:: convert :: TryFrom ;
9
9
10
- use digest:: core_api:: BlockSizeUser ;
10
+ use digest:: core_api:: { Block , BlockSizeUser } ;
11
11
use 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 } ;
14
13
use generic_array:: { ArrayLength , GenericArray } ;
15
14
16
- use crate :: util:: i2osp;
17
15
use crate :: { Error , Result } ;
18
16
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
-
25
17
fn xor < L : ArrayLength < u8 > > ( x : GenericArray < u8 , L > , y : GenericArray < u8 , L > ) -> GenericArray < u8 , L > {
26
18
x. into_iter ( ) . zip ( y) . map ( |( x1, x2) | x1 ^ x2) . collect ( )
27
19
}
28
20
29
21
/// Corresponds to the expand_message_xmd() function defined in
30
22
/// <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 ] ,
40
26
) -> Result < GenericArray < u8 , L > >
41
27
where
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 > ,
43
31
{
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 {
47
41
return Err ( Error :: HashToCurveError ) ;
48
42
}
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 ) ?;
52
43
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 ( ) ;
54
50
51
+ // b_0 = H(msg_prime)
55
52
// 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) ;
59
58
}
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 ( ) ;
63
66
64
- // b[0]
65
- let b_0 = h. finalize_reset ( ) ;
66
67
let mut b_i = GenericArray :: default ( ) ;
67
68
68
69
let mut uniform_bytes = GenericArray :: default ( ) ;
69
70
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 ( ) ) ] ) ;
76
84
}
77
85
78
86
Ok ( uniform_bytes)
81
89
#[ cfg( test) ]
82
90
mod tests {
83
91
use generic_array:: typenum:: { U128 , U32 } ;
84
- use generic_array:: GenericArray ;
85
92
86
93
struct Params {
87
94
msg : & ' static str ,
@@ -91,6 +98,8 @@ mod tests {
91
98
92
99
#[ test]
93
100
fn test_expand_message_xmd ( ) {
101
+ const DST : [ u8 ; 27 ] = * b"QUUX-V01-CS02-with-expander" ;
102
+
94
103
// Test vectors taken from Section K.1 of https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.txt
95
104
let test_vectors: alloc:: vec:: Vec < Params > = alloc:: vec![
96
105
Params {
@@ -190,20 +199,13 @@ mod tests {
190
199
378fba044a31f5cb44583a892f5969dcd73b3fa128816e",
191
200
} ,
192
201
] ;
193
- let dst = GenericArray :: from ( * b"QUUX-V01-CS02-with-expander" ) ;
194
202
195
203
for tv in test_vectors {
196
204
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 ( ) ) ,
207
209
_ => unimplemented ! ( ) ,
208
210
}
209
211
. unwrap ( ) ;
0 commit comments