Skip to content

Commit f4e105c

Browse files
committed
solved the mystery of ct_gt, but at what cost [skip ci]
1 parent 83c1012 commit f4e105c

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

curve25519-dalek/src/backend/serial/u64/field.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,4 +597,24 @@ impl FieldElement51 {
597597

598598
Choice::from(c_gt as u8)
599599
}
600+
601+
/// Returns 1 if self is greater than the other and 0 otherwise
602+
// strategy: check if b-a overflows. if it does not overflow, then a was larger
603+
pub(crate) fn mpn_sub_n(&self, other: &Self) -> Choice {
604+
let mut _ul = 0_u64;
605+
let mut _vl = 0_u64;
606+
let mut _rl = 0_u64;
607+
608+
let mut cy = 0_u64;
609+
for i in 0..5 {
610+
_ul = self.0[i];
611+
_vl = other.0[i];
612+
613+
let (_sl, _cy1) = _ul.overflowing_sub(_vl);
614+
let (_rl, _cy2) = _sl.overflowing_sub(cy);
615+
cy = _cy1 as u64 | _cy2 as u64;
616+
}
617+
618+
Choice::from((cy != 0_u64) as u8)
619+
}
600620
}

curve25519-dalek/src/elligator2.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ impl MapToPointVariant for Randomized {
231231
}
232232

233233
#[cfg(feature = "digest")]
234+
/// In general this mode should **NEVER** be used unless there is a very specific
235+
/// reason to do so as it has multiple serious known flaws.
236+
///
234237
/// Converts between a point on elliptic curve E (Curve25519) and an element of
235238
/// the finite field F over which E is defined. Supports older implementations
236239
/// with a common implementation bug (Signal, Kleshni-C).
@@ -244,9 +247,6 @@ impl MapToPointVariant for Randomized {
244247
/// high-order two bits set. The kleshni C and Signal implementations are examples
245248
/// of libraries that don't always use the least square root.
246249
///
247-
/// In general this mode should NOT be used unless there is a very specific
248-
/// reason to do so.
249-
///
250250
// We return the LSR for to_representative values. This is here purely for testing
251251
// compatability and ensuring that we understand the subtle differences that can
252252
// influence proper implementation.
@@ -266,10 +266,28 @@ impl MapToPointVariant for Legacy {
266266
CtOption::new(point, Choice::from(1))
267267
}
268268

269+
// There is a bug in the kleshni implementation where it
270+
// takes a sortcut when computng greater than for field elemements.
271+
// For the purpose of making tests pass matching the bugged implementation
272+
// I am adding the bug here intentionally. Legacy is not exposed and
273+
// should not be exposed as it is obviously flawed in multiple ways.
274+
//
275+
// What we want is:
276+
// If root - (p - 1) / 2 < 0, root := -root
277+
// This is not equivalent to:
278+
// if root > (p - 1)/2 root := -root
279+
//
269280
fn to_representative(point: &[u8; 32], _tweak: u8) -> CtOption<[u8; 32]> {
270281
let pubkey = EdwardsPoint::mul_base_clamped(*point);
271282
let v_in_sqrt = v_in_sqrt_pubkey_edwards(&pubkey);
283+
272284
point_to_representative(&MontgomeryPoint(*point), v_in_sqrt.into())
285+
286+
// let divide_minus_p_1_2 = FieldElement::from_bytes(&DIVIDE_MINUS_P_1_2_BYTES);
287+
// let did_negate = divide_minus_p_1_2.ct_gt(&b);
288+
// let should_negate = Self::gt(&b, &divide_minus_p_1_2);
289+
// FieldElement::conditional_negate(&mut b, did_negate ^ should_negate);
290+
// CtOption::new(b.as_bytes(), c)
273291
}
274292
}
275293

@@ -583,7 +601,8 @@ pub(crate) fn point_to_representative(
583601
let mut b = FieldElement::conditional_select(&r1, &r0, Choice::from(v_in_sqrt as u8));
584602

585603
// If root > (p - 1) / 2, root := -root
586-
let negate = divide_minus_p_1_2.ct_gt(&b);
604+
// let negate = divide_minus_p_1_2.ct_gt(&b);
605+
let negate = divide_minus_p_1_2.mpn_sub_n(&b);
587606
FieldElement::conditional_negate(&mut b, negate);
588607

589608
CtOption::new(b.as_bytes(), is_encodable)
@@ -677,7 +696,9 @@ pub(crate) fn v_in_sqrt_pubkey_edwards(pubkey: &EdwardsPoint) -> Choice {
677696
let v = &(&t0 * &inv1) * &(&pubkey.Z * &sqrt_minus_a_plus_2);
678697

679698
// is v <= (q-1)/2 ?
680-
divide_minus_p_1_2.ct_gt(&v)
699+
// divide_minus_p_1_2.ct_gt(&v)
700+
// v.is_negative()
701+
divide_minus_p_1_2.mpn_sub_n(&v)
681702
}
682703

683704
// ============================================================================

0 commit comments

Comments
 (0)