curveforge 0.3.0

Optimised, secure, and generalised algorithms for elliptic curve arithmetic
Documentation
use crate::models::montgomery::*;
use crate::prelude::*;

#[cfg(feature = "ed25519")]
use crate::curves::ed25519::Ed25519Point;
#[cfg(feature = "ed25519")]
use subtle::ConditionallySelectable;

elliptic_curve! {
    [attributes]
    name = Curve25519
    model = Montgomery

    field_size = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed
    group_size = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed

    generator = (0x09, 0x1)
    identity = (0x1, 0x0)

    [constants]
    a = 0x76d06
    b = 0x01

    [properties]
    serialized_bytes = 32
}

impl Curve25519Point {
    #[cfg(feature = "ed25519")]
    /// Convert this Montgomery point to an Edwards point.
    ///
    /// The `sign` parameter specifies the sign of the resulting Edwards x-coordinate, which is 0
    /// for positive and 1 for negative.
    pub fn to_edwards(&self, sign: subtle::Choice) -> Ed25519Point {
        // We pass through a serialization-deserialization roundtrip,
        // because the field types are different.
        let y_pos = Ed25519Point::deserialize(&MontgomeryModel::edwards_y(*self).serialize().into_array());
        let y_neg = y_pos.neg();

        Ed25519Point::conditional_select(&y_pos, &y_neg, sign)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn serialize_deserialize_generator() {
        let g = <Curve25519 as EllipticCurve>::Point::generator();

        let r1 = <<Curve25519 as EllipticCurve>::Point as DeserializePoint>::deserialize(g.serialize());

        assert!(g.equal(r1.clone()).unwrap_u8() != 0, "Expected \n{:#?}, but received \n{:#?}", g, r1)
    }

    #[test]
    fn serialize_deserialize() {
        let g = <Curve25519 as EllipticCurve>::Point::generator();
        let s = <Curve25519 as EllipticCurve>::Scalar::random(rand::rng());

        let r0 = g.scalar_mul(s);
        let r1 = <<Curve25519 as EllipticCurve>::Point as DeserializePoint>::deserialize(r0.serialize());

        assert!(r0.clone().equal(r1.clone()).unwrap_u8() != 0, "Expected \n{:#?}, but received \n{:#?}", r0, r1)
    }

    #[test]
    fn small_multiplication() {
        let g = <Curve25519 as EllipticCurve>::Point::generator();

        let mut p0 = g.clone();
        let mut s = DynamicElement::ONE;
        for i in 0usize..32usize {
            let p1 = g.clone().scalar_mul(s);

            let r0 = p0.clone().serialize();
            let r1 = p1.serialize();

            assert!(
                r0 == r1,
                "Small Randomized Multiplication s=2^{} error: expected {:?}, but received {:?}",
                i,
                &r0,
                &r1
            );

            s = s.dbl();
            p0 = p0.dbl();
        }
    }

    #[test]
    fn multiplication() {
        let g = <Curve25519 as EllipticCurve>::Point::generator();

        for i in 1..32 {
            let s0 = <Curve25519 as EllipticCurve>::Scalar::random(rand::rng());
            let s1 = <Curve25519 as EllipticCurve>::Scalar::random(rand::rng());
            let s2 = s0 * s1;

            let p0 = g.clone().scalar_mul(s0);
            let p1 = p0.clone().scalar_mul(s1);
            let p2 = g.clone().scalar_mul(s2);

            let r1 = p1.serialize();
            let r2 = p2.serialize();

            assert!(r1 == r2, "Randomized Multiplication {} error: expected {:#?}, but received {:#?}", i, &p1, &p2);
        }
    }

    #[test]
    fn test_negation() {
        let g = <Curve25519 as EllipticCurve>::Point::generator();

        for _ in 1..32 {
            let s = <Curve25519 as EllipticCurve>::Scalar::random(rand::rng());
            let p = g.clone().scalar_mul(s);

            assert!(bool::from(p.clone().neg().neg().equal(p)), "Could not double negate on {s:?}");
        }
    }

    #[test]
    fn test_one_minus_one() {
        let one: DynamicElement<<Curve25519 as EllipticCurve>::Scalar> = DynamicElement::ONE;
        let minus_one = -one;
        let zero = DynamicElement::ZERO;

        assert_eq!(zero, one + minus_one);
        assert_eq!(zero, one - one);
        assert_eq!(zero, one + -one);

        let one: DynamicElement<<Curve25519 as EllipticCurve>::Base> = DynamicElement::ONE;
        let minus_one = -one;
        let zero = DynamicElement::ZERO;

        assert_eq!(zero, one + minus_one);
        assert_eq!(zero, one - one);
        assert_eq!(zero, one + -one);
    }

    #[test]
    fn via_edwards() {
        let g = <Curve25519 as EllipticCurve>::Point::generator();
        let roundtrip = g.to_edwards(subtle::Choice::from(0u8)).to_montgomery();
        assert!(bool::from(g.equal(roundtrip)), "Roundtrip via Edwards failed");
    }

    #[test]
    fn basepoint_montgomery_to_edwards() {
        // Positive equivalency
        let ed = Curve25519Point::generator().to_edwards(0.into());
        assert!(
            bool::from(ed.equal(Ed25519Point::generator())),
            "Montgomery generator does not map to Edwards generator, got {:#?} instead",
            ed
        );
        // Negative equivalency
        let ed = Curve25519Point::generator().to_edwards(1.into());
        assert!(
            bool::from(ed.equal(Ed25519Point::generator().neg())),
            "Montgomery generator does not map to Edwards generator, got {ed:#?} instead",
        );
    }
}