Document algorithm for NAFs

This commit is contained in:
Henry de Valence 2018-04-06 12:11:59 -07:00
parent 6b768c2a1a
commit 68bbd1bd03

View file

@ -80,6 +80,7 @@ pub struct Scalar {
/// ///
/// The integer representing this scalar must be bounded above by \\(2\^{255}\\), or equivalently the high bit of `bytes[31]` must be zero. /// The integer representing this scalar must be bounded above by \\(2\^{255}\\), or equivalently the high bit of `bytes[31]` must be zero.
/// ///
/// This ensures that there is room for a carry bit when computing a NAF representation.
// XXX This is pub(crate) so we can write literal constants. If const fns were stable, we could make the Scalar constructors const fns and use those instead. // XXX This is pub(crate) so we can write literal constants. If const fns were stable, we could make the Scalar constructors const fns and use those instead.
pub(crate) bytes: [u8; 32], pub(crate) bytes: [u8; 32],
} }
@ -497,17 +498,79 @@ impl Scalar {
/// ///
/// A width-\\(w\\) NAF of a positive integer \\(k\\) is an expression /// A width-\\(w\\) NAF of a positive integer \\(k\\) is an expression
/// $$ /// $$
/// k = \sum_{i=0}\^n k\_i 2\^i, /// k = \sum_{i=0}\^m n\_i 2\^i,
/// $$ /// $$
/// where each nonzero /// where each nonzero
/// coefficient \\(k\_i\\) is odd and bounded by \\(|k\_i| < 2\^{w-1}\\), /// coefficient \\(n\_i\\) is odd and bounded by \\(|n\_i| < 2\^{w-1}\\),
/// \\(k\_{n-1}\\) is nonzero, and at most one of any \\(w\\) consecutive /// \\(n\_{m-1}\\) is nonzero, and at most one of any \\(w\\) consecutive
/// coefficients is nonzero. (Hankerson, Menezes, Vanstone; def 3.32). /// coefficients is nonzero. (Hankerson, Menezes, Vanstone; def 3.32).
/// ///
/// The length of the NAF is at most one more than the length of
/// the binary representation of \\(k\\). This is why the
/// `Scalar` type maintains an invariant that the top bit is
/// \\(0\\), so that the NAF of a scalar has at most 256 digits.
///
/// Intuitively, this is like a binary expansion, except that we /// Intuitively, this is like a binary expansion, except that we
/// allow some coefficients to grow up to \\(2\^{w-1}\\) so that the /// allow some coefficients to grow in magnitude up to
/// nonzero coefficients are as sparse as possible. /// \\(2\^{w-1}\\) so that the nonzero coefficients are as sparse
/// as possible.
///
/// When doing scalar multiplication, we can then use a lookup
/// table of precomputed multiples of a point to add the nonzero
/// terms \\( k_i P \\). Using signed digits cuts the table size
/// in half, and using odd digits cuts the table size in half
/// again.
///
/// To compute a \\(w\\)-NAF, we use a modification of Algorithm 3.35 of HMV:
///
/// 1. \\( i \gets 0 \\)
/// 2. While \\( k \ge 1 \\):
/// 1. If \\(k\\) is odd, \\( n_i \gets k \operatorname{mods} 2^w \\), \\( k \gets k - n_i \\).
/// 2. If \\(k\\) is even, \\( n_i \gets 0 \\).
/// 3. \\( k \gets k / 2 \\), \\( i \gets i + 1 \\).
/// 3. Return \\( n_0, n_1, ... , \\)
///
/// Here \\( \bar x = x \operatorname{mods} 2^w \\) means the
/// \\( \bar x \\) with \\( \bar x \equiv x \pmod{2^w} \\) and
/// \\( -2^{w-1} \leq \bar x < 2^w \\).
///
/// We implement this by scanning across the bits of \\(k\\) from LSB to MSB.
/// Write the bits of \\(k\\) as
/// $$
/// k = \sum\_{i=0}\^m k\_i 2^i,
/// $$
/// and split the sum as
/// $$
/// k = \sum\_{i=0}^{w-1} k\_i 2^i + 2^w \sum\_{i=0} k\_{i+w} 2^i
/// $$
/// where the first part is \\( k \mod 2^w \\).
///
/// If \\( k \mod 2^w\\) is odd, and \\( k \mod 2^w < 2^{w-1} \\), then we emit
/// \\( n_0 = k \mod 2^w \\). Instead of computing
/// \\( k - n_0 \\), we just advance \\(w\\) bits and reindex.
///
/// If \\( k \mod 2^w\\) is odd, and \\( k \mod 2^w \ge 2^{w-1} \\), then
/// \\( n_0 = k \operatorname{mods} 2^w = k \mod 2^w - 2^w \\).
/// The quantity \\( k - n_0 \\) is
/// $$
/// \begin{aligned}
/// k - n_0 &= \sum\_{i=0}^{w-1} k\_i 2^i + 2^w \sum\_{i=0} k\_{i+w} 2^i
/// - \sum\_{i=0}^{w-1} k\_i 2^i + 2^w \\\\
/// &= 2^w + 2^w \sum\_{i=0} k\_{i+w} 2^i
/// \end{aligned}
/// $$
/// so instead of computing the subtraction, we can set a carry
/// bit, advance \\(w\\) bits, and reindex.
///
/// If \\( k \mod 2^w\\) is even, we emit \\(0\\), advance 1 bit
/// and reindex. In fact, by setting all digits to \\(0\\)
/// initially, we don't need to emit anything.
pub(crate) fn non_adjacent_form(&self, w: usize) -> [i8; 256] { pub(crate) fn non_adjacent_form(&self, w: usize) -> [i8; 256] {
// required by the NAF definition
debug_assert!( w >= 2 );
// required so that the NAF digits fit in i8
debug_assert!( w <= 8 );
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
let mut naf = [0i8; 256]; let mut naf = [0i8; 256];
@ -564,7 +627,7 @@ impl Scalar {
/// $$ /// $$
/// a = a\_0 + a\_1 16\^1 + \cdots + a_{63} 16\^{63}, /// a = a\_0 + a\_1 16\^1 + \cdots + a_{63} 16\^{63},
/// $$ /// $$
/// with \\(-8 \leq a_i < 8\\) for \\(0 \leq i < 63\\) and \\(-8 \leq a_63 \leq 8\\). /// with \\(-8 \leq a_i < 8\\) for \\(0 \leq i < 63\\) and \\(-8 \leq a_{63} \leq 8\\).
pub(crate) fn to_radix_16(&self) -> [i8; 64] { pub(crate) fn to_radix_16(&self) -> [i8; 64] {
debug_assert!(self[31] <= 127); debug_assert!(self[31] <= 127);
let mut output = [0i8; 64]; let mut output = [0i8; 64];