betrusted-curve25519-dalek-.../src/field.rs
bunnie 98dfce7ec2 fix remaining configuration warts
builds clealy as a test crate, without any dependency on
xous proper for configurations that don't involve xous.
2021-06-27 03:02:18 +08:00

1299 lines
52 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// -*- mode: rust; -*-
//
// This file is part of curve25519-dalek.
// Copyright (c) 2016-2021 isis agora lovecruft
// Copyright (c) 2016-2019 Henry de Valence
// See LICENSE for licensing information.
//
// Authors:
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
// - Henry de Valence <hdevalence@hdevalence.ca>
//! Field arithmetic modulo \\(p = 2\^{255} - 19\\).
//!
//! The `curve25519_dalek::field` module provides a type alias
//! `curve25519_dalek::field::FieldElement` to a field element type
//! defined in the `backend` module; either `FieldElement51` or
//! `FieldElement2625`.
//!
//! Field operations defined in terms of machine
//! operations, such as field multiplication or squaring, are defined in
//! the backend implementation.
//!
//! Field operations defined in terms of other field operations, such as
//! field inversion or square roots, are defined here.
use core::cmp::{Eq, PartialEq};
use subtle::ConditionallySelectable;
use subtle::ConditionallyNegatable;
use subtle::Choice;
use subtle::ConstantTimeEq;
use constants;
use backend;
#[cfg(feature = "fiat_u32_backend")]
pub use backend::serial::fiat_u32::field::*;
#[cfg(feature = "fiat_u64_backend")]
pub use backend::serial::fiat_u64::field::*;
/// A `FieldElement` represents an element of the field
/// \\( \mathbb Z / (2\^{255} - 19)\\).
///
/// The `FieldElement` type is an alias for one of the platform-specific
/// implementations.
/// Using formally-verified field arithmetic from fiat-crypto
#[cfg(feature = "fiat_u32_backend")]
pub type FieldElement = backend::serial::fiat_u32::field::FieldElement2625;
#[cfg(feature = "fiat_u64_backend")]
pub type FieldElement = backend::serial::fiat_u64::field::FieldElement51;
#[cfg(feature = "u64_backend")]
pub use backend::serial::u64::field::*;
/// A `FieldElement` represents an element of the field
/// \\( \mathbb Z / (2\^{255} - 19)\\).
///
/// The `FieldElement` type is an alias for one of the platform-specific
/// implementations.
#[cfg(feature = "u64_backend")]
pub type FieldElement = backend::serial::u64::field::FieldElement51;
#[cfg(feature = "u32_backend")]
pub use backend::serial::u32::field::*;
/// A `FieldElement` represents an element of the field
/// \\( \mathbb Z / (2\^{255} - 19)\\).
///
/// The `FieldElement` type is an alias for one of the platform-specific
/// implementations.
#[cfg(feature = "u32_backend")]
pub type FieldElement = backend::serial::u32::field::FieldElement2625;
impl Eq for FieldElement {}
impl PartialEq for FieldElement {
fn eq(&self, other: &FieldElement) -> bool {
self.ct_eq(other).unwrap_u8() == 1u8
}
}
impl ConstantTimeEq for FieldElement {
/// Test equality between two `FieldElement`s. Since the
/// internal representation is not canonical, the field elements
/// are normalized to wire format before comparison.
fn ct_eq(&self, other: &FieldElement) -> Choice {
self.to_bytes().ct_eq(&other.to_bytes())
}
}
impl FieldElement {
/// Determine if this `FieldElement` is negative, in the sense
/// used in the ed25519 paper: `x` is negative if the low bit is
/// set.
///
/// # Return
///
/// If negative, return `Choice(1)`. Otherwise, return `Choice(0)`.
pub fn is_negative(&self) -> Choice {
let bytes = self.to_bytes();
(bytes[0] & 1).into()
}
/// Determine if this `FieldElement` is zero.
///
/// # Return
///
/// If zero, return `Choice(1)`. Otherwise, return `Choice(0)`.
pub fn is_zero(&self) -> Choice {
let zero = [0u8; 32];
let bytes = self.to_bytes();
bytes.ct_eq(&zero)
}
/// Compute (self^(2^250-1), self^11), used as a helper function
/// within invert() and pow22523().
fn pow22501(&self) -> (FieldElement, FieldElement) {
// Instead of managing which temporary variables are used
// for what, we define as many as we need and leave stack
// allocation to the compiler
//
// Each temporary variable t_i is of the form (self)^e_i.
// Squaring t_i corresponds to multiplying e_i by 2,
// so the pow2k function shifts e_i left by k places.
// Multiplying t_i and t_j corresponds to adding e_i + e_j.
//
// Temporary t_i Nonzero bits of e_i
//
let t0 = self.square(); // 1 e_0 = 2^1
let t1 = t0.square().square(); // 3 e_1 = 2^3
let t2 = self * &t1; // 3,0 e_2 = 2^3 + 2^0
let t3 = &t0 * &t2; // 3,1,0
let t4 = t3.square(); // 4,2,1
let t5 = &t2 * &t4; // 4,3,2,1,0
let t6 = t5.pow2k(5); // 9,8,7,6,5
let t7 = &t6 * &t5; // 9,8,7,6,5,4,3,2,1,0
let t8 = t7.pow2k(10); // 19..10
let t9 = &t8 * &t7; // 19..0
let t10 = t9.pow2k(20); // 39..20
let t11 = &t10 * &t9; // 39..0
let t12 = t11.pow2k(10); // 49..10
let t13 = &t12 * &t7; // 49..0
let t14 = t13.pow2k(50); // 99..50
let t15 = &t14 * &t13; // 99..0
let t16 = t15.pow2k(100); // 199..100
let t17 = &t16 * &t15; // 199..0
let t18 = t17.pow2k(50); // 249..50
let t19 = &t18 * &t13; // 249..0
(t19, t3)
}
/// Given a slice of public `FieldElements`, replace each with its inverse.
///
/// All input `FieldElements` **MUST** be nonzero.
#[cfg(feature = "alloc")]
pub fn batch_invert(inputs: &mut [FieldElement]) {
// Montgomerys Trick and Fast Implementation of Masked AES
// Genelle, Prouff and Quisquater
// Section 3.2
let n = inputs.len();
let mut scratch = vec![FieldElement::one(); n];
// Keep an accumulator of all of the previous products
let mut acc = FieldElement::one();
// Pass through the input vector, recording the previous
// products in the scratch space
for (input, scratch) in inputs.iter().zip(scratch.iter_mut()) {
*scratch = acc;
acc = &acc * input;
}
// acc is nonzero iff all inputs are nonzero
assert_eq!(acc.is_zero().unwrap_u8(), 0);
// Compute the inverse of all products
acc = acc.invert();
// Pass through the vector backwards to compute the inverses
// in place
for (input, scratch) in inputs.iter_mut().rev().zip(scratch.into_iter().rev()) {
let tmp = &acc * input;
*input = &acc * &scratch;
acc = tmp;
}
}
/// Given a nonzero field element, compute its inverse.
///
/// The inverse is computed as self^(p-2), since
/// x^(p-2)x = x^(p-1) = 1 (mod p).
///
/// This function returns zero on input zero.
pub fn invert(&self) -> FieldElement {
// The bits of p-2 = 2^255 -19 -2 are 11010111111...11.
//
// nonzero bits of exponent
let (t19, t3) = self.pow22501(); // t19: 249..0 ; t3: 3,1,0
let t20 = t19.pow2k(5); // 254..5
let t21 = &t20 * &t3; // 254..5,3,1,0
t21
}
/// Raise this field element to the power (p-5)/8 = 2^252 -3.
fn pow_p58(&self) -> FieldElement {
// The bits of (p-5)/8 are 101111.....11.
//
// nonzero bits of exponent
let (t19, _) = self.pow22501(); // 249..0
let t20 = t19.pow2k(2); // 251..2
let t21 = self * &t20; // 251..2,0
t21
}
/// Given `FieldElements` `u` and `v`, compute either `sqrt(u/v)`
/// or `sqrt(i*u/v)` in constant time.
///
/// This function always returns the nonnegative square root.
///
/// # Return
///
/// - `(Choice(1), +sqrt(u/v)) ` if `v` is nonzero and `u/v` is square;
/// - `(Choice(1), zero) ` if `u` is zero;
/// - `(Choice(0), zero) ` if `v` is zero and `u` is nonzero;
/// - `(Choice(0), +sqrt(i*u/v))` if `u/v` is nonsquare (so `i*u/v` is square).
///
pub fn sqrt_ratio_i(u: &FieldElement, v: &FieldElement) -> (Choice, FieldElement) {
// Using the same trick as in ed25519 decoding, we merge the
// inversion, the square root, and the square test as follows.
//
// To compute sqrt(α), we can compute β = α^((p+3)/8).
// Then β^2 = ±α, so multiplying β by sqrt(-1) if necessary
// gives sqrt(α).
//
// To compute 1/sqrt(α), we observe that
// 1/β = α^(p-1 - (p+3)/8) = α^((7p-11)/8)
// = α^3 * (α^7)^((p-5)/8).
//
// We can therefore compute sqrt(u/v) = sqrt(u)/sqrt(v)
// by first computing
// r = u^((p+3)/8) v^(p-1-(p+3)/8)
// = u u^((p-5)/8) v^3 (v^7)^((p-5)/8)
// = (uv^3) (uv^7)^((p-5)/8).
//
// If v is nonzero and u/v is square, then r^2 = ±u/v,
// so vr^2 = ±u.
// If vr^2 = u, then sqrt(u/v) = r.
// If vr^2 = -u, then sqrt(u/v) = r*sqrt(-1).
//
// If v is zero, r is also zero.
let v3 = &v.square() * v;
let v7 = &v3.square() * v;
let mut r = &(u * &v3) * &(u * &v7).pow_p58();
let check = v * &r.square();
let i = &constants::SQRT_M1;
let correct_sign_sqrt = check.ct_eq( u);
let flipped_sign_sqrt = check.ct_eq( &(-u));
let flipped_sign_sqrt_i = check.ct_eq(&(&(-u)*i));
let r_prime = &constants::SQRT_M1 * &r;
r.conditional_assign(&r_prime, flipped_sign_sqrt | flipped_sign_sqrt_i);
// Choose the nonnegative square root.
let r_is_negative = r.is_negative();
r.conditional_negate(r_is_negative);
let was_nonzero_square = correct_sign_sqrt | flipped_sign_sqrt;
(was_nonzero_square, r)
}
/// Attempt to compute `sqrt(1/self)` in constant time.
///
/// Convenience wrapper around `sqrt_ratio_i`.
///
/// This function always returns the nonnegative square root.
///
/// # Return
///
/// - `(Choice(1), +sqrt(1/self)) ` if `self` is a nonzero square;
/// - `(Choice(0), zero) ` if `self` is zero;
/// - `(Choice(0), +sqrt(i/self)) ` if `self` is a nonzero nonsquare;
///
pub fn invsqrt(&self) -> (Choice, FieldElement) {
FieldElement::sqrt_ratio_i(&FieldElement::one(), self)
}
}
#[cfg(test)]
extern crate rand;
#[cfg(test)]
mod test {
use field::*;
use subtle::ConditionallyNegatable;
/// Random element a of GF(2^255-19), from Sage
/// a = 1070314506888354081329385823235218444233221\
/// 2228051251926706380353716438957572
static A_BYTES: [u8; 32] =
[ 0x04, 0xfe, 0xdf, 0x98, 0xa7, 0xfa, 0x0a, 0x68,
0x84, 0x92, 0xbd, 0x59, 0x08, 0x07, 0xa7, 0x03,
0x9e, 0xd1, 0xf6, 0xf2, 0xe1, 0xd9, 0xe2, 0xa4,
0xa4, 0x51, 0x47, 0x36, 0xf3, 0xc3, 0xa9, 0x17];
/// Byte representation of a**2
static ASQ_BYTES: [u8; 32] =
[ 0x75, 0x97, 0x24, 0x9e, 0xe6, 0x06, 0xfe, 0xab,
0x24, 0x04, 0x56, 0x68, 0x07, 0x91, 0x2d, 0x5d,
0x0b, 0x0f, 0x3f, 0x1c, 0xb2, 0x6e, 0xf2, 0xe2,
0x63, 0x9c, 0x12, 0xba, 0x73, 0x0b, 0xe3, 0x62];
/// Byte representation of 1/a
static AINV_BYTES: [u8; 32] =
[0x96, 0x1b, 0xcd, 0x8d, 0x4d, 0x5e, 0xa2, 0x3a,
0xe9, 0x36, 0x37, 0x93, 0xdb, 0x7b, 0x4d, 0x70,
0xb8, 0x0d, 0xc0, 0x55, 0xd0, 0x4c, 0x1d, 0x7b,
0x90, 0x71, 0xd8, 0xe9, 0xb6, 0x18, 0xe6, 0x30];
/// Byte representation of a^((p-5)/8)
static AP58_BYTES: [u8; 32] =
[0x6a, 0x4f, 0x24, 0x89, 0x1f, 0x57, 0x60, 0x36,
0xd0, 0xbe, 0x12, 0x3c, 0x8f, 0xf5, 0xb1, 0x59,
0xe0, 0xf0, 0xb8, 0x1b, 0x20, 0xd2, 0xb5, 0x1f,
0x15, 0x21, 0xf9, 0xe3, 0xe1, 0x61, 0x21, 0x55];
#[test]
fn a_mul_a_vs_a_squared_constant() {
let a = FieldElement::from_bytes(&A_BYTES);
let asq = FieldElement::from_bytes(&ASQ_BYTES);
assert_eq!(asq, &a * &a);
}
#[test]
fn a_square_vs_a_squared_constant() {
let a = FieldElement::from_bytes(&A_BYTES);
let asq = FieldElement::from_bytes(&ASQ_BYTES);
assert_eq!(asq, a.square());
}
#[test]
fn a_square2_vs_a_squared_constant() {
let a = FieldElement::from_bytes(&A_BYTES);
let asq = FieldElement::from_bytes(&ASQ_BYTES);
assert_eq!(a.square2(), &asq+&asq);
}
#[test]
fn a_invert_vs_inverse_of_a_constant() {
let a = FieldElement::from_bytes(&A_BYTES);
let ainv = FieldElement::from_bytes(&AINV_BYTES);
let should_be_inverse = a.invert();
assert_eq!(ainv, should_be_inverse);
assert_eq!(FieldElement::one(), &a * &should_be_inverse);
}
#[test]
fn batch_invert_a_matches_nonbatched() {
let a = FieldElement::from_bytes(&A_BYTES);
let ap58 = FieldElement::from_bytes(&AP58_BYTES);
let asq = FieldElement::from_bytes(&ASQ_BYTES);
let ainv = FieldElement::from_bytes(&AINV_BYTES);
let a2 = &a + &a;
let a_list = vec![a, ap58, asq, ainv, a2];
let mut ainv_list = a_list.clone();
FieldElement::batch_invert(&mut ainv_list[..]);
for i in 0..5 {
assert_eq!(a_list[i].invert(), ainv_list[i]);
}
}
#[test]
fn sqrt_ratio_behavior() {
let zero = FieldElement::zero();
let one = FieldElement::one();
let i = constants::SQRT_M1;
let two = &one + &one; // 2 is nonsquare mod p.
let four = &two + &two; // 4 is square mod p.
// 0/0 should return (1, 0) since u is 0
let (choice, sqrt) = FieldElement::sqrt_ratio_i(&zero, &zero);
assert_eq!(choice.unwrap_u8(), 1);
assert_eq!(sqrt, zero);
assert_eq!(sqrt.is_negative().unwrap_u8(), 0);
// 1/0 should return (0, 0) since v is 0, u is nonzero
let (choice, sqrt) = FieldElement::sqrt_ratio_i(&one, &zero);
assert_eq!(choice.unwrap_u8(), 0);
assert_eq!(sqrt, zero);
assert_eq!(sqrt.is_negative().unwrap_u8(), 0);
// 2/1 is nonsquare, so we expect (0, sqrt(i*2))
let (choice, sqrt) = FieldElement::sqrt_ratio_i(&two, &one);
assert_eq!(choice.unwrap_u8(), 0);
assert_eq!(sqrt.square(), &two * &i);
assert_eq!(sqrt.is_negative().unwrap_u8(), 0);
// 4/1 is square, so we expect (1, sqrt(4))
let (choice, sqrt) = FieldElement::sqrt_ratio_i(&four, &one);
assert_eq!(choice.unwrap_u8(), 1);
assert_eq!(sqrt.square(), four);
assert_eq!(sqrt.is_negative().unwrap_u8(), 0);
// 1/4 is square, so we expect (1, 1/sqrt(4))
let (choice, sqrt) = FieldElement::sqrt_ratio_i(&one, &four);
assert_eq!(choice.unwrap_u8(), 1);
assert_eq!(&sqrt.square() * &four, one);
assert_eq!(sqrt.is_negative().unwrap_u8(), 0);
}
#[test]
fn a_p58_vs_ap58_constant() {
let a = FieldElement::from_bytes(&A_BYTES);
let ap58 = FieldElement::from_bytes(&AP58_BYTES);
assert_eq!(ap58, a.pow_p58());
}
#[test]
fn equality() {
let a = FieldElement::from_bytes(&A_BYTES);
let ainv = FieldElement::from_bytes(&AINV_BYTES);
assert!(a == a);
assert!(a != ainv);
}
/// Notice that the last element has the high bit set, which
/// should be ignored
static B_BYTES: [u8;32] =
[113, 191, 169, 143, 91, 234, 121, 15,
241, 131, 217, 36, 230, 101, 92, 234,
8, 208, 170, 251, 97, 127, 70, 210,
58, 23, 166, 87, 240, 169, 184, 178];
#[test]
fn from_bytes_highbit_is_ignored() {
let mut cleared_bytes = B_BYTES;
cleared_bytes[31] &= 127u8;
let with_highbit_set = FieldElement::from_bytes(&B_BYTES);
let without_highbit_set = FieldElement::from_bytes(&cleared_bytes);
assert_eq!(without_highbit_set, with_highbit_set);
}
#[test]
fn conditional_negate() {
let one = FieldElement::one();
let minus_one = FieldElement::minus_one();
let mut x = one;
x.conditional_negate(Choice::from(1));
assert_eq!(x, minus_one);
x.conditional_negate(Choice::from(0));
assert_eq!(x, minus_one);
x.conditional_negate(Choice::from(1));
assert_eq!(x, one);
}
#[test]
fn encoding_is_canonical() {
// Encode 1 wrongly as 1 + (2^255 - 19) = 2^255 - 18
let one_encoded_wrongly_bytes: [u8;32] = [0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f];
// Decode to a field element
let one = FieldElement::from_bytes(&one_encoded_wrongly_bytes);
// .. then check that the encoding is correct
let one_bytes = one.to_bytes();
assert_eq!(one_bytes[0], 1);
for i in 1..32 {
assert_eq!(one_bytes[i], 0);
}
}
#[test]
fn batch_invert_empty() {
FieldElement::batch_invert(&mut []);
}
#[test]
fn make_vectors() {
// reminder to self: to create just this vector, run
// cargo test field::test::make_vectors
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use engine25519_as::*;
use self::rand::Rng;
fn write_helper(file: &mut File, elem: FieldElement) {
let elem_bytes = elem.to_bytes();
let _ = file.write(&elem_bytes);
/*
for i in 0..elem_bytes.len()/4 {
let word: u32 = elem_bytes.pread::<u32>(i).unwrap();
let _ = write!(file, "{:02x}", elem_bytes[i]);
}
let _ = write!(file, " ");*/
}
fn write_test_header(file: &mut File,
loading_address: u32,
mcode: &[i32],
num_src_regs: u32,
reg_window: u32,
num_tests: u32,
) {
let mcode_len: u32 = mcode.len() as u32;
let _ = file.write(&(0x5645_4354 as u32).to_le_bytes());
let _ = file.write(&( ((loading_address & 0xFFFF << 16)
| (mcode_len & 0xFFFF)
) as u32)
.to_le_bytes() ); // load address 0 + code length
let _ = file.write(&( ((num_src_regs & 0x1F) << 27 // number of source registers per test
| (reg_window & 0xF) << 23 // register window to use
| (num_tests & 0x3F_FFFF) // number of tests
) as u32)
.to_le_bytes() ); // 2 registers, window 0, 1 vector
// the actual microcode
for i in 0..mcode_len {
let _ = file.write(&(mcode[i as usize] as u32).to_le_bytes());
}
// pad with 0's to a 256-bit stride
for _ in 0..(8 - ((3 + mcode_len) % 8)) { // 3 words for metadata + code length
let _ = file.write(&[0,0,0,0]); // write out 32-bit words of 0 (as array of u8)
}
}
let path = Path::new("test_vectors.bin");
let mut file = File::create(&path).unwrap();
// Metadata record format. Each test should have the following layout:
// 0x0 0x56454354 "VECT" - indicates a valid vector set
// 0x4 [31 load address 16] [15 length of code 0]
// 0x8 [31 N registers to load 27] [26 W window 23] [22 X number of vectors sets 0]
// 0xC [microcode] (variable length)
// [ padding of 0x0 until 0x20 * align ]
// 0x20*align [X test vectors]
// Records can repeat; as long as "VECT" is found, the test framework will attemp to load and run the test
//
// End of records MUST end with a word that is NOT 0x56454354
// This is because the ROM read can "wrap around" and the test will run forever
// We use 0xFFFF_FFFF to indicate this
//
// For each test, vectors are storted with the following convention:
// Check result is always in r31
// N Registers loaded starting at r0 into window W
fn test_add(mut file: &mut File) {
// test addition. two input registers (r0, r1), one output register (r31).
let num_src_regs = 2;
let reg_window = 0;
let num_tests = 5; // one manual test + 4 random vectors
let loading_address = 0; // microcode loading address
let mcode = assemble_engine25519!(
start:
add %2, %0, %1
trd %3, %2
sub %31, %2, %3
fin
);
write_test_header(&mut file, loading_address, &mcode, num_src_regs, reg_window, num_tests);
// test vectors
// 1 plus -1 = 0 -> this works overflow path
let a = FieldElement::one();
let b = FieldElement::minus_one();
let q = &a + &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// four random numbers
for _ in 0..4 {
let a = FieldElement::from_bytes(&rand::thread_rng().gen::<[u8; 32]>());
let b = FieldElement::from_bytes(&rand::thread_rng().gen::<[u8; 32]>());
let q = &a + &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
}
}
fn ref_fact(n: usize) -> FieldElement {
let mut a = FieldElement::one();
let mut result = FieldElement::one();
for _ in 0..n {
result = &result * &a;
a = &a + &FieldElement::one();
}
result
}
fn test_loop(mut file: &mut File) {
// test addition. two input registers (r0, r1), one output register (r31).
let num_src_regs = 1;
let reg_window = 0;
let num_tests = 5; // 5 sequential tests
let loading_address = 0; // microcode loading address
// compute a factorial using a loop
// also tests psa/psb with constants
// %0 is the argument, %31 is the result
// %10 is the multiplicand for the factorial
// %0 is re-used as the loop counter
let mcode = assemble_engine25519!(
start:
psa %10, #1
psb %31, #1
loop:
mul %31, %31, %10
add %10, %10, #1
sub %0, %0, #1
brz end, %0
brz loop, #0
end:
fin
);
write_test_header(&mut file, loading_address, &mcode, num_src_regs, reg_window, num_tests);
// test vectors
let mut n = FieldElement::one();
for i in 1..6 {
write_helper(&mut file, n);
n = &n + &FieldElement::one(); // mirror i's progression
let q = ref_fact(i);
write_helper(&mut file, q);
}
}
fn test_cswap(mut file: &mut File) {
// test cswap. three input registers: (r0, r1) to swap, (r2) to control swap, one output register (r31).
let num_src_regs = 3;
let reg_window = 15;
let num_tests = 4;
let loading_address = 0; // microcode loading address
// psa is used to move %0 to %31 mostly to test that psa works
let mcode = assemble_engine25519!(
start:
xor %30, %0, %1
msk %30, %2, %30
xor %0, %30, %0
xor %1, %30, %1
psa %31, %0
fin
);
write_test_header(&mut file, loading_address, &mcode, num_src_regs, reg_window, num_tests);
// test vectors
for i in 0..4 {
let a = FieldElement::from_bytes(&rand::thread_rng().gen::<[u8; 32]>());
let b = FieldElement::from_bytes(&rand::thread_rng().gen::<[u8; 32]>());
let swap: FieldElement;
let q: FieldElement;
if i % 2 == 0 {
swap = FieldElement::zero();
q = a;
} else {
swap = FieldElement::one();
q = b;
}
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, swap);
write_helper(&mut file, q);
}
}
fn test_mul(mut file: &mut File) {
let extra_tests = 100; // vary this to add more random vectors at the end
// test multiplier. two input registers: (r0, r1), one output register (r31).
let num_src_regs = 2;
let reg_window = 0;
let num_tests = 22 + extra_tests;
let loading_address = 0; // microcode loading address
let mcode = assemble_engine25519!(
start:
mul %31, %1, %0
fin
);
write_test_header(&mut file, loading_address, &mcode, num_src_regs, reg_window, num_tests);
// 1: 1*1 - simple case
let a = FieldElement::one();
let b = FieldElement::one();
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 2: 1*-1 - simple case
let a = FieldElement::one();
let b = FieldElement::minus_one();
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 3
let a = FieldElement::from_bytes(&[
0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7f,]);
let b = FieldElement::one();
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 4
let a = FieldElement::from_bytes(&[
0xA4, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x2A,
]);
let b = FieldElement::from_bytes(&[
3, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]);
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 5
let a = FieldElement::from_bytes(&[
0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7f,
]);
let b = FieldElement::from_bytes(&[
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]);
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 6
let a = FieldElement::from_bytes(&[
0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3f,
]);
let b = FieldElement::from_bytes(&[
2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]);
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 7
let a = FieldElement::from_bytes(&[
0xA5, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x2A,
]);
let b = FieldElement::from_bytes(&[
3, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]);
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 8
let a = FieldElement::from_bytes(&[
0xA5, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x2A,
]);
let b = FieldElement::from_bytes(&[
3, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]);
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 9: not normalized input!
let a = FieldElement::from_bytes(&[
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7f,
]);
let b = FieldElement::from_bytes(&[
1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]);
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 10
let a = FieldElement::from_bytes(&[
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3f,
]);
let b = FieldElement::from_bytes(&[
2, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]);
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 11
let a = FieldElement::from_bytes(&[
0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3f,
]);
let b = FieldElement::from_bytes(&[
4, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]);
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 12
let a = FieldElement::from_bytes(&[
0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x19,
]);
let b = FieldElement::from_bytes(&[
5, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
]);
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 13
let a = FieldElement::from_bytes(&[
0x94, 0xc2, 0xf9, 0x3b, 0xb7, 0xe7, 0xe5, 0x78,
0x22, 0x23, 0x00, 0x14, 0x55, 0x41, 0x56, 0x05,
0xb0, 0xfe, 0x1d, 0x61, 0x0d, 0x0b, 0x08, 0xc9,
0x22, 0x3a, 0xc4, 0x55, 0xcd, 0xb0, 0x93, 0x52,
]);
let b = FieldElement::from_bytes(&[
0x17, 0x0c, 0x1e, 0x93, 0xea, 0x6e, 0x51, 0xc0,
0xcb, 0xf9, 0x48, 0xe7, 0x60, 0x36, 0x1f, 0xaf,
0x65, 0x8d, 0xf2, 0xe9, 0x36, 0xd2, 0x71, 0x00,
0x94, 0x56, 0x48, 0x55, 0x1c, 0xe9, 0x48, 0x1d,
]);
let q = &a * &b;
// 08 55 8c eb 97 70 ea b5 da c7 eb 83 d1 3a b3 a7
// 99 31 f4 be 87 3c 26 e9 1c d0 9c 82 08 da 5c 0d
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 14
let a = FieldElement::from_bytes(&[
0x6d, 0xad, 0x72, 0xf8, 0x64, 0x1b, 0x8f, 0x43,
0xba, 0x50, 0xb5, 0x83, 0xe1, 0x5f, 0xd6, 0x43,
0x9b, 0xb2, 0xbc, 0x60, 0xae, 0x92, 0x3a, 0xdb,
0x05, 0x83, 0x4a, 0xd6, 0x19, 0x36, 0x95, 0x87,
]);
let b = FieldElement::from_bytes(&[
0xe4, 0x34, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
let q = &a * &b;
// 000002a6 bc74dd8d 2e43fe6f 23132ca5 d3e70179 c129465a 7c4d49cc ccf864a7
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
// 0xa7, 0x64, 0xf8, 0xcc, 0xcc, 0x49, 0x4d, 0x7c,
// 0x5a, 0x46, 0x29, 0xc1, 0x79, 0x01, 0xe7, 0xd3,
// 0xa5, 0x2c, 0x13, 0x23, 0x6f, 0xfe, 0x43, 0x2e,
// 0x8d, 0xdd, 0x74, 0xbc, 0xa6, 0x02, 0x00, 0x00,
// 15-22
for _ in 0..(8+extra_tests) {
let a = FieldElement::from_bytes(&rand::thread_rng().gen::<[u8; 32]>());
let b = FieldElement::from_bytes(&rand::thread_rng().gen::<[u8; 32]>());
let q = &a * &b;
write_helper(&mut file, a);
write_helper(&mut file, b);
write_helper(&mut file, q);
}
}
fn test_diff_add_and_double(mut file: &mut File) {
use montgomery::ProjectivePoint;
// test cswap. three input registers: (r0, r1) to swap, (r2) to control swap, one output register (r31).
let num_src_regs = 5;
let reg_window = 0;
let num_tests = 8;
let loading_address = 0; // microcode loading address
let mcode = assemble_engine25519!(
start:
// test preamble
psa %20, %0
psa %21, %1
psa %22, %2
psa %23, %3
psa %24, %4
// P.U in %20
// P.W in %21
// Q.U in %22
// Q.W in %23
// affine_PmQ in %24
// %30 is the TRD scratch register
// %29 is the subtraction temporary value register
// let t0 = &P.U + &P.W;
add %0, %20, %21
trd %30, %0
sub %0, %0, %30
// let t1 = &P.U - &P.W;
sub %21, #3, %21 // negate &P.W using #FIELDPRIME (#3)
add %1, %20, %21
trd %30, %1
sub %1, %1, %30
// let t2 = &Q.U + &Q.W;
add %2, %22, %23
trd %30, %2
sub %2, %2, %30
// let t3 = &Q.U - &Q.W;
sub %23, #3, %23
add %3, %22, %23
trd %30, %3
sub %3, %3, %30
// let t4 = t0.square(); // (U_P + W_P)^2 = U_P^2 + 2 U_P W_P + W_P^2
mul %4, %0, %0
// let t5 = t1.square(); // (U_P - W_P)^2 = U_P^2 - 2 U_P W_P + W_P^2
mul %5, %1, %1
// let t6 = &t4 - &t5; // 4 U_P W_P
sub %29, #3, %5
add %6, %4, %29
trd %30, %6
sub %6, %6, %30
// let t7 = &t0 * &t3; // (U_P + W_P) (U_Q - W_Q) = U_P U_Q + W_P U_Q - U_P W_Q - W_P W_Q
mul %7, %0, %3
// let t8 = &t1 * &t2; // (U_P - W_P) (U_Q + W_Q) = U_P U_Q - W_P U_Q + U_P W_Q - W_P W_Q
mul %8, %1, %2
// let t9 = &t7 + &t8; // 2 (U_P U_Q - W_P W_Q)
add %9, %7, %8
trd %30, %9
sub %9, %9, %30
// let t10 = &t7 - &t8; // 2 (W_P U_Q - U_P W_Q)
sub %29, #3, %8
add %10, %7, %29
trd %30, %10
sub %10, %10, %30
// let t11 = t9.square(); // 4 (U_P U_Q - W_P W_Q)^2
mul %11, %9, %9
// let t12 = t10.square(); // 4 (W_P U_Q - U_P W_Q)^2
mul %12, %10, %10
// let t13 = &APLUS2_OVER_FOUR * &t6; // (A + 2) U_P U_Q
mul %13, #4, %6 // #4 is A+2/4
// let t14 = &t4 * &t5; // ((U_P + W_P)(U_P - W_P))^2 = (U_P^2 - W_P^2)^2
mul %14, %4, %5
// let t15 = &t13 + &t5; // (U_P - W_P)^2 + (A + 2) U_P W_P
add %15, %13, %5
trd %30, %15
sub %15, %15, %30
// let t16 = &t6 * &t15; // 4 (U_P W_P) ((U_P - W_P)^2 + (A + 2) U_P W_P)
mul %16, %6, %15
// let t17 = affine_PmQ * &t12; // U_D * 4 (W_P U_Q - U_P W_Q)^2
mul %17, %24, %12 // affine_PmQ loaded into %24
///// these can be eliminated down the road, but included for 1:1 algorithm correspodence to reference in early testing
// let t18 = t11; // W_D * 4 (U_P U_Q - W_P W_Q)^2
psa %18, %11
// P.U = t14; // U_{P'} = (U_P + W_P)^2 (U_P - W_P)^2
psa %20, %14
// P.W = t16; // W_{P'} = (4 U_P W_P) ((U_P - W_P)^2 + ((A + 2)/4) 4 U_P W_P)
psa %21, %16
// Q.U = t18; // U_{Q'} = W_D * 4 (U_P U_Q - W_P W_Q)^2
psa %22, %18
// Q.W = t17; // W_{Q'} = U_D * 4 (W_P U_Q - U_P W_Q)^2
psa %23, %17
// test postamble -- sum together the points to create a single composite test output
add %31, %20, %21
trd %30, %31
sub %31, %31, %30
add %31, %31, %22
trd %30, %31
sub %31, %31, %30
add %31, %31, %23
trd %30, %31
sub %31, %31, %30 // leave result in r31
fin // finish execution
); write_test_header(&mut file, loading_address, &mcode, num_src_regs, reg_window, num_tests);
use montgomery::differential_add_and_double;
// test vectors
for _ in 0..8 {
let pu = FieldElement::from_bytes(&rand::thread_rng().gen::<[u8; 32]>());
let pw = FieldElement::from_bytes(&rand::thread_rng().gen::<[u8; 32]>());
let qu = FieldElement::from_bytes(&rand::thread_rng().gen::<[u8; 32]>());
let qw = FieldElement::from_bytes(&rand::thread_rng().gen::<[u8; 32]>());
let pmq = FieldElement::from_bytes(&rand::thread_rng().gen::<[u8; 32]>());
write_helper(&mut file, pu);
write_helper(&mut file, pw);
write_helper(&mut file, qu);
write_helper(&mut file, qw);
write_helper(&mut file, pmq);
#[allow(non_snake_case)]
let mut P: ProjectivePoint = ProjectivePoint{U:pu, W:pw};
#[allow(non_snake_case)]
let mut Q: ProjectivePoint = ProjectivePoint{U:qu, W:qw};
differential_add_and_double(&mut P, &mut Q, &pmq);
write_helper(&mut file, &(&P.U + &P.W) + &(&Q.U + &Q.W));
}
}
fn test_scalar_mul(mut file: &mut File) {
use montgomery::ProjectivePoint;
// test cswap. three input registers: (r0, r1) to swap, (r2) to control swap, one output register (r31).
let num_src_regs = 7;
let reg_window = 0;
let num_tests = 1;
let loading_address = 0; // microcode loading address
let mcode = assemble_engine25519!(
start:
psa %25, %0 // x0.U
psa %26, %1 // x0.W
psa %27, %2 // x1.U
psa %28, %3 // x1.W
psa %24, %4 // affine point
psa %31, %5 // scalar
psa %19, %6 // the number 254
// P.U in %20
// P.W in %21
// Q.U in %22
// Q.W in %23
// affine_PmQ in %24
// %30 is the TRD scratch register and cswap dummy
// %29 is the subtraction temporary value register and k_t
// x0.U in %25
// x0.W in %26
// x1.U in %27
// x1.W in %28
// %19 is the loop counter, starts with 254 (if 0, loop runs exactly once)
// %31 is the scalar
// %18 is the swap variable
psa %18, #0
// for i in (0..255).rev()
mainloop:
// let choice: u8 = (bits[i + 1] ^ bits[i]) as u8;
// ProjectivePoint::conditional_swap(&mut x0, &mut x1, choice.into());
xbt %29, %31 // orignally[k_t = (k>>t) & 1] now[k_t = k[254]]
shl %31, %31 // k = k<<1
xor %18, %18, %29 // swap ^= k_t
// cswap x0.U (%25), x1.U (%27)
xor %30, %25, %27
msk %30, %18, %30
xor %25, %30, %25
xor %27, %30, %27
// cswap x0.W (%26), x1.W (%28)
xor %30, %26, %28
msk %30, %18, %30
xor %26, %30, %26
xor %28, %30, %28
psa %18, %29 // swap = k_t
// differential_add_and_double(&mut x0, &mut x1, &affine_u);
psa %20, %25
psa %21, %26
psa %22, %27
psa %23, %28
// affine_u is already in %24
// let t0 = &P.U + &P.W;
add %0, %20, %21
trd %30, %0
sub %0, %0, %30
// let t1 = &P.U - &P.W;
sub %21, #3, %21 // negate &P.W using #FIELDPRIME (#3)
add %1, %20, %21
trd %30, %1
sub %1, %1, %30
// let t2 = &Q.U + &Q.W;
add %2, %22, %23
trd %30, %2
sub %2, %2, %30
// let t3 = &Q.U - &Q.W;
sub %23, #3, %23
add %3, %22, %23
trd %30, %3
sub %3, %3, %30
// let t4 = t0.square(); // (U_P + W_P)^2 = U_P^2 + 2 U_P W_P + W_P^2
mul %4, %0, %0
// let t5 = t1.square(); // (U_P - W_P)^2 = U_P^2 - 2 U_P W_P + W_P^2
mul %5, %1, %1
// let t6 = &t4 - &t5; // 4 U_P W_P
sub %29, #3, %5
add %6, %4, %29
trd %30, %6
sub %6, %6, %30
// let t7 = &t0 * &t3; // (U_P + W_P) (U_Q - W_Q) = U_P U_Q + W_P U_Q - U_P W_Q - W_P W_Q
mul %7, %0, %3
// let t8 = &t1 * &t2; // (U_P - W_P) (U_Q + W_Q) = U_P U_Q - W_P U_Q + U_P W_Q - W_P W_Q
mul %8, %1, %2
// let t9 = &t7 + &t8; // 2 (U_P U_Q - W_P W_Q)
add %9, %7, %8
trd %30, %9
sub %9, %9, %30
// let t10 = &t7 - &t8; // 2 (W_P U_Q - U_P W_Q)
sub %29, #3, %8
add %10, %7, %29
trd %30, %10
sub %10, %10, %30
// let t11 = t9.square(); // 4 (U_P U_Q - W_P W_Q)^2
mul %11, %9, %9
// let t12 = t10.square(); // 4 (W_P U_Q - U_P W_Q)^2
mul %12, %10, %10
// let t13 = &APLUS2_OVER_FOUR * &t6; // (A + 2) U_P U_Q
mul %13, #4, %6 // #4 is A+2/4
// let t14 = &t4 * &t5; // ((U_P + W_P)(U_P - W_P))^2 = (U_P^2 - W_P^2)^2
mul %14, %4, %5
// let t15 = &t13 + &t5; // (U_P - W_P)^2 + (A + 2) U_P W_P
add %15, %13, %5
trd %30, %15
sub %15, %15, %30
// let t16 = &t6 * &t15; // 4 (U_P W_P) ((U_P - W_P)^2 + (A + 2) U_P W_P)
mul %16, %6, %15
// let t17 = affine_PmQ * &t12; // U_D * 4 (W_P U_Q - U_P W_Q)^2
mul %17, %24, %12 // affine_PmQ loaded into %24
///// these can be eliminated down the road, but included for 1:1 algorithm correspodence to reference in early testing
// P.U = t14; // U_{P'} = (U_P + W_P)^2 (U_P - W_P)^2
psa %20, %14
// P.W = t16; // W_{P'} = (4 U_P W_P) ((U_P - W_P)^2 + ((A + 2)/4) 4 U_P W_P)
psa %21, %16
// let t18 = t11; // W_D * 4 (U_P U_Q - W_P W_Q)^2
// Q.U = t18; // U_{Q'} = W_D * 4 (U_P U_Q - W_P W_Q)^2
psa %22, %11 // collapsed two to save a register
// Q.W = t17; // W_{Q'} = U_D * 4 (W_P U_Q - U_P W_Q)^2
psa %23, %17
///// 'return' arguments for next iteration, can be optimized out later
psa %25, %20
psa %26, %21
psa %27, %22
psa %28, %23
brz end, %19 // if loop counter is 0, quit
sub %19, %19, #1 // subtract one from the loop counter and run again
brz mainloop, #0 // go back to the top
end:
// ProjectivePoint::conditional_swap(&mut x0, &mut x1, Choice::from(bits[0] as u8));
// cswap x0.U (%25), x1.U (%27)
xor %30, %25, %27
msk %30, %18, %30
xor %25, %30, %25
xor %27, %30, %27
// cswap x0.W (%26), x1.W (%28)
xor %30, %26, %28
msk %30, %18, %30
xor %26, %30, %26
xor %28, %30, %28
//// test post-amble
// test postamble -- sum together the points to create a single composite test output
add %31, %25, %26
trd %30, %31
sub %31, %31, %30
fin // finish execution
);
write_test_header(&mut file, loading_address, &mcode, num_src_regs, reg_window, num_tests);
use scalar::Scalar;
use montgomery::MontgomeryPoint;
use montgomery::differential_add_and_double;
fn clamp_scalar(mut scalar: [u8; 32]) -> Scalar {
scalar[0] &= 248;
scalar[31] &= 127;
scalar[31] |= 64;
Scalar::from_bits(scalar)
}
let scalar: Scalar = clamp_scalar(rand::thread_rng().gen::<[u8; 32]>());
let mp: MontgomeryPoint = MontgomeryPoint {0: rand::thread_rng().gen::<[u8; 32]>() };
// Algorithm 8 of Costello-Smith 2017
let affine_u = FieldElement::from_bytes(&mp.0);
let mut x0 = ProjectivePoint {
U: FieldElement::one(),
W: FieldElement::zero(),
};
let mut x1 = ProjectivePoint {
U: affine_u,
W: FieldElement::one(),
};
// test vectors input to test routine
write_helper(&mut file, x0.U);
write_helper(&mut file, x0.W);
write_helper(&mut file, x1.U);
write_helper(&mut file, x1.W);
write_helper(&mut file, affine_u);
file.write(&scalar.bytes).unwrap();
let number254 = FieldElement::from_bytes(&[
254, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
write_helper(&mut file, number254);
let bits: [i8; 256] = scalar.bits();
for i in (0..255).rev() {
let choice: u8 = (bits[i + 1] ^ bits[i]) as u8;
debug_assert!(choice == 0 || choice == 1);
ProjectivePoint::conditional_swap(&mut x0, &mut x1, choice.into());
differential_add_and_double(&mut x0, &mut x1, &affine_u);
}
ProjectivePoint::conditional_swap(&mut x0, &mut x1, Choice::from(bits[0] as u8));
// result is in x0
// result vector
write_helper(&mut file, &x0.U + &x0.W);
}
test_scalar_mul(&mut file);
test_add(&mut file);
test_loop(&mut file);
test_cswap(&mut file);
test_mul(&mut file);
test_diff_add_and_double(&mut file);
// end sequence
let _ = file.write(&(0xFFFF_FFFF as u32).to_le_bytes());
}
}