secp256kfun 0.12.1

A mid-level secp256k1 library optimized for fun!
Documentation
pub use crate::vendor::k256::Scalar;
use crate::{
    backend::{BackendPoint, BackendScalar, TimeSensitive},
    vendor::k256::{AffinePoint, FieldBytes, FieldElement, ProjectivePoint, mul},
};
use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq};

pub static G_POINT: ProjectivePoint = ProjectivePoint::GENERATOR;
pub type Point = ProjectivePoint;
// We don't implement multiplication tables yet
pub type BasePoint = ProjectivePoint;

impl BackendScalar for Scalar {
    fn minus_one() -> Self {
        -Scalar::ONE
    }

    fn zero() -> Self {
        Scalar::ZERO
    }

    fn from_bytes_mod_order(bytes: [u8; 32]) -> Self {
        Scalar::from_bytes_reduced(&FieldBytes::from(bytes))
    }

    fn from_bytes(bytes: [u8; 32]) -> Option<Self> {
        Scalar::from_repr(FieldBytes::from(bytes))
    }

    fn to_bytes(&self) -> [u8; 32] {
        Scalar::to_bytes(self).into()
    }
}

impl BackendPoint for Point {
    fn zero() -> Point {
        ProjectivePoint::IDENTITY
    }

    fn is_zero(&self) -> bool {
        self.is_identity().into()
    }

    fn norm_to_coordinates(&self) -> ([u8; 32], [u8; 32]) {
        (self.x.to_bytes().into(), self.y.to_bytes().into())
    }

    fn norm_from_bytes_y_oddness(x_bytes: [u8; 32], y_odd: bool) -> Option<Point> {
        let x_bytes = FieldBytes::from(x_bytes);
        let option: Option<AffinePoint> =
            AffinePoint::decompress(&x_bytes, (y_odd as u8).into()).into();
        option.map(|affine| affine.into())
    }

    fn norm_from_coordinates(x: [u8; 32], y: [u8; 32]) -> Option<Point> {
        let x_bytes = FieldBytes::from(x);
        let y_field = Option::from(FieldElement::from_bytes(&FieldBytes::from(y)))?;

        // Decompress to get the valid y coordinate for this x
        let option: Option<AffinePoint> = AffinePoint::decompress(&x_bytes, 0.into()).into();
        option.and_then(|point| {
            // Check if the provided y matches the computed y or its negation
            if point.y == y_field || point.y.negate(1).normalize() == y_field {
                Some(AffinePoint::new(point.x, y_field).into())
            } else {
                None
            }
        })
    }

    fn hash_to_curve<
        H: crate::hash::Hash32 + crate::digest::Update + crate::digest::crypto_common::BlockSizeUser,
    >(
        msg: &[u8],
        dst: &[u8],
    ) -> Point {
        crate::vendor::hash_to_curve::hash_to_curve::<H>(msg, dst)
    }
}

pub struct ConstantTime;

impl TimeSensitive for ConstantTime {
    fn scalar_mul_norm_point(lhs: &Scalar, rhs: &Point) -> Point {
        rhs * lhs
    }

    fn scalar_mul_point(lhs: &Scalar, rhs: &Point) -> Point {
        rhs * lhs
    }

    fn scalar_eq(lhs: &Scalar, rhs: &Scalar) -> bool {
        lhs.ct_eq(rhs).into()
    }

    fn point_normalize(point: &mut Point) {
        let zinv_opt = point.z.invert();
        let was_zero = zinv_opt.is_none();
        let zinv = zinv_opt.unwrap_or(FieldElement::ONE);
        point.x *= zinv;
        point.y *= zinv;
        point.x = point.x.normalize();
        point.y = point.y.normalize();
        point.z.conditional_assign(&FieldElement::ONE, !was_zero);
    }

    fn point_eq_point(lhs: &Point, rhs: &Point) -> bool {
        lhs.ct_eq(rhs).into()
    }

    fn point_eq_norm_point(lhs: &Point, rhs: &Point) -> bool {
        let rhs = norm_point_to_affine(rhs);
        lhs.eq_affine(&rhs).into()
    }

    fn point_add_point(lhs: &Point, rhs: &Point) -> Point {
        lhs + rhs
    }

    fn point_add_norm_point(lhs: &Point, rhs: &Point) -> Point {
        // use more efficient version for affine
        let rhs = norm_point_to_affine(rhs);
        lhs + &rhs
    }

    fn point_neg(point: &mut Point) {
        point.y = point.y.negate(1).normalize_weak()
    }

    fn point_conditional_negate(point: &mut Point, cond: bool) {
        point.conditional_negate(Choice::from(cond as u8));
        point.y = point.y.normalize()
    }

    fn norm_point_neg(point: &mut Point) {
        point.y = point.y.negate(1).normalize();
    }

    fn norm_point_eq_norm_point(lhs: &Point, rhs: &Point) -> bool {
        let both_infinity = Choice::from((lhs.is_zero() && rhs.is_zero()) as u8);
        (both_infinity | lhs.x.ct_eq(&rhs.x) & lhs.y.ct_eq(&rhs.y)).into()
    }

    fn norm_point_is_y_even(point: &Point) -> bool {
        (!point.y.is_odd()).into()
    }

    fn norm_point_conditional_negate(point: &mut Point, cond: bool) {
        point.conditional_negate(Choice::from(cond as u8));
        point.y = point.y.normalize()
    }

    fn basepoint_double_mul(x: &Scalar, A: &BasePoint, y: &Scalar, B: &Point) -> Point {
        Self::point_double_mul(x, A, y, B)
    }

    // Only use the "lincomb" method if we don't have alloc. If we do we might as well use the
    // allocating verison to avoid compiling two methods that do the same thing.
    #[cfg(not(feature = "alloc"))]
    fn point_double_mul(x: &Scalar, A: &Point, y: &Scalar, B: &Point) -> Point {
        mul::lincomb_generic(&[A, B], &[x, y])
    }

    #[cfg(feature = "alloc")]
    fn point_double_mul(x: &Scalar, A: &Point, y: &Scalar, B: &Point) -> Point {
        mul::lincomb_iter([A, B].into_iter(), [x, y].into_iter())
    }

    fn scalar_add(lhs: &Scalar, rhs: &Scalar) -> Scalar {
        lhs + rhs
    }

    fn scalar_sub(lhs: &Scalar, rhs: &Scalar) -> Scalar {
        lhs - rhs
    }

    fn scalar_cond_negate(scalar: &mut Scalar, neg: bool) {
        scalar.conditional_negate(Choice::from(neg as u8))
    }

    fn scalar_is_high(scalar: &Scalar) -> bool {
        scalar.is_high().into()
    }

    fn scalar_is_zero(scalar: &Scalar) -> bool {
        scalar.is_zero().into()
    }

    fn scalar_mul(lhs: &Scalar, rhs: &Scalar) -> Scalar {
        lhs * rhs
    }

    fn scalar_invert(scalar: &Scalar) -> Scalar {
        scalar.invert().unwrap()
    }

    fn scalar_mul_basepoint(scalar: &Scalar, base: &BasePoint) -> Point {
        base * scalar
    }

    #[cfg(feature = "alloc")]
    #[inline(always)]
    fn lincomb_iter<
        A: Iterator<Item = AT>,
        B: Iterator<Item = BT>,
        AT: AsRef<Point>,
        BT: AsRef<Scalar>,
    >(
        points: A,
        scalars: B,
    ) -> Point {
        mul::lincomb_iter(points.map(|p| *p.as_ref()), scalars)
    }
}

pub struct VariableTime;

// delegate everything to constant time for now
impl TimeSensitive for VariableTime {
    fn scalar_mul_norm_point(lhs: &Scalar, rhs: &Point) -> Point {
        ConstantTime::scalar_mul_norm_point(lhs, rhs)
    }

    fn scalar_mul_point(lhs: &Scalar, rhs: &Point) -> Point {
        ConstantTime::scalar_mul_point(lhs, rhs)
    }

    fn scalar_eq(lhs: &Scalar, rhs: &Scalar) -> bool {
        ConstantTime::scalar_eq(lhs, rhs)
    }

    fn point_eq_point(lhs: &Point, rhs: &Point) -> bool {
        ConstantTime::point_eq_point(lhs, rhs)
    }

    fn point_normalize(point: &mut Point) {
        ConstantTime::point_normalize(point)
    }

    fn point_eq_norm_point(lhs: &Point, rhs: &Point) -> bool {
        ConstantTime::point_eq_norm_point(lhs, rhs)
    }

    fn point_add_point(lhs: &Point, rhs: &Point) -> Point {
        ConstantTime::point_add_point(lhs, rhs)
    }

    fn point_add_norm_point(lhs: &Point, rhs: &Point) -> Point {
        ConstantTime::point_add_norm_point(lhs, rhs)
    }

    fn point_neg(point: &mut Point) {
        ConstantTime::point_neg(point)
    }

    fn point_conditional_negate(point: &mut Point, cond: bool) {
        ConstantTime::point_conditional_negate(point, cond)
    }

    fn norm_point_neg(point: &mut Point) {
        ConstantTime::norm_point_neg(point)
    }

    fn norm_point_eq_norm_point(lhs: &Point, rhs: &Point) -> bool {
        ConstantTime::norm_point_eq_norm_point(lhs, rhs)
    }

    fn norm_point_is_y_even(point: &Point) -> bool {
        ConstantTime::norm_point_is_y_even(point)
    }

    fn norm_point_conditional_negate(point: &mut Point, cond: bool) {
        ConstantTime::norm_point_conditional_negate(point, cond)
    }

    fn basepoint_double_mul(x: &Scalar, A: &BasePoint, y: &Scalar, B: &Point) -> Point {
        Self::point_double_mul(x, A, y, B)
    }

    fn scalar_add(lhs: &Scalar, rhs: &Scalar) -> Scalar {
        ConstantTime::scalar_add(lhs, rhs)
    }

    fn scalar_sub(lhs: &Scalar, rhs: &Scalar) -> Scalar {
        ConstantTime::scalar_sub(lhs, rhs)
    }

    fn scalar_cond_negate(scalar: &mut Scalar, neg: bool) {
        ConstantTime::scalar_cond_negate(scalar, neg)
    }

    fn scalar_is_high(scalar: &Scalar) -> bool {
        ConstantTime::scalar_is_high(scalar)
    }

    fn scalar_is_zero(scalar: &Scalar) -> bool {
        ConstantTime::scalar_is_zero(scalar)
    }

    fn scalar_mul(lhs: &Scalar, rhs: &Scalar) -> Scalar {
        ConstantTime::scalar_mul(lhs, rhs)
    }

    fn scalar_invert(scalar: &Scalar) -> Scalar {
        ConstantTime::scalar_invert(scalar)
    }

    fn scalar_mul_basepoint(scalar: &Scalar, base: &BasePoint) -> Point {
        ConstantTime::scalar_mul_basepoint(scalar, base)
    }

    fn point_double_mul(x: &Scalar, A: &Point, y: &Scalar, B: &Point) -> Point {
        ConstantTime::point_double_mul(x, A, y, B)
    }

    #[cfg(feature = "alloc")]
    fn lincomb_iter<
        A: Iterator<Item = AT>,
        B: Iterator<Item = BT>,
        AT: AsRef<Point>,
        BT: AsRef<Scalar>,
    >(
        points: A,
        scalars: B,
    ) -> Point {
        ConstantTime::lincomb_iter(points, scalars)
    }
}

impl VariableTime {
    pub fn point_x_eq_scalar(point: &Point, scalar: &Scalar) -> bool {
        if point.is_identity().into() {
            return false;
        }
        let mut point = *point;
        Self::point_normalize(&mut point);
        Scalar::from_bytes_reduced(&point.x.to_bytes()).eq(scalar)
    }
}

fn norm_point_to_affine(proj_point: &Point) -> AffinePoint {
    debug_assert!(
        proj_point.is_identity().into() && proj_point.z.normalizes_to_zero().into()
            || proj_point.z == FieldElement::ONE
    );
    AffinePoint::conditional_select(
        &AffinePoint::new(proj_point.x, proj_point.y),
        &AffinePoint::IDENTITY,
        proj_point.is_identity(),
    )
}