Skip to content

Commit 242b6af

Browse files
committed
Optimize the constant time Scalar::is_canonical check
1 parent c823543 commit 242b6af

File tree

2 files changed

+13
-8
lines changed

2 files changed

+13
-8
lines changed

curve25519-dalek/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ required-features = ["alloc", "rand_core"]
5050
cfg-if = "1"
5151
rand_core = { version = "0.6.4", default-features = false, optional = true }
5252
digest = { version = "0.10", default-features = false, optional = true }
53-
subtle = { version = "2.3.0", default-features = false }
53+
subtle = { version = "2.4", default-features = false }
5454
serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] }
5555
zeroize = { version = "1", default-features = false, optional = true }
5656

curve25519-dalek/src/scalar.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,8 @@ use digest::generic_array::typenum::U64;
132132
#[cfg(feature = "digest")]
133133
use digest::Digest;
134134

135-
use subtle::Choice;
136-
use subtle::ConditionallySelectable;
137-
use subtle::ConstantTimeEq;
138-
use subtle::CtOption;
135+
use subtle::{Choice, CtOption};
136+
use subtle::{ConditionallySelectable, ConstantTimeEq, ConstantTimeGreater};
139137

140138
#[cfg(feature = "zeroize")]
141139
use zeroize::Zeroize;
@@ -253,9 +251,8 @@ impl Scalar {
253251
/// if `bytes` is a canonical byte representation modulo the group order \\( \ell \\);
254252
/// - `None` if `bytes` is not a canonical byte representation.
255253
pub fn from_canonical_bytes(bytes: [u8; 32]) -> CtOption<Scalar> {
256-
let high_bit_unset = (bytes[31] >> 7).ct_eq(&0);
257254
let candidate = Scalar { bytes };
258-
CtOption::new(candidate, high_bit_unset & candidate.is_canonical())
255+
CtOption::new(candidate, candidate.is_canonical())
259256
}
260257

261258
/// Construct a `Scalar` from the low 255 bits of a 256-bit integer. This breaks the invariant
@@ -1125,7 +1122,15 @@ impl Scalar {
11251122
/// Check whether this `Scalar` is the canonical representative mod \\(\ell\\). This is not
11261123
/// public because any `Scalar` that is publicly observed is reduced, by scalar invariant #2.
11271124
fn is_canonical(&self) -> Choice {
1128-
self.ct_eq(&self.reduce())
1125+
let mut over = Choice::from(0);
1126+
let mut under = Choice::from(0);
1127+
for (this, l) in self.unpack().0.iter().zip(&constants::L.0).rev() {
1128+
let gt = this.ct_gt(l);
1129+
let eq = this.ct_eq(l);
1130+
under |= (!gt & !eq) & !over;
1131+
over |= gt;
1132+
}
1133+
under
11291134
}
11301135
}
11311136

0 commit comments

Comments
 (0)