3434//! [`EdwardsPoint`] objects themselves.
3535//!
3636//! ```rust
37+ //! # use hex::FromHex;
3738//! use rand::RngCore;
3839//! use curve25519_dalek::{MontgomeryPoint, EdwardsPoint, elligator2::{RFC9380, Randomized, MapToPointVariant}};
3940//!
4041//! // Montgomery Points can be mapped to and from elligator representatives
4142//! // using any algorithm variant.
4243//! let tweak = rand::thread_rng().next_u32() as u8;
43- //! let mont_point = MontgomeryPoint::default(); // example point known to be representable
44+ //! let mont_point = MontgomeryPoint::default(); // example private key point known to be representable
4445//! let r = mont_point.to_representative::<RFC9380>(tweak).unwrap();
46+ //! let pubkey = RFC9380::mul_base_clamped(mont_point.to_bytes()).to_montgomery();
4547//!
46- //! _ = MontgomeryPoint::from_representative::<RFC9380>(&r).unwrap();
48+ //! let derived_pubkey = MontgomeryPoint::from_representative::<RFC9380>(&r).unwrap();
49+ //! assert_eq!(pubkey, derived_pubkey);
4750//!
48- //! // Edwards Points can be transformed as well.
51+ //! // Points in byte format can be transformed
52+ //!
53+ //! // (example known representable point)
54+ //! let mut point = <[u8;32]>::from_hex("00deadbeef00deadbeef00deadbeef00deadbeef00deadbeef00deadbeef0124").unwrap();
55+ //! let tweak = rand::thread_rng().next_u32() as u8;
56+ //! let r = RFC9380::to_representative(&point, tweak).unwrap();
57+ //! let pubkey = RFC9380::mul_base_clamped(point).to_montgomery();
58+ //!
59+ //! let derived_pubkey = MontgomeryPoint::from_representative::<RFC9380>(&r).unwrap();
60+ //! assert_eq!(pubkey, derived_pubkey);
61+ //!
62+ //! // Edwards Points can be transformed as well. This example uses the Randomized variant.
4963//! let edw_point = EdwardsPoint::default(); // example point known to be representable
5064//! let r = edw_point.to_representative::<Randomized>(tweak).unwrap();
65+ //! let pubkey = Randomized::mul_base_clamped(edw_point.to_montgomery().to_bytes());
5166//!
52- //! _ = EdwardsPoint::from_representative::<Randomized>(&r).unwrap();
67+ //! let derived_pubkey = EdwardsPoint::from_representative::<Randomized>(&r).unwrap();
68+ //! assert_eq!(pubkey, derived_pubkey);
5369//! ```
5470//!
5571//! ### Generating Representable Points.
@@ -131,8 +147,7 @@ use crate::EdwardsPoint;
131147
132148use cfg_if:: cfg_if;
133149use subtle:: {
134- Choice , ConditionallyNegatable , ConditionallySelectable , ConstantTimeEq , ConstantTimeGreater ,
135- CtOption ,
150+ Choice , ConditionallyNegatable , ConditionallySelectable , ConstantTimeEq , CtOption ,
136151} ;
137152
138153/// bitmask for a single byte when clearing the high order two bits of a representative
@@ -141,10 +156,6 @@ pub(crate) const MASK_UNSET_BYTE: u8 = 0x3f;
141156pub ( crate ) const MASK_SET_BYTE : u8 = 0xc0 ;
142157
143158/// (p - 1) / 2 = 2^254 - 10
144- pub ( crate ) const DIVIDE_MINUS_P_1_2_BYTES : [ u8 ; 32 ] = [
145- 0xf6 , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
146- 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x3f ,
147- ] ;
148159
149160/// Common interface for the different ways to compute the elligator2 forward
150161/// and reverse transformations.
@@ -230,67 +241,6 @@ impl MapToPointVariant for Randomized {
230241 }
231242}
232243
233- #[ 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- ///
237- /// Converts between a point on elliptic curve E (Curve25519) and an element of
238- /// the finite field F over which E is defined. Supports older implementations
239- /// with a common implementation bug (Signal, Kleshni-C).
240- ///
241- /// In contrast to the [`RFC9380`] variant, `Legacy` does NOT assume that input values are always
242- /// going to be the least-square-root representation of the field element.
243- /// This is divergent from the specifications for both elligator2 and RFC 9380,
244- /// however, some older implementations miss this detail. This allows us to be
245- /// compatible with those alternate implementations if necessary, since the
246- /// resulting point will be different for inputs with either of the
247- /// high-order two bits set. The kleshni C and Signal implementations are examples
248- /// of libraries that don't always use the least square root.
249- ///
250- // We return the LSR for to_representative values. This is here purely for testing
251- // compatability and ensuring that we understand the subtle differences that can
252- // influence proper implementation.
253- pub struct Legacy ;
254-
255- #[ cfg( feature = "digest" ) ]
256- impl MapToPointVariant for Legacy {
257- fn from_representative ( representative : & [ u8 ; 32 ] ) -> CtOption < EdwardsPoint > {
258- let representative = FieldElement :: from_bytes ( representative) ;
259- let ( x, y) = map_fe_to_edwards ( & representative) ;
260- let point = EdwardsPoint {
261- X : x,
262- Y : y,
263- Z : FieldElement :: ONE ,
264- T : & x * & y,
265- } ;
266- CtOption :: new ( point, Choice :: from ( 1 ) )
267- }
268-
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- //
280- fn to_representative ( point : & [ u8 ; 32 ] , _tweak : u8 ) -> CtOption < [ u8 ; 32 ] > {
281- let pubkey = EdwardsPoint :: mul_base_clamped ( * point) ;
282- let v_in_sqrt = v_in_sqrt_pubkey_edwards ( & pubkey) ;
283-
284- 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, ÷_minus_p_1_2);
289- // FieldElement::conditional_negate(&mut b, did_negate ^ should_negate);
290- // CtOption::new(b.as_bytes(), c)
291- }
292- }
293-
294244// ===========================================================================
295245// Montgomery and Edwards Interfaces
296246// ===========================================================================
@@ -577,8 +527,6 @@ pub(crate) fn point_to_representative(
577527 point : & MontgomeryPoint ,
578528 v_in_sqrt : bool ,
579529) -> CtOption < [ u8 ; 32 ] > {
580- let divide_minus_p_1_2 = FieldElement :: from_bytes ( & DIVIDE_MINUS_P_1_2_BYTES ) ;
581-
582530 // a := point
583531 let a = & FieldElement :: from_bytes ( & point. 0 ) ;
584532 let a_neg = -a;
@@ -602,7 +550,8 @@ pub(crate) fn point_to_representative(
602550
603551 // If root > (p - 1) / 2, root := -root
604552 // let negate = divide_minus_p_1_2.ct_gt(&b);
605- let negate = divide_minus_p_1_2. mpn_sub_n ( & b) ;
553+ // let negate = divide_minus_p_1_2.mpn_sub_n(&b);
554+ let negate = b. is_negative ( ) ;
606555 FieldElement :: conditional_negate ( & mut b, negate) ;
607556
608557 CtOption :: new ( b. as_bytes ( ) , is_encodable)
@@ -678,8 +627,6 @@ pub(crate) fn v_in_sqrt(key_input: &[u8; 32]) -> Choice {
678627/// Determines if `V <= (p - 1)/2` for an EdwardsPoint (e.g an x25519 public key)
679628/// and returns a [`Choice`] indicating the result.
680629pub ( crate ) fn v_in_sqrt_pubkey_edwards ( pubkey : & EdwardsPoint ) -> Choice {
681- let divide_minus_p_1_2 = FieldElement :: from_bytes ( & DIVIDE_MINUS_P_1_2_BYTES ) ;
682-
683630 // sqrtMinusAPlus2 is sqrt(-(486662+2))
684631 let ( _, sqrt_minus_a_plus_2) = FieldElement :: sqrt_ratio_i (
685632 & ( & MONTGOMERY_A_NEG - & ( & FieldElement :: ONE + & FieldElement :: ONE ) ) ,
@@ -697,8 +644,8 @@ pub(crate) fn v_in_sqrt_pubkey_edwards(pubkey: &EdwardsPoint) -> Choice {
697644
698645 // is v <= (q-1)/2 ?
699646 // divide_minus_p_1_2.ct_gt(&v)
700- // v.is_negative( )
701- divide_minus_p_1_2 . mpn_sub_n ( & v )
647+ // divide_minus_p_1_2.mpn_sub_n(&v )
648+ !v . is_negative ( )
702649}
703650
704651// ============================================================================
@@ -800,8 +747,3 @@ mod randomness;
800747#[ cfg( test) ]
801748#[ cfg( feature = "elligator2" ) ]
802749mod subgroup;
803-
804- #[ cfg( test) ]
805- #[ cfg( feature = "elligator2" ) ]
806- #[ cfg( feature = "digest" ) ]
807- mod legacy;
0 commit comments