@@ -28,20 +28,16 @@ use std::iter::repeat;
28
28
use std:: result:: Result ;
29
29
use std:: slice:: Chunks ;
30
30
use std:: vec:: Vec ;
31
-
32
- pub use num:: bigint:: { BigInt , ToBigInt } ;
33
- pub use num:: traits:: { FromPrimitive , ToPrimitive } ;
31
+ use std:: num:: Wrapping ;
34
32
35
33
use chacha20:: ChaCha20 ;
36
34
use cryptoutil:: xor_keystream;
37
35
use symmetriccipher:: SynchronousStreamCipher ; // Used in order to call ChaCha20::process().
38
36
39
37
extern crate bit_vec;
38
+ extern crate extprim;
40
39
use self :: bit_vec:: BitVec ;
41
-
42
- macro_rules! u64toBI {
43
- ( $x: expr) => ( BigInt :: from_u64( $x) . expect( & format!( "Couldn't convert {:?} into BigInt" , $x) [ ..] ) )
44
- }
40
+ use self :: extprim:: u128:: u128;
45
41
46
42
#[ derive( Debug , Clone , Copy ) ]
47
43
pub enum Error {
@@ -327,11 +323,11 @@ impl Subkeygen for HS1 {
327
323
assert_eq ! ( kPrime. len( ) , 32 ) ;
328
324
329
325
N = toStr ( 12 ,
330
- & ( ( self . parameters . b as u64 * 2u64 . pow ( 48 ) +
331
- self . parameters . t as u64 * 2u64 . pow ( 40 ) +
332
- self . parameters . r as u64 * 2u64 . pow ( 32 ) +
333
- self . parameters . l as u64 * 2u64 . pow ( 16 ) +
334
- K . len ( ) as u64 ) as usize ) ) ;
326
+ ( ( self . parameters . b as u64 * 2u64 . pow ( 48 ) +
327
+ self . parameters . t as u64 * 2u64 . pow ( 40 ) +
328
+ self . parameters . r as u64 * 2u64 . pow ( 32 ) +
329
+ self . parameters . l as u64 * 2u64 . pow ( 16 ) +
330
+ K . len ( ) as u64 ) as usize ) ) ;
335
331
N . truncate ( 12 ) ;
336
332
337
333
chacha = ChaCha20 :: new ( & kPrime, & N [ ..] , Some ( self . parameters . r as i8 ) ) ;
@@ -480,45 +476,65 @@ impl PRF for HS1 {
480
476
/// 7. else Y = toStr(4, (kA[0] + kA[1] × (h mod 2^32) + kA[2] × (h div 2 ^32)) div 2^32)
481
477
impl Hash for HS1 {
482
478
fn hash ( & self , kN : & [ u32 ] , kP : & u64 , kA : & [ u64 ] , M : & [ u8 ] ) -> Vec < u8 > {
479
+ const T61_1 : u64 = ( 1 << 61 ) - 1 ;
480
+ const T60_1 : u64 = ( 1 << 60 ) - 1 ;
483
481
let n: u32 ;
484
482
let Mi : Chunks < u8 > ;
485
483
let mut Y : Vec < u8 > ;
486
- let mut a: Vec < BigInt > = Vec :: new ( ) ;
487
- let mut h: BigInt ;
488
- let mut m: BigInt ; // m is set to one of two moduli, each reused rather than recomputed.
484
+ let mut h: u64 = 1 ;
485
+
486
+ fn mod60 ( a : u64 ) -> u64 {
487
+ ( a >> 60 ) + ( a & T60_1 )
488
+ }
489
+
490
+ fn mod61 ( a : u64 ) -> u64 {
491
+ ( a >> 61 ) + ( a & T61_1 )
492
+ }
493
+
494
+ fn poly_step ( a : u64 , m : u64 , k : u64 ) -> u64 {
495
+ let tmp = mod61 ( ( u128:: new ( a) . wrapping_mul ( u128:: new ( k) ) ) . low64 ( ) ) ;
496
+ tmp + m
497
+ }
498
+
499
+ fn poly_finalize ( mut a : u64 ) -> u64 {
500
+ a = ( a & T61_1 ) + ( a >> 61 ) ;
501
+ if a == T61_1 {
502
+ 0
503
+ } else {
504
+ a
505
+ }
506
+ }
489
507
490
508
// 1. n = max(⌈|M|/b⌉, 1)
491
509
n = std:: cmp:: max ( M . len ( ) as u32 / self . parameters . b as u32 , 1 ) ;
492
510
493
511
// 2. M_1 || M_2 || … || M_n = M and |M_i| = b for each 1 ≤ i ≤ n.
494
512
Mi = M . chunks ( self . parameters . b as usize ) ;
495
513
514
+ debug_assert ! ( Mi . clone( ) . count( ) == n as usize ) ;
515
+
496
516
// 3. m_i = toInts(4, pad(16, M_i)) for each 1 ≤ i ≤ n.
497
- for ( _ , chunk) in Mi . enumerate ( ) {
517
+ for ( i , chunk) in Mi . enumerate ( ) {
498
518
let mi: Vec < u32 > = toInts4 ( & pad ( 16 , & chunk) ) . unwrap ( ) ;
499
519
// 4. a_i = NH(kN, m_i) mod 2^60 + (|M_i| mod 16) for each 1 ≤ i ≤ n.
500
- a. push ( NH ( kN, & mi) + BigInt :: from_u8 ( self . parameters . b % 16u8 ) . unwrap ( ) ) ;
501
- }
502
- // 5. h = kP^n + (a_1 × kP^(n-1)) + (a_2 × kP^(n-2)) + ... + (a_n × kP^0) mod (2^61 - 1)
503
- h = u64toBI ! ( ( * kP as u64 ) . pow( n) % 2u64 . pow( 61 ) - 1 ) ;
504
- m = u64toBI ! ( 2u64 . pow( 61 ) - 1 ) ;
505
- for ( ai, j) in a. iter ( ) . zip ( n as i32 ..0 ) {
506
- h = h + ( ai % m. clone ( ) ) * ( u64toBI ! ( kP. pow( j as u32 ) ) % m. clone ( ) ) % m. clone ( ) ;
520
+ let ai: u64 = NH ( kN, & mi) ; // + ((self.parameters.b % 16u8) as u64);
521
+ // 5. h = kP^n + (a_1 × kP^(n-1)) + (a_2 × kP^(n-2)) + ... + (a_n × kP^0) mod (2^61 - 1)
522
+ // (computed via horner's method)
523
+ h = poly_step ( h, mod60 ( ai) , * kP) ;
507
524
}
525
+ h = poly_finalize ( h) ;
508
526
// 6. if (t ≤ 4) Y = toStr(8, h)
509
527
if self . parameters . t <= 4 {
510
- Y = toStr ( 8 , & ( h . to_u64 ( ) . unwrap ( ) as usize ) ) ;
528
+ Y = toStr ( 8 , h as usize ) ; // XXX cmr: I don't like the look of that 'as usize'
511
529
Y . truncate ( 8 ) ;
512
530
} else {
513
531
// 7. else Y = toStr(4, (kA[0] + kA[1] × (h mod 2^32) + kA[2] × (h div 2 ^32)) div 2^32)
514
- m = u64toBI ! ( 2u64 . pow( 32 ) ) ;
515
- Y = toStr ( 4 ,
516
- & ( ( ( u64toBI ! ( kA[ 0 ] . clone( ) ) +
517
- u64toBI ! ( kA[ 1 ] . clone( ) ) * ( h. clone ( ) % m. clone ( ) ) +
518
- u64toBI ! ( kA[ 2 ] . clone( ) ) * ( h. clone ( ) / m. clone ( ) ) ) /
519
- m. clone ( ) )
520
- . to_u64 ( )
521
- . unwrap ( ) as usize ) ) ;
532
+ let m: u64 = 1 << 32 ;
533
+ let div: u64 = h / m;
534
+ let mod_: u64 = h % m;
535
+
536
+ let tmp = ( kA[ 0 ] . wrapping_add ( kA[ 1 ] . wrapping_mul ( mod_) ) . wrapping_add ( kA[ 2 ] . wrapping_mul ( div) ) ) / m;
537
+ Y = toStr ( 4 , tmp as usize ) ; // XXX cmr: I don't like the look of that 'as usize'
522
538
Y . truncate ( 4 ) ;
523
539
}
524
540
Y
@@ -561,7 +577,7 @@ impl Encrypt for HS1 {
561
577
let C : Vec < u8 > ;
562
578
563
579
k = self . subkeygen ( & take32 ( K ) ) ;
564
- m = [ pad ( 16 , & A ) , pad ( 16 , & M ) , toStr ( 8 , & A . len ( ) ) , toStr ( 8 , & M . len ( ) ) ] . concat ( ) ;
580
+ m = [ pad ( 16 , & A ) , pad ( 16 , & M ) , toStr ( 8 , A . len ( ) ) , toStr ( 8 , M . len ( ) ) ] . concat ( ) ;
565
581
566
582
// XXX_QUESTION: Here we are supposed to use `l` as the final parameter to prf(). However,
567
583
// because y must equal 32 — as noted in a XXX_QUESTION above in prf() — we can only do this
@@ -638,7 +654,7 @@ impl Decrypt for HS1 {
638
654
& C ,
639
655
& self . prf ( & k, & T , N , ( 64 + C . len ( ) ) as i64 ) [ 64 ..C . len ( ) ] ) ;
640
656
M = out. to_vec ( ) ;
641
- m = [ pad ( 16 , & A ) , pad ( 16 , & M ) , toStr ( 8 , & A . len ( ) ) , toStr ( 8 , & M . len ( ) ) ] . concat ( ) ;
657
+ m = [ pad ( 16 , & A ) , pad ( 16 , & M ) , toStr ( 8 , A . len ( ) ) , toStr ( 8 , M . len ( ) ) ] . concat ( ) ;
642
658
t = self . prf ( & k, & m, N , self . parameters . l as i64 ) ;
643
659
644
660
if * T == t {
@@ -649,18 +665,12 @@ impl Decrypt for HS1 {
649
665
}
650
666
}
651
667
652
- // XXX_QUESTION: We moved the `mod 2^60` from hash() to NH() for simplicity… should this be changed
653
- // in the spec?
654
- //
655
- // XXX_QUESTION: This is the only place in HS1-SIV which requires a bignum… everything else can get
656
- // away with utilising either u32 or u64. Should/can this be restructured to avoid needing a bignum?
657
- //
658
668
/// Given vectors of integers, `v1` and `v2`, returns the result of the following algorithm:
659
669
///
660
670
/// ```text
661
- /// n/4 ⎛ ⎞
662
- /// NH(v1, v2) = Σ ⎜(v1[4i-3]+v2[4i-3]) × (v1[4i-1]+v2[4i-1]) +⎟
663
- /// i=1 ⎝(v1[4i-2]+v2[4i-2]) × (v1[4i]+v2[4i]) ⎠
671
+ /// n/4 ⎛ ⎞
672
+ /// NH(v1, v2) = Σ ⎜(v1[4i-3]+v2[4i-3] mod 2^32 ) × (v1[4i-1]+v2[4i-1] mod 2^32 ) +⎟ mod 2^64
673
+ /// i=1 ⎝(v1[4i-2]+v2[4i-2] mod 2^32 ) × (v1[4i ]+v2[4i ] mod 2^32) ⎠
664
674
/// ```
665
675
/// where `n = min(v1.len(), v2.len())` and is alway a multiple of 4.
666
676
///
@@ -675,21 +685,24 @@ impl Decrypt for HS1 {
675
685
/// let v2: Vec<u32> = vec![543516756, 2003792483, 1768711712, 1629516645,
676
686
/// 1768759412, 1734962788, 3044456, 0];
677
687
///
678
- /// assert_eq!(NH(&v1, &v2).to_u64().unwrap() , 162501409595406698u64);
688
+ /// assert_eq!(NH(&v1, &v2), 162501409595406698u64);
679
689
/// ```
680
- pub fn NH ( v1 : & [ u32 ] , v2 : & [ u32 ] ) -> BigInt {
681
- let mut sum: BigInt = BigInt :: from_usize ( 0 ) . unwrap ( ) ;
682
- let m: BigInt = BigInt :: from_u64 ( 2u64 . pow ( 60 ) ) . unwrap ( ) ;
683
- let bn1: Vec < BigInt > = v1. iter ( ) . map ( |x| x. to_bigint ( ) . unwrap ( ) ) . collect ( ) ;
684
- let bn2: Vec < BigInt > = v2. iter ( ) . map ( |x| x. to_bigint ( ) . unwrap ( ) ) . collect ( ) ;
685
-
686
- for i in 1 ..std:: cmp:: min ( bn1. len ( ) , bn2. len ( ) ) / 4 {
687
- sum = sum +
688
- ( ( ( & bn1[ 4 * i - 3 ] + & bn2[ 4 * i - 3 ] ) * ( & bn1[ 4 * i - 1 ] + & bn2[ 4 * i - 1 ] ) +
689
- ( & bn1[ 4 * i - 2 ] + & bn2[ 4 * i - 2 ] ) * ( & bn1[ 4 * i] + & bn2[ 4 * i] ) ) %
690
- & m) ;
690
+ pub fn NH ( v1 : & [ u32 ] , v2 : & [ u32 ] ) -> u64 {
691
+ let mut sum: Wrapping < u64 > = Wrapping ( 0 ) ;
692
+
693
+ for i in 1 ..std:: cmp:: min ( v1. len ( ) , v2. len ( ) ) / 4 {
694
+ // XXX cmr: RIP debug perf.
695
+ let va = Wrapping ( v1[ 4 * i - 3 ] ) ;
696
+ let vb = Wrapping ( v2[ 4 * i - 3 ] ) ;
697
+ let vc = Wrapping ( v1[ 4 * i - 2 ] ) ;
698
+ let vd = Wrapping ( v2[ 4 * i - 2 ] ) ;
699
+ let ve = Wrapping ( v1[ 4 * i - 1 ] ) ;
700
+ let vf = Wrapping ( v2[ 4 * i - 1 ] ) ;
701
+ let vg = Wrapping ( v1[ 4 * i] ) ;
702
+ let vh = Wrapping ( v2[ 4 * i] ) ;
703
+ sum += ( Wrapping ( ( va + vb) . 0 as u64 ) * Wrapping ( ( ve + vf) . 0 as u64 ) ) + ( Wrapping ( ( vc + vd) . 0 as u64 ) * Wrapping ( ( vg + vh) . 0 as u64 ) ) ;
691
704
}
692
- sum
705
+ sum. 0
693
706
}
694
707
695
708
// -------------------------------------------------------------------------------------------------
@@ -745,7 +758,7 @@ fn pad(multiple: usize, input: &[u8]) -> Vec<u8> {
745
758
/// let s3: Vec<u8> = toStr(4, &4294967295);
746
759
/// assert!(vec![255, 255, 255, 255] == s3);
747
760
/// ```
748
- pub fn toStr < ' a > ( n : isize , x : & ' a usize ) -> Vec < u8 > {
761
+ pub fn toStr ( n : isize , x : usize ) -> Vec < u8 > {
749
762
let binary: String = format ! ( "{:b}" , x. to_le( ) ) ;
750
763
let len: isize = n * 8isize - binary. len ( ) as isize ;
751
764
let bits = ( 0 ..binary. len ( ) ) . map ( |i| binary. chars ( ) . nth ( i) . unwrap ( ) == '1' ) ;
@@ -852,7 +865,11 @@ fn take32<'a>(x: &'a [u8]) -> [u8; 32] {
852
865
#[ cfg( test) ]
853
866
mod tests {
854
867
use hs1:: * ;
868
+ use std;
869
+ use quickcheck:: TestResult ;
855
870
use std:: iter:: repeat;
871
+ use num:: bigint:: { BigInt , ToBigInt } ;
872
+ use num:: traits:: { FromPrimitive , ToPrimitive } ;
856
873
857
874
static KEY_32_BYTES : [ u8 ; 32 ] = [ 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 , 0x08 , 0x09 , 0x10 ,
858
875
0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 , 0x18 , 0x19 , 0x20 ,
@@ -872,32 +889,32 @@ mod tests {
872
889
#[ test]
873
890
fn test_hs1_toStr_toInts4_3 ( ) {
874
891
let orig: usize = 3 ;
875
- assert_eq ! ( toInts4( & toStr( 4 , & orig) ) . unwrap( ) [ 0 ] , orig as u32 ) ;
892
+ assert_eq ! ( toInts4( & toStr( 4 , orig) ) . unwrap( ) [ 0 ] , orig as u32 ) ;
876
893
}
877
894
#[ test]
878
895
fn test_hs1_toStr_toInts4_256 ( ) {
879
896
let orig: usize = 256 ;
880
- assert_eq ! ( toInts4( & toStr( 4 , & orig) ) . unwrap( ) [ 0 ] , orig as u32 ) ;
897
+ assert_eq ! ( toInts4( & toStr( 4 , orig) ) . unwrap( ) [ 0 ] , orig as u32 ) ;
881
898
}
882
899
#[ test]
883
900
fn test_hs1_toStr_toInts4_4294967295 ( ) {
884
901
let orig: usize = 4294967295 ;
885
- assert_eq ! ( toInts4( & toStr( 4 , & orig) ) . unwrap( ) [ 0 ] , orig as u32 ) ;
902
+ assert_eq ! ( toInts4( & toStr( 4 , orig) ) . unwrap( ) [ 0 ] , orig as u32 ) ;
886
903
}
887
904
#[ test]
888
905
fn test_hs1_toStr_toInts8_3 ( ) {
889
906
let orig: usize = 3 ;
890
- assert_eq ! ( toInts8( & toStr( 8 , & orig) ) . unwrap( ) [ 0 ] , orig as u64 ) ;
907
+ assert_eq ! ( toInts8( & toStr( 8 , orig) ) . unwrap( ) [ 0 ] , orig as u64 ) ;
891
908
}
892
909
#[ test]
893
910
fn test_hs1_toStr_toInts8_256 ( ) {
894
911
let orig: usize = 256 ;
895
- assert_eq ! ( toInts8( & toStr( 8 , & orig) ) . unwrap( ) [ 0 ] , orig as u64 ) ;
912
+ assert_eq ! ( toInts8( & toStr( 8 , orig) ) . unwrap( ) [ 0 ] , orig as u64 ) ;
896
913
}
897
914
#[ test]
898
915
fn test_hs1_toStr_toInts8_18446744073709551615 ( ) {
899
916
let orig: usize = 18446744073709551615 ;
900
- assert_eq ! ( toInts8( & toStr( 8 , & orig) ) . unwrap( ) [ 0 ] , orig as u64 ) ;
917
+ assert_eq ! ( toInts8( & toStr( 8 , orig) ) . unwrap( ) [ 0 ] , orig as u64 ) ;
901
918
}
902
919
903
920
#[ test]
@@ -1063,4 +1080,28 @@ mod tests {
1063
1080
hs1. decrypt ( & KEY_32_BYTES [ ..] , & a, & e, & associated_data ( ) , & nonce ( ) ) ;
1064
1081
assert_eq ! ( & d. unwrap( ) [ ..] , & msg( ) [ ..] ) ;
1065
1082
}
1083
+
1084
+ fn old_nh ( v1 : & [ u32 ] , v2 : & [ u32 ] ) -> u64 {
1085
+ let mut sum: BigInt = BigInt :: from_usize ( 0 ) . unwrap ( ) ;
1086
+ let m: BigInt = BigInt :: from_u64 ( 2u64 . pow ( 60 ) ) . unwrap ( ) ;
1087
+ let bn1: Vec < BigInt > = v1. iter ( ) . map ( |x| x. to_bigint ( ) . unwrap ( ) ) . collect ( ) ;
1088
+ let bn2: Vec < BigInt > = v2. iter ( ) . map ( |x| x. to_bigint ( ) . unwrap ( ) ) . collect ( ) ;
1089
+
1090
+ for i in 1 ..std:: cmp:: min ( bn1. len ( ) , bn2. len ( ) ) / 4 {
1091
+ sum = sum +
1092
+ ( ( ( & bn1[ 4 * i - 3 ] + & bn2[ 4 * i - 3 ] ) * ( & bn1[ 4 * i - 1 ] + & bn2[ 4 * i - 1 ] ) +
1093
+ ( & bn1[ 4 * i - 2 ] + & bn2[ 4 * i - 2 ] ) * ( & bn1[ 4 * i] + & bn2[ 4 * i] ) ) %
1094
+ & m) ;
1095
+ }
1096
+ sum. to_u64 ( ) . unwrap ( )
1097
+ }
1098
+
1099
+ quickcheck ! {
1100
+ fn nh_optzn_is_correct( v1: Vec <u32 >, v2: Vec <u32 >) -> TestResult {
1101
+ if v1. len( ) != v2. len( ) || std:: cmp:: min( v1. len( ) , v2. len( ) ) % 4 != 0 {
1102
+ return TestResult :: discard( ) ;
1103
+ }
1104
+ return TestResult :: from_bool( super :: NH ( & v1, & v2) == old_nh( & v1, & v2) ) ;
1105
+ }
1106
+ }
1066
1107
}
0 commit comments