1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! Multiscalar multiplication
//!
//! Let $s_{1, \dots, n}$ and $P_{1, \dots, n}$ be lists of scalars and points
//! respectively. Multiscalar multiplication is computing point $Q$ such that:
//!
//! $$Q = s_1 P_1 + \dots + s_n P_n$$
//!
//! This module provides various algorithms for computing multiscalar multiplication
//! efficiently.
//!
//! ## Performance
//! Computing the sum naively, i.e. calculating each $s_i P_i$ separately and
//! $\sum$-ing them, is inefficient even for small $n$. You can see that in the
//! comparison below.
//!
#![doc = include_str!("../perf/multiscalar/secp256k1.svg")]
//!
//! ## How to use it
//! In most cases, all you need is [`Scalar::multiscalar_mul`] which defaults
//! to the most efficient available algorithm, similarly to [`struct@Default`].
//!
//! Alternatively, if you need to use a specific algorithm, this module provides
//! [`Straus`] and [`Dalek`].
//!
//! On [`Ed25519`](crate::curves::Ed25519) curve, consider using [`Dalek`] multiscalar
//! implementation.

use crate::{Curve, Point, Scalar};

#[cfg(feature = "alloc")]
mod straus;

#[cfg(feature = "alloc")]
pub use self::straus::Straus;

/// Multiscalar multiplication algorithm
///
/// See [module-level docs](self) for motivation and list of provided algorithms.
pub trait MultiscalarMul<E: Curve> {
    /// Performs multiscalar multiplication
    ///
    /// Takes iterator of pairs `(scalar, point)`. Returns sum of `scalar * point`. Iterator must have
    /// exact size (i.e. it's [`ExactSizeIterator`]). Iterator size is used to determine the best
    /// algorithm for multiscalar multiplication, preallocate memory, etc. If iterator size is not
    /// correct, it may worsen performance or lead to runtime panic.
    ///
    /// Note that the multiscalar algorithm is not necessarily constant-time, thus is should not be
    /// used with [`SecretScalar<E>`](crate::SecretScalar).
    fn multiscalar_mul<S, P>(scalar_points: impl ExactSizeIterator<Item = (S, P)>) -> Point<E>
    where
        S: AsRef<Scalar<E>>,
        P: AsRef<Point<E>>;
}

/// Defaults to the most efficient multiscalar multiplication algorithm
///
/// When `alloc` feature is off, it always falls back to [`Naive`] implementation.
///
/// When `alloc` feature is on, it uses [`Straus`] algorithm.
///
/// It may be more convenient to use [`Scalar::multiscalar_mul`] which is an alias
/// to `Default`.
pub struct Default;

#[cfg(not(feature = "alloc"))]
impl<E: Curve> MultiscalarMul<E> for Default {
    fn multiscalar_mul<S, P>(scalar_points: impl ExactSizeIterator<Item = (S, P)>) -> Point<E>
    where
        S: AsRef<Scalar<E>>,
        P: AsRef<Point<E>>,
    {
        Naive::multiscalar_mul(scalar_points)
    }
}

#[cfg(feature = "alloc")]
impl<E: Curve> MultiscalarMul<E> for Default {
    fn multiscalar_mul<S, P>(scalar_points: impl ExactSizeIterator<Item = (S, P)>) -> Point<E>
    where
        S: AsRef<Scalar<E>>,
        P: AsRef<Point<E>>,
    {
        Straus::multiscalar_mul(scalar_points)
    }
}

/// Naive algorithm
///
/// Computes multiscalar multiplication naively, by calculating each $s_i P_i$ separately,
/// and $\sum$-ing them.
///
/// Complexity:
///
/// $$\text{cost} = \log_2 s \cdot D + \frac{1}{2} \log_2 s \cdot A$$
pub struct Naive;

impl<E: Curve> MultiscalarMul<E> for Naive {
    fn multiscalar_mul<S, P>(scalar_points: impl IntoIterator<Item = (S, P)>) -> Point<E>
    where
        S: AsRef<Scalar<E>>,
        P: AsRef<Point<E>>,
    {
        scalar_points
            .into_iter()
            .map(|(scalar, point)| scalar.as_ref() * point.as_ref())
            .sum()
    }
}

/// Multiscalar implementation for [`Ed25519`] curve
///
/// [`curve25519_dalek`] library provides multiscalar multiplication algorithm which only
/// works with [`Ed25519`] curve. Due to the fact that it's specifically instantiated for
/// the only one curve, this implementation is more efficient than generic [`struct@Default`]
/// or [`Straus`].
///
#[doc = include_str!("../perf/multiscalar/ed25519.svg")]
///
/// [`Ed25519`]: crate::curves::Ed25519
#[cfg(all(feature = "curve-ed25519", feature = "alloc"))]
pub struct Dalek;

#[cfg(all(feature = "curve-ed25519", feature = "alloc"))]
impl MultiscalarMul<crate::curves::Ed25519> for Dalek {
    fn multiscalar_mul<S, P>(
        scalar_points: impl IntoIterator<Item = (S, P)>,
    ) -> Point<crate::curves::Ed25519>
    where
        S: AsRef<Scalar<crate::curves::Ed25519>>,
        P: AsRef<Point<crate::curves::Ed25519>>,
    {
        use alloc::vec::Vec;

        use curve25519_dalek::traits::VartimeMultiscalarMul;
        use generic_ec_core::{OnCurve, SmallFactor};

        use crate::as_raw::AsRaw;

        let scalar_points = scalar_points.into_iter().collect::<Vec<_>>();
        let scalars = scalar_points.iter().map(|(s, _)| &s.as_ref().as_raw().0);
        let points = scalar_points.iter().map(|(_, p)| &p.as_ref().as_raw().0);

        let result = curve25519_dalek::EdwardsPoint::vartime_multiscalar_mul(scalars, points);
        let result = generic_ec_curves::ed25519::Point(result);

        // Resulting point must be valid
        debug_assert!(result.is_on_curve().into() && result.is_torsion_free().into());
        Point::from_raw_unchecked(result)
    }
}