Skip to content

Commit

Permalink
Fix(modexp): remove leading zeros when factoring modulus (#760)
Browse files Browse the repository at this point in the history
## Description

There was a small bug in the new modexp implementation where sometimes
factoring an even modulus into and odd number times a power of two left
a leading zero on the odd number. This threw off some of the other parts
of the algorithm, leading to incorrect results. This PR fixes the issue
and adds test cases for it.
  • Loading branch information
birchmd authored May 12, 2023
1 parent cd4655b commit d6b1399
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 0 deletions.
27 changes: 27 additions & 0 deletions engine-modexp/src/mpnat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ impl MPNat {
tmp.digits
.copy_from_slice(&modulus.digits[trailing_zeros..]);
}
while tmp.digits.last() == Some(&0) {
tmp.digits.pop();
}
tmp
};
debug_assert!(power_of_two.is_power_of_two(), "Factored out power of two");
Expand Down Expand Up @@ -525,6 +528,30 @@ fn test_modpow_even() {
0x3fe14e11392c6c6be8efe956c965d5af,
);

let base: Vec<u8> = vec![
0x36, 0xAB, 0xD4, 0x52, 0x4E, 0x89, 0xA3, 0x4C, 0x89, 0xC4, 0x20, 0x94, 0x25, 0x47, 0xE1,
0x2C, 0x7B, 0xE1,
];
let exponent: Vec<u8> = vec![0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x17, 0xEA, 0x78];
let modulus: Vec<u8> = vec![
0x02, 0xF0, 0x75, 0x8C, 0x6A, 0x04, 0x20, 0x09, 0x55, 0xB6, 0x49, 0xC3, 0x57, 0x22, 0xB8,
0x00, 0x00, 0x00, 0x00,
];
let result = crate::modexp(&base, &exponent, &modulus);
assert_eq!(
result,
vec![2, 63, 79, 118, 41, 54, 235, 9, 115, 212, 107, 110, 173, 181, 157, 104, 208, 97, 1]
);

let base = hex::decode("36abd4524e89a34c89c420942547e12c7be1").unwrap();
let exponent = hex::decode("01000000000517ea78").unwrap();
let modulus = hex::decode("02f0758c6a04200955b649c35722b800000000").unwrap();
let result = crate::modexp(&base, &exponent, &modulus);
assert_eq!(
hex::encode(result),
"023f4f762936eb0973d46b6eadb59d68d06101"
);

fn check_modpow_even(base: u128, exp: u128, modulus: u128, expected: u128) {
let mut x = MPNat::from_big_endian(&base.to_be_bytes());
let m = MPNat::from_big_endian(&modulus.to_be_bytes());
Expand Down
19 changes: 19 additions & 0 deletions engine-tests/src/tests/modexp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,25 @@ fn bench_modexp() {
"Aurora not least:\n{result:?}"
);

let input = BenchInput {
base: vec![
0x36, 0xAB, 0xD4, 0x52, 0x4E, 0x89, 0xA3, 0x4C, 0x89, 0xC4, 0x20, 0x94, 0x25, 0x47,
0xE1, 0x2C, 0x7B, 0xE1,
],
exp: vec![0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0x17, 0xEA, 0x78],
modulus: vec![
0x02, 0xF0, 0x75, 0x8C, 0x6A, 0x04, 0x20, 0x09, 0x55, 0xB6, 0x49, 0xC3, 0x57, 0x22,
0xB8, 0x00, 0x00, 0x00, 0x00,
],
num_iters: Some(1_000),
};
let result = context.bench(&input);
assert_eq!(
result.least(),
Implementation::Aurora,
"Aurora not least:\n{result:?}"
);

// Typical example with U256-sized inputs.
let input = BenchInput::random(32);
let result = context.bench(&input);
Expand Down

0 comments on commit d6b1399

Please sign in to comment.