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 = "simd", nightly))]
50    Avx512,
51    Serial,
52}
53
54#[inline]
55fn get_selected_backend() -> BackendKind {
56    #[cfg(all(curve25519_dalek_backend = "simd", 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 = "simd", 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 = "simd", 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 = "simd", 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    pub fn optional_mixed_multiscalar_mul<I, J, K>(
132        &self,
133        static_scalars: I,
134        dynamic_scalars: J,
135        dynamic_points: K,
136    ) -> Option<EdwardsPoint>
137    where
138        I: IntoIterator,
139        I::Item: core::borrow::Borrow<Scalar>,
140        J: IntoIterator,
141        J::Item: core::borrow::Borrow<Scalar>,
142        K: IntoIterator<Item = Option<EdwardsPoint>>,
143    {
144        use crate::traits::VartimePrecomputedMultiscalarMul;
145
146        match self {
147            #[cfg(curve25519_dalek_backend = "simd")]
148            VartimePrecomputedStraus::Avx2(inner) => inner.optional_mixed_multiscalar_mul(
149                static_scalars,
150                dynamic_scalars,
151                dynamic_points,
152            ),
153            #[cfg(all(curve25519_dalek_backend = "simd", nightly))]
154            VartimePrecomputedStraus::Avx512ifma(inner) => inner.optional_mixed_multiscalar_mul(
155                static_scalars,
156                dynamic_scalars,
157                dynamic_points,
158            ),
159            VartimePrecomputedStraus::Scalar(inner) => inner.optional_mixed_multiscalar_mul(
160                static_scalars,
161                dynamic_scalars,
162                dynamic_points,
163            ),
164        }
165    }
166}
167
168#[allow(missing_docs)]
169#[cfg(feature = "alloc")]
170pub fn straus_multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
171where
172    I: IntoIterator,
173    I::Item: core::borrow::Borrow<Scalar>,
174    J: IntoIterator,
175    J::Item: core::borrow::Borrow<EdwardsPoint>,
176{
177    use crate::traits::MultiscalarMul;
178
179    match get_selected_backend() {
180        #[cfg(curve25519_dalek_backend = "simd")]
181        BackendKind::Avx2 => {
182            vector::scalar_mul::straus::spec_avx2::Straus::multiscalar_mul::<I, J>(scalars, points)
183        }
184        #[cfg(all(curve25519_dalek_backend = "simd", nightly))]
185        BackendKind::Avx512 => {
186            vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::multiscalar_mul::<I, J>(
187                scalars, points,
188            )
189        }
190        BackendKind::Serial => {
191            serial::scalar_mul::straus::Straus::multiscalar_mul::<I, J>(scalars, points)
192        }
193    }
194}
195
196#[allow(missing_docs)]
197#[cfg(feature = "alloc")]
198pub fn straus_optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
199where
200    I: IntoIterator,
201    I::Item: core::borrow::Borrow<Scalar>,
202    J: IntoIterator<Item = Option<EdwardsPoint>>,
203{
204    use crate::traits::VartimeMultiscalarMul;
205
206    match get_selected_backend() {
207        #[cfg(curve25519_dalek_backend = "simd")]
208        BackendKind::Avx2 => {
209            vector::scalar_mul::straus::spec_avx2::Straus::optional_multiscalar_mul::<I, J>(
210                scalars, points,
211            )
212        }
213        #[cfg(all(curve25519_dalek_backend = "simd", nightly))]
214        BackendKind::Avx512 => {
215            vector::scalar_mul::straus::spec_avx512ifma_avx512vl::Straus::optional_multiscalar_mul::<
216                I,
217                J,
218            >(scalars, points)
219        }
220        BackendKind::Serial => {
221            serial::scalar_mul::straus::Straus::optional_multiscalar_mul::<I, J>(scalars, points)
222        }
223    }
224}
225
226/// Perform constant-time, variable-base scalar multiplication.
227pub fn variable_base_mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint {
228    match get_selected_backend() {
229        #[cfg(curve25519_dalek_backend = "simd")]
230        BackendKind::Avx2 => vector::scalar_mul::variable_base::spec_avx2::mul(point, scalar),
231        #[cfg(all(curve25519_dalek_backend = "simd", nightly))]
232        BackendKind::Avx512 => {
233            vector::scalar_mul::variable_base::spec_avx512ifma_avx512vl::mul(point, scalar)
234        }
235        BackendKind::Serial => serial::scalar_mul::variable_base::mul(point, scalar),
236    }
237}
238
239/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
240#[allow(non_snake_case)]
241pub fn vartime_double_base_mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint {
242    match get_selected_backend() {
243        #[cfg(curve25519_dalek_backend = "simd")]
244        BackendKind::Avx2 => vector::scalar_mul::vartime_double_base::spec_avx2::mul(a, A, b),
245        #[cfg(all(curve25519_dalek_backend = "simd", nightly))]
246        BackendKind::Avx512 => {
247            vector::scalar_mul::vartime_double_base::spec_avx512ifma_avx512vl::mul(a, A, b)
248        }
249        BackendKind::Serial => serial::scalar_mul::vartime_double_base::mul(a, A, b),
250    }
251}