Skip to content

Commit ca97af6

Browse files
authored
Merge pull request #345 from fortanix/yx/ecp_refactor
Correct `EcPoint` const time comparison and better comments and namings
2 parents 75cb9b7 + eb2cf13 commit ca97af6

File tree

4 files changed

+129
-21
lines changed

4 files changed

+129
-21
lines changed

mbedtls/benches/ecp_eq_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ fn ecp_equal(a: &EcPoint, b: &EcPoint) {
66
}
77

88
fn ecp_equal_const_time(a: &EcPoint, b: &EcPoint) {
9-
assert!(!a.eq_const_time(&b));
9+
assert!(!a.eq_const_time(&b).unwrap());
1010
}
1111

1212
fn criterion_benchmark(c: &mut Criterion) {

mbedtls/src/bignum/mod.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,25 @@ impl Mpi {
139139
}
140140
}
141141

142+
/// Checks if an [`Mpi`] is less than the other in constant time.
143+
///
144+
/// Will return [`Error::MpiBadInputData`] if the allocated length of the two input [`Mpi`]s is not the same.
145+
pub fn less_than_const_time(&self, other: &Mpi) -> Result<bool> {
146+
mpi_inner_less_than_const_time(&self.inner, &other.inner)
147+
}
148+
149+
/// Compares an [`Mpi`] with the other in constant time.
150+
///
151+
/// Will return [`Error::MpiBadInputData`] if the allocated length of the two input [`Mpi`]s is not the same.
152+
pub fn cmp_const_time(&self, other: &Mpi) -> Result<Ordering> {
153+
mpi_inner_cmp_const_time(&self.inner, &other.inner)
154+
}
155+
156+
/// Checks equalness with the other in constant time.
157+
pub fn eq_const_time(&self, other: &Mpi) -> Result<bool> {
158+
mpi_inner_eq_const_time(&self.inner, &other.inner)
159+
}
160+
142161
pub fn as_u32(&self) -> Result<u32> {
143162
if self.bit_length()? > 32 {
144163
// Not exactly correct but close enough
@@ -409,6 +428,35 @@ impl Mpi {
409428
}
410429
}
411430

431+
pub(super) fn mpi_inner_eq_const_time(x: &mpi, y: &mpi) -> core::prelude::v1::Result<bool, Error> {
432+
match mpi_inner_cmp_const_time(x, y) {
433+
Ok(order) => Ok(order == Ordering::Equal),
434+
Err(Error::MpiBadInputData) => Ok(false),
435+
Err(e) => Err(e),
436+
}
437+
}
438+
439+
fn mpi_inner_cmp_const_time(x: &mpi, y: &mpi) -> Result<Ordering> {
440+
let less = mpi_inner_less_than_const_time(x, y);
441+
let more = mpi_inner_less_than_const_time(y, x);
442+
match (less, more) {
443+
(Ok(true), Ok(false)) => Ok(Ordering::Less),
444+
(Ok(false), Ok(true)) => Ok(Ordering::Greater),
445+
(Ok(false), Ok(false)) => Ok(Ordering::Equal),
446+
(Ok(true), Ok(true)) => unreachable!(),
447+
(Err(e), _) => Err(e),
448+
(Ok(_), Err(e)) => Err(e),
449+
}
450+
}
451+
452+
fn mpi_inner_less_than_const_time(x: &mpi, y: &mpi) -> Result<bool> {
453+
let mut r = 0;
454+
unsafe {
455+
mpi_lt_mpi_ct(x, y, &mut r).into_result()?;
456+
};
457+
Ok(r == 1)
458+
}
459+
412460
impl Ord for Mpi {
413461
fn cmp(&self, other: &Mpi) -> Ordering {
414462
let r = unsafe { mpi_cmp_mpi(&self.inner, &other.inner) };
@@ -709,3 +757,53 @@ impl ShrAssign<usize> for Mpi {
709757
// mbedtls_mpi_sub_abs
710758
// mbedtls_mpi_mod_int
711759
// mbedtls_mpi_gcd
760+
761+
#[cfg(test)]
762+
mod tests {
763+
use super::*;
764+
765+
#[test]
766+
fn test_less_than_const_time() {
767+
let mpi1 = Mpi::new(10).unwrap();
768+
let mpi2 = Mpi::new(20).unwrap();
769+
770+
assert_eq!(mpi1.less_than_const_time(&mpi2), Ok(true));
771+
772+
assert_eq!(mpi1.less_than_const_time(&mpi1), Ok(false));
773+
774+
assert_eq!(mpi2.less_than_const_time(&mpi1), Ok(false));
775+
776+
// Check: function returns `Error::MpiBadInputData` if the allocated length of the two input Mpis is not the same.
777+
let mpi3 = Mpi::from_binary(&[
778+
0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
779+
])
780+
.unwrap();
781+
assert_eq!(mpi3.less_than_const_time(&mpi3), Ok(false));
782+
assert_eq!(mpi2.less_than_const_time(&mpi3), Err(Error::MpiBadInputData));
783+
}
784+
785+
#[test]
786+
fn test_cmp_const_time() {
787+
let mpi1 = Mpi::new(10).unwrap();
788+
let mpi2 = Mpi::new(20).unwrap();
789+
790+
assert_eq!(mpi1.cmp_const_time(&mpi2), Ok(Ordering::Less));
791+
792+
let mpi3 = Mpi::new(10).unwrap();
793+
assert_eq!(mpi1.cmp_const_time(&mpi3), Ok(Ordering::Equal));
794+
795+
let mpi4 = Mpi::new(5).unwrap();
796+
assert_eq!(mpi1.cmp_const_time(&mpi4), Ok(Ordering::Greater));
797+
}
798+
799+
#[test]
800+
fn test_eq_const_time() {
801+
let mpi1 = Mpi::new(10).unwrap();
802+
let mpi2 = Mpi::new(10).unwrap();
803+
804+
assert_eq!(mpi1.eq_const_time(&mpi2), Ok(true));
805+
806+
let mpi3 = Mpi::new(20).unwrap();
807+
assert_eq!(mpi1.eq_const_time(&mpi3), Ok(false));
808+
}
809+
}

mbedtls/src/ecp/mod.rs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,6 @@ impl EcPoint {
309309
Mpi::copy(&self.inner.Y)
310310
}
311311

312-
pub fn z(&self) -> Result<Mpi> {
313-
Mpi::copy(&self.inner.Z)
314-
}
315-
316312
pub fn is_zero(&self) -> Result<bool> {
317313
/*
318314
mbedtls_ecp_is_zero takes arg as non-const for no particular reason
@@ -373,9 +369,14 @@ Please use `mul_with_rng` instead."
373369
///
374370
/// This function will return an error if:
375371
///
376-
/// * `k` is not a valid private key, or `self` is not a valid public key.
372+
/// * The scalar `k` is not valid as a private key, determined by mbedtls function [`mbedtls_ecp_check_privkey`].
373+
/// * The point `self` is not valid as a public key, determined by mbedtls function [`mbedtls_ecp_check_pubkey`].
377374
/// * Memory allocation fails.
378-
/// * Any other kind of failure occurs during the execution of the underlying `mbedtls_ecp_mul` function.
375+
/// * Any other kind of failure occurs during the execution of the underlying [`mbedtls_ecp_mul`] function.
376+
///
377+
/// [`mbedtls_ecp_check_pubkey`]: https://github.com/fortanix/rust-mbedtls/blob/main/mbedtls-sys/vendor/include/mbedtls/ecp.h#L1115-L1143
378+
/// [`mbedtls_ecp_check_privkey`]: https://github.com/fortanix/rust-mbedtls/blob/main/mbedtls-sys/vendor/include/mbedtls/ecp.h#L1145-L1165
379+
/// [`mbedtls_ecp_mul`]: https://github.com/fortanix/rust-mbedtls/blob/main/mbedtls-sys/vendor/include/mbedtls/ecp.h#L933-L971
379380
pub fn mul_with_rng<F: crate::rng::Random>(&self, group: &mut EcGroup, k: &Mpi, rng: &mut F) -> Result<EcPoint> {
380381
// Note: mbedtls_ecp_mul performs point validation itself so we skip that here
381382

@@ -433,13 +434,22 @@ Please use `mul_with_rng` instead."
433434
}
434435
}
435436

436-
/// This function compares two points in const time.
437-
pub fn eq_const_time(&self, other: &EcPoint) -> bool {
438-
unsafe {
439-
let x = mpi_cmp_mpi(&self.inner.X, &other.inner.X) == 0;
440-
let y = mpi_cmp_mpi(&self.inner.Y, &other.inner.Y) == 0;
441-
let z = mpi_cmp_mpi(&self.inner.Z, &other.inner.Z) == 0;
442-
x & y & z
437+
/// This function checks equalness of two points in const time.
438+
///
439+
/// The implementation is based on C mbedtls function [`mbedtls_ecp_point_cmp`].
440+
/// This new implementation ensures there is no shortcut when any of `x, y ,z` fields of two points is not equal.
441+
///
442+
/// [`mbedtls_ecp_point_cmp`]: https://github.com/fortanix/rust-mbedtls/blob/main/mbedtls-sys/vendor/library/ecp.c#L809-L825
443+
pub fn eq_const_time(&self, other: &EcPoint) -> Result<bool> {
444+
let x = crate::bignum::mpi_inner_eq_const_time(&self.inner.X, &other.inner.X);
445+
let y = crate::bignum::mpi_inner_eq_const_time(&self.inner.Y, &other.inner.Y);
446+
let z = crate::bignum::mpi_inner_eq_const_time(&self.inner.Z, &other.inner.Z);
447+
match (x, y, z) {
448+
(Ok(true), Ok(true), Ok(true)) => Ok(true),
449+
(Ok(_), Ok(_), Ok(_)) => Ok(false),
450+
(Ok(_), Ok(_), Err(e)) => Err(e),
451+
(Ok(_), Err(e), _) => Err(e),
452+
(Err(e), _, _) => Err(e),
443453
}
444454
}
445455

@@ -718,9 +728,9 @@ mod tests {
718728
assert!(g.eq(&g).unwrap());
719729
assert!(zero.eq(&zero).unwrap());
720730
assert!(!g.eq(&zero).unwrap());
721-
assert!(g.eq_const_time(&g));
722-
assert!(zero.eq_const_time(&zero));
723-
assert!(!g.eq_const_time(&zero));
731+
assert!(g.eq_const_time(&g).unwrap());
732+
assert!(zero.eq_const_time(&zero).unwrap());
733+
assert!(!g.eq_const_time(&zero).unwrap());
724734
}
725735

726736
#[test]

mbedtls/src/pk/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ impl Pk {
336336
#[deprecated(
337337
since = "0.12.3",
338338
note = "This function does not accept an RNG so it's vulnerable to side channel attacks.
339-
Please use `private_from_ec_components_with_rng` instead."
339+
Please use `private_from_ec_scalar_with_rng` instead."
340340
)]
341341
pub fn private_from_ec_components(mut curve: EcGroup, private_key: Mpi) -> Result<Pk> {
342342
let mut ret = Self::init();
@@ -378,8 +378,8 @@ Please use `private_from_ec_components_with_rng` instead."
378378
///
379379
/// * Fails to generate `EcPoint` from given EcGroup in `curve`.
380380
/// * The underlying C `mbedtls_pk_setup` function fails to set up the `Pk` context.
381-
/// * The `EcPoint::mul` function fails to generate the public key point.
382-
pub fn private_from_ec_components_with_rng<F: Random>(mut curve: EcGroup, private_key: Mpi, rng: &mut F) -> Result<Pk> {
381+
/// * The `EcPoint::mul_with_rng` function fails to generate the public key point.
382+
pub fn private_from_ec_scalar_with_rng<F: Random>(mut curve: EcGroup, private_key: Mpi, rng: &mut F) -> Result<Pk> {
383383
let mut ret = Self::init();
384384
let curve_generator = curve.generator()?;
385385
let public_point = curve_generator.mul_with_rng(&mut curve, &private_key, rng)?;
@@ -1266,7 +1266,7 @@ iy6KC991zzvaWY/Ys+q/84Afqa+0qJKQnPuy/7F5GkVdQA/lfbhi
12661266

12671267
assert_eq!(pem1, pem2);
12681268

1269-
let mut key_from_components = Pk::private_from_ec_components_with_rng(
1269+
let mut key_from_components = Pk::private_from_ec_scalar_with_rng(
12701270
secp256r1.clone(),
12711271
key1.ec_private().unwrap(),
12721272
&mut crate::test_support::rand::test_rng(),

0 commit comments

Comments
 (0)