Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 50f0dd79 authored by David Duarte's avatar David Duarte Committed by Gerrit Code Review
Browse files

Merge "rootcanal/ec: Use Jacobian Coordinates in Point implementation" into main

parents 2b019843 f209c86e
Loading
Loading
Loading
Loading
+98 −44
Original line number Diff line number Diff line
@@ -78,7 +78,7 @@ impl PublicKey {
    }

    fn to_point<Curve: EllipticCurve>(&self) -> Point<Curve> {
        Point::new(&self.get_x(), &self.get_y())
        Point::from_affine(self.get_x(), self.get_y())
    }
}

@@ -251,10 +251,11 @@ impl EllipticCurve for P256r1 {
    const PUBLIC_KEY_SIZE: usize = 64;
}

// https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates
#[derive(Debug, PartialEq)]
enum Point<Curve> {
    Infinite(PhantomData<Curve>),
    Finite { x: BigInt, y: BigInt, _curve: PhantomData<Curve> },
    Finite { x: BigInt, y: BigInt, z: BigInt, _curve: PhantomData<Curve> },
}

impl<Curve> Point<Curve>
@@ -269,35 +270,61 @@ where
        &Self::g() * private_key
    }

    fn new(x: &BigInt, y: &BigInt) -> Self {
        Point::Finite { x: x.clone(), y: y.clone(), _curve: PhantomData }
    fn new(x: BigInt, y: BigInt, z: BigInt) -> Self {
        Point::Finite { x, y, z, _curve: PhantomData }
    }

    fn from_affine(x: BigInt, y: BigInt) -> Self {
        Self::new(x, y, BigInt::from(1))
    }

    fn g() -> Self {
        Self::new(
            &BigInt::from_bytes_be(Sign::Plus, Curve::G_X.as_ref()),
            &BigInt::from_bytes_be(Sign::Plus, Curve::G_Y.as_ref()),
        Self::from_affine(
            BigInt::from_bytes_be(Sign::Plus, Curve::G_X.as_ref()),
            BigInt::from_bytes_be(Sign::Plus, Curve::G_Y.as_ref()),
        )
    }

    #[cfg(test)]
    fn get_x(&self) -> Option<BigInt> {
    fn to_affine(&self) -> Option<(BigInt, BigInt)> {
        match self {
            Point::Infinite(_) => None,
            Point::Finite { x, .. } => Some(x.clone()),
            Point::Finite { x, y, z, _curve } => {
                let p = &Curve::p();
                let inv_z = mod_inv(z, p).unwrap();
                let affine_x = (x * inv_z.pow(2)) % p;
                let affine_y = (y * inv_z.pow(3)) % p;
                Some((affine_x, affine_y))
            }
        }
    }

    fn to_bytes(&self) -> Option<Vec<u8>> {
        match self {
            Point::Infinite(_) => None,
            Point::Finite { x, y, _curve: _ } => {
        self.to_affine().map(|(x, y)| {
            let mut x = x.to_signed_bytes_le();
            x.resize(Curve::PRIVATE_KEY_SIZE, 0);
            let mut y = y.to_signed_bytes_le();
            y.resize(Curve::PRIVATE_KEY_SIZE, 0);
            x.append(&mut y);
                Some(x)
            x
        })
    }

    fn double(&self) -> Self {
        // https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates#Point_Doubling_(4M_+_6S_or_4M_+_4S)
        match self {
            Point::Infinite(_) => Point::o(),
            Point::Finite { y, .. } if y.is_zero() => Point::o(),
            Point::Finite { x, y, z, _curve } => {
                let s = 4 * x * y.pow(2);
                let m: BigInt = 3 * x.pow(2) + Curve::A * z.pow(4);

                let rx = m.pow(2) - 2 * &s;
                let ry = m * (s - &rx) - 8 * y.pow(4);
                let rz = 2 * y * z;

                let p = &Curve::p();

                Point::new(rx % p, ry % p, rz % p)
            }
        }
    }
@@ -310,7 +337,7 @@ where
    fn clone(&self) -> Self {
        match self {
            Point::Infinite(_) => Point::o(),
            Point::Finite { x, y, .. } => Point::new(x, y),
            Point::Finite { x, y, z, _curve } => Point::new(x.clone(), y.clone(), z.clone()),
        }
    }
}
@@ -326,30 +353,39 @@ where
    fn add(self, rhs: &Point<Curve>) -> Self::Output {
        // P + O = O + P = P
        match (self, rhs) {
            (Point::Infinite(_), Point::Infinite(_)) => Self::Output::o(),
            (Point::Infinite(_), Point::Infinite(_)) => Point::o(),
            (Point::Infinite(_), Point::Finite { .. }) => rhs.clone(),
            (Point::Finite { .. }, Point::Infinite(_)) => self.clone(),
            (
                Point::Finite { _curve: _, x: x1, y: y1 },
                Point::Finite { _curve: _, x: x2, y: y2 },
                Point::Finite { _curve: _, x: x1, y: y1, z: z1 },
                Point::Finite { _curve: _, x: x2, y: y2, z: z2 },
            ) => {
                // P + (-P) = O
                if x1 == x2 && y1 == &(-y2) {
                    return Self::Output::o();
                }
                // https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates#Point_Addition_(12M_+_4S)
                let p = &Curve::p();
                // d(x^3 + ax + b) / dx = (3x^2 + a) / 2y
                let slope = if x1 == x2 {
                    (&(3 * x1.pow(2) + Curve::A) * &mod_inv(&(2 * y1), p).unwrap()) % p
                let u1 = (x1 * z2.pow(2)) % p;
                let u2 = (x2 * z1.pow(2)) % p;
                let s1 = (y1 * z2.pow(3)) % p;
                let s2 = (y2 * z1.pow(3)) % p;

                if u1 == u2 {
                    if s1 != s2 {
                        Point::o()
                    } else {
                    // dy/dx = (y2 - y1) / (x2 - x1)
                    (&(y2 - y1) * &mod_inv(&(x2 - x1), p).unwrap()) % p
                };
                // Solving (x-p)(x-q)(x-r) = x^3 + ax + b
                // => x = d^2 - x1 - x2
                let x = (slope.pow(2) - x1 - x2) % p;
                let y = (slope * (x1 - &x) - y1) % p;
                Point::new(&x, &y)
                        self.double()
                    }
                } else {
                    let h = &u2 - &u1;
                    let r = &s2 - &s1;

                    let h2 = (&h * &h) % p;
                    let h3 = (&h * &h2) % p;
                    let u1h2 = (&u1 * &h2) % p;
                    let x3 = r.pow(2) - &h3 - 2 * &u1h2;
                    let y3 = r * (&u1h2 - &x3) - s1 * &h3;
                    let z3 = h * z1 * z2;

                    Point::new(x3 % p, y3 % p, z3 % p)
                }
            }
        }
    }
@@ -371,7 +407,7 @@ where
            if i.is_odd() {
                result = &result + &addend;
            }
            addend = &addend + &addend;
            addend = addend.double();
            i /= 2;
        }
        result
@@ -441,10 +477,19 @@ mod tests {
            let priv_b = BigInt::parse_bytes(&test_case.priv_b, 16).unwrap();
            let pub_a = Point::<P192r1>::generate_public_key(&priv_a);
            let pub_b = Point::<P192r1>::generate_public_key(&priv_b);
            assert_eq!(pub_a.get_x().unwrap(), BigInt::parse_bytes(&test_case.pub_a, 16).unwrap());
            assert_eq!(
                pub_a.to_affine().unwrap().0,
                BigInt::parse_bytes(&test_case.pub_a, 16).unwrap()
            );
            let shared = &pub_a * &priv_b;
            assert_eq!(shared.get_x().unwrap(), BigInt::parse_bytes(&test_case.dh_x, 16).unwrap());
            assert_eq!((&pub_a * &priv_b).get_x().unwrap(), (&pub_b * &priv_a).get_x().unwrap());
            assert_eq!(
                shared.to_affine().unwrap().0,
                BigInt::parse_bytes(&test_case.dh_x, 16).unwrap()
            );
            assert_eq!(
                (&pub_a * &priv_b).to_affine().unwrap().0,
                (&pub_b * &priv_a).to_affine().unwrap().0
            );
        }
    }

@@ -455,10 +500,19 @@ mod tests {
            let priv_b = BigInt::parse_bytes(&test_case.priv_b, 16).unwrap();
            let pub_a = Point::<P256r1>::generate_public_key(&priv_a);
            let pub_b = Point::<P256r1>::generate_public_key(&priv_b);
            assert_eq!(pub_a.get_x().unwrap(), BigInt::parse_bytes(&test_case.pub_a, 16).unwrap());
            assert_eq!(
                pub_a.to_affine().unwrap().0,
                BigInt::parse_bytes(&test_case.pub_a, 16).unwrap()
            );
            let shared = &pub_a * &priv_b;
            assert_eq!(shared.get_x().unwrap(), BigInt::parse_bytes(&test_case.dh_x, 16).unwrap());
            assert_eq!((&pub_a * &priv_b).get_x().unwrap(), (&pub_b * &priv_a).get_x().unwrap());
            assert_eq!(
                shared.to_affine().unwrap().0,
                BigInt::parse_bytes(&test_case.dh_x, 16).unwrap()
            );
            assert_eq!(
                (&pub_a * &priv_b).to_affine().unwrap().0,
                (&pub_b * &priv_a).to_affine().unwrap().0
            );
        }
    }
}