// -*- mode: rust; -*- // // This file is part of curve25519-dalek. // Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence // See LICENSE for licensing information. // // Authors: // - Isis Agora Lovecruft // - Henry de Valence //! Code for fixed- and sliding-window functionality #![allow(non_snake_case)] use core::fmt::Debug; use subtle::ConditionallyNegatable; use subtle::ConditionallyAssignable; use subtle::ConstantTimeEq; use subtle::Choice; use traits::Identity; /// A lookup table of precomputed multiples of a point \\(P\\), used to /// compute \\( xP \\) for \\( -8 \leq x \leq 8 \\). /// /// The computation of \\( xP \\) is done in constant time by the `select` function. /// /// Since `LookupTable` does not implement `Index`, it's more difficult /// to accidentally use the table directly. Unfortunately the table is /// only `pub(crate)` so that we can write hardcoded constants, so it's /// still technically possible. It would be nice to prevent direct /// access to the table. /// /// XXX make this generic with respect to table size #[derive(Copy, Clone)] pub struct LookupTable(pub(crate) [T; 8]); use clear_on_drop::clear::ZeroSafe; /// This type isn't actually zeroable (all zero bytes are not valid /// points), but we want to be able to use `clear_on_drop` to erase slices /// of `LookupTable`. /// /// Since the `ZeroSafe` trait is only used by `clear_on_drop`, the only /// situation where this would be a problem is if code attempted to use /// a `ClearOnDrop` to erase a `LookupTable` and then used the table /// afterwards. /// /// Normally this is not a problem, since the table's storage is usually /// dropped too. /// /// XXX is this a good compromise? unsafe impl ZeroSafe for LookupTable {} impl LookupTable where T: Identity + ConditionallyAssignable + ConditionallyNegatable { /// Given \\(-8 \leq x \leq 8\\), return \\(xP\\) in constant time. pub fn select(&self, x: i8) -> T { debug_assert!(x >= -8); debug_assert!(x <= 8); // Compute xabs = |x| let xmask = x >> 7; let xabs = (x + xmask) ^ xmask; // Set t = 0 * P = identity let mut t = T::identity(); for j in 1..9 { // Copy `points[j-1] == j*P` onto `t` in constant time if `|x| == j`. let c = (xabs as u8).ct_eq(&(j as u8)); t.conditional_assign(&self.0[j-1], c); } // Now t == |x| * P. let neg_mask = Choice::from((xmask & 1) as u8); t.conditional_negate(neg_mask); // Now t == x * P. t } } impl Default for LookupTable { fn default() -> LookupTable { LookupTable([T::default(); 8]) } } impl Debug for LookupTable { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "LookupTable({:?})", self.0) } } use edwards::EdwardsPoint; use curve_models::ProjectiveNielsPoint; use curve_models::AffineNielsPoint; impl<'a> From<&'a EdwardsPoint> for LookupTable { fn from(P: &'a EdwardsPoint) -> Self { let mut points = [P.to_projective_niels(); 8]; for j in 0..7 { points[j+1] = (P + &points[j]) .to_extended() .to_projective_niels(); } LookupTable(points) } } impl<'a> From<&'a EdwardsPoint> for LookupTable { fn from(P: &'a EdwardsPoint) -> Self { let mut points = [P.to_affine_niels(); 8]; // XXX batch inversion would be good if perf mattered here for j in 0..7 { points[j+1] = (P + &points[j]) .to_extended() .to_affine_niels() } LookupTable(points) } }