curveforge 0.3.0

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

#[cfg(feature = "curve25519")]
use crate::curves::curve25519::Curve25519Point;

elliptic_curve! {
    [attributes]
    name = Ed25519
    model = TwistedEdwards

    field_size = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed
    group_size = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed

    generator = (0x216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A,
                 0x6666666666666666666666666666666666666666666666666666666666666658,
                 0x1,
                 0x67875f0fd78b766566ea4e8e64abe37d20f09f80775152f56dde8ab3a5b7dda3)
    identity = (0x0, 0x1, 0x1, 0x0)

    [constants]
    a = -1
    d = 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3

    [properties]
    serialized_bytes = 33
}

impl Ed25519Point {
    pub fn serialize(self) -> [u8; 32] {
        let mut result = [0u8; 32];
        let long = <Self as SerializePoint>::serialize(self);

        result.copy_from_slice(&long[0..32]);
        result[31] |= (long[32] & 0x01) << 7;

        result
    }

    pub fn deserialize(bytes: &[u8; 32]) -> Self {
        let mut long = [0u8; 33];
        long[0..32].copy_from_slice(&bytes[0..32]);
        long[32] = (bytes[31] >> 7) & 0x01;
        long[31] &= 0x7F;

        <Self as DeserializePoint>::deserialize(*GenericArray::from_slice(&long))
    }

    #[cfg(feature = "curve25519")]
    /// Convert this Edwards point to a Montgomery point.
    ///
    /// Note that this conversion is not bijective, as it loses the sign of the Edwards
    /// x-coordinate.
    pub fn to_montgomery(&self) -> Curve25519Point {
        // We pass through a serialization-deserialization roundtrip,
        // because the field types are different.
        let u = TwistedEdwardsModel::montgomery_u(*self);
        <Curve25519Point as DeserializePoint>::deserialize(u.serialize())
    }
}

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

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

        let res = g.clone().add(<Ed25519 as EllipticCurve>::Point::identity());
        let a = res.clone().serialize();
        let b = <Ed25519 as EllipticCurve>::Point::generator().serialize();

        assert!(bool::from(g.equal(res)), "Identity + generator error: expected {:?}, but received {:?}", a, b);
    }

    #[test]
    fn decompress_basepoint() {
        let bytes = [
            0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
            0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
        ];

        let p = Ed25519Point::deserialize(&bytes);
        let g = <Ed25519 as EllipticCurve>::Point::generator();
        assert!(
            bool::from(p.clone().equal(g.clone())),
            "Decompress basepoint error: expected \n{:#?}, but received \n{:#?}",
            g,
            p
        );

        let serialized = g.serialize();

        assert!(
            bytes == serialized,
            "Compress basepoint error: expected {:?}, but received {:?}",
            bytes,
            serialized
        );
    }

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

        for _ in 0..32 {
            let s = <Ed25519 as EllipticCurve>::Scalar::random(rand::rng());

            let r0 = g.clone().scalar_mul(s);
            let r1 = Ed25519Point::deserialize(&r0.clone().serialize());

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

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

        let mut p0 = g.clone();
        for i in 1usize..32usize {
            let s = <Ed25519 as EllipticCurve>::Scalar::from_usize(i);

            let p1 = g.clone().scalar_mul(s);

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

            assert!(
                bool::from(p0.clone().equal(p1)),
                "Small Randomized Multiplication {} error: expected {:?}, but received {:?}",
                i,
                &r0,
                &r1
            );
            p0 = p0.clone().add(g.clone());
        }
    }

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

        for i in 1..32 {
            let s0 = <Ed25519 as EllipticCurve>::Scalar::random(rand::rng());
            let s1 = <Ed25519 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, &r1, &r2);
        }
    }

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

        for _ in 1..32 {
            let s = <Ed25519 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<<Ed25519 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<<Ed25519 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 basepoint_edwards_to_montgomery() {
        let mont = Ed25519Point::generator().to_montgomery();
        assert!(
            bool::from(mont.equal(Curve25519Point::generator())),
            "Edwards generator does not map to Montgomery generator, got {mont:#?} instead",
        );
    }
}