curve25519_dalek/backend/
mod.rs

1// -*- mode: rust; -*-
2//
3// This file is part of curve25519-dalek.
4// Copyright (c) 2016-2021 isis lovecruft
5// Copyright (c) 2016-2019 Henry de Valence
6// See LICENSE for licensing information.
7//
8// Authors:
9// - isis agora lovecruft <isis@patternsinthevoid.net>
10// - Henry de Valence <hdevalence@hdevalence.ca>
11
12//! **INTERNALS:** Pluggable implementations for different architectures.
13//!
14//! The backend code is split into two parts: a serial backend,
15//! and a vector backend.
16//!
17//! The [`serial`] backend contains 32- and 64-bit implementations of
18//! field arithmetic and scalar arithmetic, as well as implementations
19//! of point operations using the mixed-model strategy (passing
20//! between different curve models depending on the operation).
21//!
22//! The [`vector`] backend contains implementations of vectorized
23//! field arithmetic, used to implement point operations using a novel
24//! implementation strategy derived from parallel formulas of Hisil,
25//! Wong, Carter, and Dawson.
26//!
27//! Because the two strategies give rise to different curve models,
28//! it's not possible to reuse exactly the same scalar multiplication
29//! code (or to write it generically), so both serial and vector
30//! backends contain matching implementations of scalar multiplication
31//! algorithms.  These are intended to be selected by a `#[cfg]`-based
32//! type alias.
33//!
34//! The [`vector`] backend is selected by the `simd_backend` cargo
35//! feature; it uses the [`serial`] backend for non-vectorized operations.
36
37use crate::EdwardsPoint;
38use crate::Scalar;
39
40pub mod serial;
41
42#[cfg(curve25519_dalek_backend = "simd")]
43pub mod vector;
44
45#[derive(Copy, Clone)]
46enum BackendKind {
47    #[cfg(curve25519_dalek_backend = "simd")]
48    Avx2,
49    #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
50    Avx512,
51    Serial,
52}
53
54#[inline]
55fn get_selected_backend() -> BackendKind {
56    #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
57    {
58        cpufeatures::new!(cpuid_avx512, "avx512ifma", "avx512vl");
59        let token_avx512: cpuid_avx512::InitToken = cpuid_avx512::init();
60        if token_avx512.get() {
61            return BackendKind::Avx512;
62        }
63    }
64
65    #[cfg(curve25519_dalek_backend = "simd")]
66    {
67        cpufeatures::new!(cpuid_avx2, "avx2");
68        let token_avx2: cpuid_avx2::InitToken = cpuid_avx2::init();
69        if token_avx2.get() {
70            return BackendKind::Avx2;
71        }
72    }
73
74    BackendKind::Serial
75}
76
77#[allow(missing_docs)]
78#[cfg(feature = "alloc")]
79pub fn pippenger_optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
80where
81    I: IntoIterator,
82    I::Item: core::borrow::Borrow<Scalar>,
83    J: IntoIterator<Item = Option<EdwardsPoint>>,
84{
85    use crate::traits::VartimeMultiscalarMul;
86
87    match get_selected_backend() {
88        #[cfg(curve25519_dalek_backend = "simd")]
89        BackendKind::Avx2 =>
90            vector::scalar_mul::pippenger::spec_avx2::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
91        #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
92        BackendKind::Avx512 =>
93            vector::scalar_mul::pippenger::spec_avx512ifma_avx512vl::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
94        BackendKind::Serial =>
95            serial::scalar_mul::pippenger::Pippenger::optional_multiscalar_mul::<I, J>(scalars, points),
96    }
97}
98
99#[cfg(feature = "alloc")]
100pub(crate) enum VartimePrecomputedStraus {
101    #[cfg(curve25519_dalek_backend = "simd")]
102    Avx2(vector::scalar_mul::precomputed_straus::spec_avx2::VartimePrecomputedStraus),
103    #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
104    Avx512ifma(
105        vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus,
106    ),
107    Scalar(serial::scalar_mul::precomputed_straus::VartimePrecomputedStraus),
108}
109
110#[cfg(feature = "alloc")]
111impl VartimePrecomputedStraus {
112    pub fn new<I>(static_points: I) -> Self
113    where
114        I: IntoIterator,
115        I::Item: core::borrow::Borrow<EdwardsPoint>,
116    {
117        use crate::traits::VartimePrecomputedMultiscalarMul;
118
119        match get_selected_backend() {
120            #[cfg(curve25519_dalek_backend = "simd")]
121            BackendKind::Avx2 =>
122                VartimePrecomputedStraus::Avx2(vector::scalar_mul::precomputed_straus::spec_avx2::VartimePrecomputedStraus::new(static_points)),
123            #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
124            BackendKind::Avx512 =>
125                VartimePrecomputedStraus::Avx512ifma(vector::scalar_mul::precomputed_straus::spec_avx512ifma_avx512vl::VartimePrecomputedStraus::new(static_points)),
126            BackendKind::Serial =>
127                VartimePrecomputedStraus::Scalar(serial::scalar_mul::precomputed_straus::VartimePrecomputedStraus::new(static_points))
128        }
129    }
130
131    /// Return the number of static points in the precomputation.
132    pub fn len(&self) -> usize {
133        use crate::traits::VartimePrecomputedMultiscalarMul;
134
135        match self {
136            #[cfg(curve25519_dalek_backend = "simd")]
137            VartimePrecomputedStraus::Avx2(inner) => inner.len(),
138            #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
139            VartimePrecomputedStraus::Avx512ifma(inner) => inner.len(),
140            VartimePrecomputedStraus::Scalar(inner) => inner.len(),
141        }
142    }
143
144    /// Determine if the precomputation is empty.
145    pub fn is_empty(&self) -> bool {
146        use crate::traits::VartimePrecomputedMultiscalarMul;
147
148        match self {
149            #[cfg(curve25519_dalek_backend = "simd")]
150            VartimePrecomputedStraus::Avx2(inner) => inner.is_empty(),
151            #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
152            VartimePrecomputedStraus::Avx512ifma(inner) => inner.is_empty(),
153            VartimePrecomputedStraus::Scalar(inner) => inner.is_empty(),
154        }
155    }
156
157    pub fn optional_mixed_multiscalar_mul<I, J, K>(
158        &self,
159        static_scalars: I,
160        dynamic_scalars: J,
161        dynamic_points: K,
162    ) -> Option<EdwardsPoint>
163    where
164        I: IntoIterator,
165        I::Item: core::borrow::Borrow<Scalar>,
166        J: IntoIterator,
167        J::Item: core::borrow::Borrow<Scalar>,
168        K: IntoIterator<Item = Option<EdwardsPoint>>,
169    {
170        use crate::traits::VartimePrecomputedMultiscalarMul;
171
172        match self {
173            #[cfg(curve25519_dalek_backend = "simd")]
174            VartimePrecomputedStraus::Avx2(inner) => inner.optional_mixed_multiscalar_mul(
175                static_scalars,
176                dynamic_scalars,
177                dynamic_points,
178            ),
179            #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
180            VartimePrecomputedStraus::Avx512ifma(inner) => inner.optional_mixed_multiscalar_mul(
181                static_scalars,
182                dynamic_scalars,
183                dynamic_points,
184            ),
185            VartimePrecomputedStraus::Scalar(inner) => inner.optional_mixed_multiscalar_mul(
186                static_scalars,
187                dynamic_scalars,
188                dynamic_points,
189            ),
190        }
191    }
192}
193
194#[allow(missing_docs)]
195#[cfg(feature = "alloc")]
196pub fn straus_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
197where
198    I: IntoIterator,
199    I::Item: core::borrow::Borrow<Scalar>,
200    J: IntoIterator,
201    J::Item: core::borrow::Borrow<EdwardsPoint>,
202{
203    use crate::traits::MultiscalarMul;
204
205    match get_selected_backend() {
206        #[cfg(curve25519_dalek_backend = "simd")]
207        BackendKind::Avx2 => {
208            vector::scalar_mul::straus::spec_avx2::Straus::multiscalar_mul::<I, J>(scalars, points)
209        }
210        #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
211        BackendKind::Avx512 => {
212            vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::multiscalar_mul::<I, J>(
213                scalars, points,
214            )
215        }
216        BackendKind::Serial => {
217            serial::scalar_mul::straus::Straus::multiscalar_mul::<I, J>(scalars, points)
218        }
219    }
220}
221
222#[allow(missing_docs)]
223#[cfg(feature = "alloc")]
224pub fn straus_optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
225where
226    I: IntoIterator,
227    I::Item: core::borrow::Borrow<Scalar>,
228    J: IntoIterator<Item = Option<EdwardsPoint>>,
229{
230    use crate::traits::VartimeMultiscalarMul;
231
232    match get_selected_backend() {
233        #[cfg(curve25519_dalek_backend = "simd")]
234        BackendKind::Avx2 => {
235            vector::scalar_mul::straus::spec_avx2::Straus::optional_multiscalar_mul::<I, J>(
236                scalars, points,
237            )
238        }
239        #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
240        BackendKind::Avx512 => {
241            vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::optional_multiscalar_mul::<
242                I,
243                J,
244            >(scalars, points)
245        }
246        BackendKind::Serial => {
247            serial::scalar_mul::straus::Straus::optional_multiscalar_mul::<I, J>(scalars, points)
248        }
249    }
250}
251
252/// Perform constant-time, variable-base scalar multiplication.
253pub fn variable_base_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
254    match get_selected_backend() {
255        #[cfg(curve25519_dalek_backend = "simd")]
256        BackendKind::Avx2 => vector::scalar_mul::variable_base::spec_avx2::mul(point, scalar),
257        #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
258        BackendKind::Avx512 => {
259            vector::scalar_mul::variable_base::spec_avx512ifma_avx512vl::mul(point, scalar)
260        }
261        BackendKind::Serial => serial::scalar_mul::variable_base::mul(point, scalar),
262    }
263}
264
265/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
266#[allow(non_snake_case)]
267pub fn vartime_double_base_mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
268    match get_selected_backend() {
269        #[cfg(curve25519_dalek_backend = "simd")]
270        BackendKind::Avx2 => vector::scalar_mul::vartime_double_base::spec_avx2::mul(a, A, b),
271        #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))]
272        BackendKind::Avx512 => {
273            vector::scalar_mul::vartime_double_base::spec_avx512ifma_avx512vl::mul(a, A, b)
274        }
275        BackendKind::Serial => serial::scalar_mul::vartime_double_base::mul(a, A, b),
276    }
277}