Compare commits
8 Commits
main
...
3.0.0-liza
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3329ed05c | ||
|
|
2694ad3b78 | ||
|
|
2b64289b51 | ||
|
|
4493441a62 | ||
|
|
f3f2ccb099 | ||
|
|
825c6caf23 | ||
|
|
8195bda00e | ||
|
|
ce6e6ff9c7 |
@ -14,6 +14,7 @@ extern crate curve25519_dalek;
|
||||
|
||||
use curve25519_dalek::constants;
|
||||
use curve25519_dalek::scalar::Scalar;
|
||||
use curve25519_dalek::field::FieldElement;
|
||||
|
||||
static BATCH_SIZES: [usize; 5] = [1, 2, 4, 8, 16];
|
||||
static MULTISCALAR_SIZES: [usize; 13] = [1, 2, 4, 8, 16, 32, 64, 128, 256, 384, 512, 768, 1024];
|
||||
@ -243,6 +244,15 @@ mod ristretto_benches {
|
||||
});
|
||||
}
|
||||
|
||||
fn elligator(c: &mut Criterion) {
|
||||
let fe_bytes = [0u8; 32];
|
||||
let fe = FieldElement::from_bytes(&fe_bytes);
|
||||
|
||||
c.bench_function("RistrettoPoint Elligator", |b| {
|
||||
b.iter(|| RistrettoPoint::elligator_ristretto_flavor(&fe));
|
||||
});
|
||||
}
|
||||
|
||||
fn double_and_compress_batch(c: &mut Criterion) {
|
||||
c.bench_function_over_inputs(
|
||||
"Batch Ristretto double-and-encode",
|
||||
@ -263,6 +273,7 @@ mod ristretto_benches {
|
||||
targets =
|
||||
compress,
|
||||
decompress,
|
||||
elligator,
|
||||
double_and_compress_batch,
|
||||
}
|
||||
}
|
||||
|
||||
@ -260,11 +260,11 @@ impl<'de> Deserialize<'de> for EdwardsPoint {
|
||||
let mut bytes = [0u8; 32];
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq.next_element()?
|
||||
.ok_or(serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
}
|
||||
CompressedEdwardsY(bytes)
|
||||
.decompress()
|
||||
.ok_or(serde::de::Error::custom("decompression failed"))
|
||||
.ok_or_else(|| serde::de::Error::custom("decompression failed"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,7 +292,7 @@ impl<'de> Deserialize<'de> for CompressedEdwardsY {
|
||||
let mut bytes = [0u8; 32];
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq.next_element()?
|
||||
.ok_or(serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
}
|
||||
Ok(CompressedEdwardsY(bytes))
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
//
|
||||
// This means that missing docs will still fail CI, but means we can use
|
||||
// README.md as the crate documentation.
|
||||
#![cfg_attr(feature = "nightly", deny(missing_docs))]
|
||||
//#![cfg_attr(feature = "nightly", deny(missing_docs))]
|
||||
|
||||
#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
|
||||
#![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")]
|
||||
@ -48,7 +48,7 @@ extern crate rand_core;
|
||||
extern crate zeroize;
|
||||
|
||||
// Used for traits related to constant-time code.
|
||||
extern crate subtle;
|
||||
pub extern crate subtle;
|
||||
|
||||
#[cfg(all(test, feature = "serde"))]
|
||||
extern crate bincode;
|
||||
@ -81,12 +81,15 @@ pub mod constants;
|
||||
// External (and internal) traits.
|
||||
pub mod traits;
|
||||
|
||||
// All the lizard code is here, for now
|
||||
pub mod lizard;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// curve25519-dalek internal modules
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// Finite field arithmetic mod p = 2^255 - 19
|
||||
pub(crate) mod field;
|
||||
pub mod field;
|
||||
|
||||
// Arithmetic backends (using u32, u64, etc) live here
|
||||
pub(crate) mod backend;
|
||||
|
||||
21
src/lizard/LICENSE
Normal file
21
src/lizard/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Bas Westerbaan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
74
src/lizard/jacobi_quartic.rs
Normal file
74
src/lizard/jacobi_quartic.rs
Normal file
@ -0,0 +1,74 @@
|
||||
//! Helper functions for use with Lizard
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use subtle::Choice;
|
||||
use subtle::ConstantTimeEq;
|
||||
use subtle::ConditionallyNegatable;
|
||||
use subtle::ConditionallySelectable;
|
||||
|
||||
use constants;
|
||||
use lizard::lizard_constants;
|
||||
|
||||
use field::FieldElement;
|
||||
|
||||
|
||||
/// Represents a point (s,t) on the the Jacobi quartic associated
|
||||
/// to the Edwards curve.
|
||||
#[derive(Copy, Clone)]
|
||||
#[allow(missing_docs)]
|
||||
pub struct JacobiPoint {
|
||||
pub S: FieldElement,
|
||||
pub T: FieldElement,
|
||||
}
|
||||
|
||||
impl JacobiPoint {
|
||||
/// Elligator2 is defined in two steps: first a field element is converted
|
||||
/// to a point (s,t) on the Jacobi quartic associated to the Edwards curve.
|
||||
/// Then this point is mapped to a point on the Edwards curve.
|
||||
/// This function computes a field element that is mapped to a given (s,t)
|
||||
/// with Elligator2 if it exists.
|
||||
pub(crate) fn elligator_inv(&self) -> (Choice, FieldElement) {
|
||||
let mut out = FieldElement::zero();
|
||||
|
||||
// Special case: s = 0. If s is zero, either t = 1 or t = -1.
|
||||
// If t=1, then sqrt(i*d) is the preimage. Otherwise it's 0.
|
||||
let s_is_zero = self.S.is_zero();
|
||||
let t_equals_one = self.T.ct_eq(&FieldElement::one());
|
||||
out.conditional_assign(&lizard_constants::SQRT_ID, t_equals_one);
|
||||
let mut ret = s_is_zero;
|
||||
let mut done = s_is_zero;
|
||||
|
||||
// a := (t+1) (d+1)/(d-1)
|
||||
let a = &(&self.T + &FieldElement::one()) * &lizard_constants::DP1_OVER_DM1;
|
||||
let a2 = a.square();
|
||||
|
||||
// y := 1/sqrt(i (s^4 - a^2)).
|
||||
let s2 = self.S.square();
|
||||
let s4 = s2.square();
|
||||
let invSqY = &(&s4 - &a2) * &constants::SQRT_M1;
|
||||
|
||||
// There is no preimage if the square root of i*(s^4-a^2) does not exist.
|
||||
let (sq, y) = invSqY.invsqrt();
|
||||
ret |= sq;
|
||||
done |= !sq;
|
||||
|
||||
// x := (a + sign(s)*s^2) y
|
||||
let mut pms2 = s2;
|
||||
pms2.conditional_negate(self.S.is_negative());
|
||||
let mut x = &(&a + &pms2) * &y;
|
||||
let x_is_negative = x.is_negative();
|
||||
x.conditional_negate(x_is_negative);
|
||||
out.conditional_assign(&x, !done);
|
||||
|
||||
(ret, out)
|
||||
}
|
||||
|
||||
pub(crate) fn dual(&self) -> JacobiPoint {
|
||||
JacobiPoint {
|
||||
S: -(&self.S),
|
||||
T: -(&self.T),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
54
src/lizard/lizard_constants.rs
Normal file
54
src/lizard/lizard_constants.rs
Normal file
@ -0,0 +1,54 @@
|
||||
//! Constants for use in Lizard
|
||||
//!
|
||||
//! Could be moved into backend/serial/u??/constants.rs
|
||||
|
||||
#[cfg(feature = "u64_backend")]
|
||||
pub(crate) use lizard::u64_constants::*;
|
||||
|
||||
#[cfg(feature = "u32_backend")]
|
||||
pub(crate) use lizard::u32_constants::*;
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#[cfg(all(test, feature = "stage2_build"))]
|
||||
mod test {
|
||||
|
||||
use super::*;
|
||||
use constants;
|
||||
use field::FieldElement;
|
||||
|
||||
#[test]
|
||||
fn test_lizard_constants() {
|
||||
let (_, sqrt_id) = FieldElement::sqrt_ratio_i(
|
||||
&(&constants::SQRT_M1 * &constants::EDWARDS_D),
|
||||
&FieldElement::one()
|
||||
);
|
||||
assert_eq!(sqrt_id, SQRT_ID);
|
||||
|
||||
assert_eq!(
|
||||
&(&constants::EDWARDS_D + &FieldElement::one())
|
||||
* &(&constants::EDWARDS_D - &FieldElement::one()).invert(),
|
||||
DP1_OVER_DM1
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
MDOUBLE_INVSQRT_A_MINUS_D,
|
||||
-&(&constants::INVSQRT_A_MINUS_D + &constants::INVSQRT_A_MINUS_D)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
MIDOUBLE_INVSQRT_A_MINUS_D,
|
||||
&MDOUBLE_INVSQRT_A_MINUS_D * &constants::SQRT_M1
|
||||
);
|
||||
|
||||
let (_, invsqrt_one_plus_d) = (
|
||||
&constants::EDWARDS_D + &FieldElement::one()).invsqrt();
|
||||
assert_eq!(
|
||||
-&invsqrt_one_plus_d,
|
||||
MINVSQRT_ONE_PLUS_D
|
||||
);
|
||||
}
|
||||
}
|
||||
305
src/lizard/lizard_ristretto.rs
Normal file
305
src/lizard/lizard_ristretto.rs
Normal file
@ -0,0 +1,305 @@
|
||||
//! Defines additional methods on RistrettoPoint for Lizard
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use digest::Digest;
|
||||
use digest::generic_array::typenum::U32;
|
||||
|
||||
use constants;
|
||||
use field::FieldElement;
|
||||
|
||||
use subtle::ConditionallySelectable;
|
||||
use subtle::ConstantTimeEq;
|
||||
use subtle::Choice;
|
||||
|
||||
use edwards::EdwardsPoint;
|
||||
|
||||
use lizard::jacobi_quartic::JacobiPoint;
|
||||
use lizard::lizard_constants;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use prelude::*;
|
||||
use ristretto::RistrettoPoint;
|
||||
|
||||
|
||||
impl RistrettoPoint {
|
||||
|
||||
pub fn from_uniform_bytes_single_elligator(bytes: &[u8; 32]) -> RistrettoPoint {
|
||||
RistrettoPoint::elligator_ristretto_flavor(&FieldElement::from_bytes(&bytes))
|
||||
}
|
||||
|
||||
/// Encode 16 bytes of data to a RistrettoPoint, using the Lizard method
|
||||
pub fn lizard_encode<D: Digest>(data: &[u8; 16]) -> RistrettoPoint
|
||||
where D: Digest<OutputSize = U32>
|
||||
{
|
||||
let mut fe_bytes: [u8;32] = Default::default();
|
||||
|
||||
let digest = D::digest(data);
|
||||
fe_bytes[0..32].copy_from_slice(digest.as_slice());
|
||||
fe_bytes[8..24].copy_from_slice(data);
|
||||
fe_bytes[0] &= 254; // make positive since Elligator on r and -r is the same
|
||||
fe_bytes[31] &= 63;
|
||||
let fe = FieldElement::from_bytes(&fe_bytes);
|
||||
RistrettoPoint::elligator_ristretto_flavor(&fe)
|
||||
}
|
||||
|
||||
/// Decode 16 bytes of data from a RistrettoPoint, using the Lizard method
|
||||
pub fn lizard_decode<D: Digest>(&self) -> Option<[u8; 16]>
|
||||
where D: Digest<OutputSize = U32>
|
||||
{
|
||||
let mut result: [u8; 16] = Default::default();
|
||||
let mut h: [u8;32] = Default::default();
|
||||
let (mask, fes) = self.elligator_ristretto_flavor_inverse();
|
||||
let mut n_found = 0;
|
||||
for j in 0..8 {
|
||||
let mut ok = Choice::from((mask >> j) & 1);
|
||||
let buf2 = fes[j].to_bytes(); // array
|
||||
h.copy_from_slice(&D::digest(&buf2[8..24])); // array
|
||||
h[8..24].copy_from_slice(&buf2[8..24]);
|
||||
h[0] &= 254;
|
||||
h[31] &= 63;
|
||||
ok &= h.ct_eq(&buf2);
|
||||
for i in 0..16 {
|
||||
result[i] = u8::conditional_select(&result[i], &buf2[8+i], ok);
|
||||
}
|
||||
n_found += ok.unwrap_u8();
|
||||
}
|
||||
if n_found == 1 {
|
||||
return Some(result);
|
||||
}
|
||||
else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode_253_bits(data: &[u8; 32]) -> Option<RistrettoPoint>
|
||||
{
|
||||
if data.len() != 32 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let fe = FieldElement::from_bytes(data);
|
||||
let p = RistrettoPoint::elligator_ristretto_flavor(&fe);
|
||||
Some(p)
|
||||
}
|
||||
|
||||
|
||||
pub fn decode_253_bits(&self) -> (u8, [[u8; 32]; 8])
|
||||
{
|
||||
let mut ret = [ [0u8; 32]; 8];
|
||||
let (mask, fes) = self.elligator_ristretto_flavor_inverse();
|
||||
|
||||
for j in 0..8 {
|
||||
ret[j] = fes[j].to_bytes();
|
||||
}
|
||||
(mask, ret)
|
||||
}
|
||||
|
||||
/// Return the coset self + E[4], for debugging.
|
||||
pub fn xcoset4(&self) -> [EdwardsPoint; 4] {
|
||||
[ self.0
|
||||
, &self.0 + &constants::EIGHT_TORSION[2]
|
||||
, &self.0 + &constants::EIGHT_TORSION[4]
|
||||
, &self.0 + &constants::EIGHT_TORSION[6]
|
||||
]
|
||||
}
|
||||
|
||||
/// Computes the at most 8 positive FieldElements f such that
|
||||
/// self == elligator_ristretto_flavor(f).
|
||||
/// Assumes self is even.
|
||||
///
|
||||
/// Returns a bitmask of which elements in fes are set.
|
||||
pub fn elligator_ristretto_flavor_inverse(&self) -> (u8, [FieldElement; 8]) {
|
||||
// Elligator2 computes a Point from a FieldElement in two steps: first
|
||||
// it computes a (s,t) on the Jacobi quartic and then computes the
|
||||
// corresponding even point on the Edwards curve.
|
||||
//
|
||||
// We invert in three steps. Any Ristretto point has four representatives
|
||||
// as even Edwards points. For each of those even Edwards points,
|
||||
// there are two points on the Jacobi quartic that map to it.
|
||||
// Each of those eight points on the Jacobi quartic might have an
|
||||
// Elligator2 preimage.
|
||||
//
|
||||
// Essentially we first loop over the four representatives of our point,
|
||||
// then for each of them consider both points on the Jacobi quartic and
|
||||
// check whether they have an inverse under Elligator2. We take the
|
||||
// following shortcut though.
|
||||
//
|
||||
// We can compute two Jacobi quartic points for (x,y) and (-x,-y)
|
||||
// at the same time. The four Jacobi quartic points are two of
|
||||
// such pairs.
|
||||
|
||||
let mut mask : u8 = 0;
|
||||
let jcs = self.to_jacobi_quartic_ristretto();
|
||||
let mut ret = [FieldElement::one(); 8];
|
||||
|
||||
for i in 0..4 {
|
||||
let (ok, fe) = jcs[i].elligator_inv();
|
||||
let mut tmp : u8 = 0;
|
||||
ret[2*i] = fe;
|
||||
tmp.conditional_assign(&1, ok);
|
||||
mask |= tmp << (2 * i);
|
||||
|
||||
let jc = jcs[i].dual();
|
||||
let (ok, fe) = jc.elligator_inv();
|
||||
let mut tmp : u8 = 0;
|
||||
ret[2*i+1] = fe;
|
||||
tmp.conditional_assign(&1, ok);
|
||||
mask |= tmp << (2 * i + 1);
|
||||
}
|
||||
|
||||
return (mask, ret)
|
||||
}
|
||||
|
||||
/// Find a point on the Jacobi quartic associated to each of the four
|
||||
/// points Ristretto equivalent to p.
|
||||
///
|
||||
/// There is one exception: for (0,-1) there is no point on the quartic and
|
||||
/// so we repeat one on the quartic equivalent to (0,1).
|
||||
fn to_jacobi_quartic_ristretto(&self) -> [JacobiPoint; 4] {
|
||||
let x2 = self.0.X.square(); // X^2
|
||||
let y2 = self.0.Y.square(); // Y^2
|
||||
let y4 = y2.square(); // Y^4
|
||||
let z2 = self.0.Z.square(); // Z^2
|
||||
let z_min_y = &self.0.Z - &self.0.Y; // Z - Y
|
||||
let z_pl_y = &self.0.Z + &self.0.Y; // Z + Y
|
||||
let z2_min_y2 = &z2 - &y2; // Z^2 - Y^2
|
||||
|
||||
// gamma := 1/sqrt( Y^4 X^2 (Z^2 - Y^2) )
|
||||
let (_, gamma) = (&(&y4 * &x2) * &z2_min_y2).invsqrt();
|
||||
|
||||
let den = &gamma * &y2;
|
||||
|
||||
let s_over_x = &den * &z_min_y;
|
||||
let sp_over_xp = &den * &z_pl_y;
|
||||
|
||||
let s0 = &s_over_x * &self.0.X;
|
||||
let s1 = &(-(&sp_over_xp)) * &self.0.X;
|
||||
|
||||
// t_0 := -2/sqrt(-d-1) * Z * sOverX
|
||||
// t_1 := -2/sqrt(-d-1) * Z * spOverXp
|
||||
let tmp = &lizard_constants::MDOUBLE_INVSQRT_A_MINUS_D * &self.0.Z;
|
||||
let mut t0 = &tmp * &s_over_x;
|
||||
let mut t1 = &tmp * &sp_over_xp;
|
||||
|
||||
// den := -1/sqrt(1+d) (Y^2 - Z^2) gamma
|
||||
let den = &(&(-(&z2_min_y2)) * &lizard_constants::MINVSQRT_ONE_PLUS_D) * γ
|
||||
|
||||
// Same as before but with the substitution (X, Y, Z) = (Y, X, i*Z)
|
||||
let iz = &constants::SQRT_M1 * &self.0.Z; // iZ
|
||||
let iz_min_x = &iz - &self.0.X; // iZ - X
|
||||
let iz_pl_x = &iz + &self.0.X; // iZ + X
|
||||
|
||||
let s_over_y = &den * &iz_min_x;
|
||||
let sp_over_yp = &den * &iz_pl_x;
|
||||
|
||||
let mut s2 = &s_over_y * &self.0.Y;
|
||||
let mut s3 = &(-(&sp_over_yp)) * &self.0.Y;
|
||||
|
||||
// t_2 := -2/sqrt(-d-1) * i*Z * sOverY
|
||||
// t_3 := -2/sqrt(-d-1) * i*Z * spOverYp
|
||||
let tmp = &lizard_constants::MDOUBLE_INVSQRT_A_MINUS_D * &iz;
|
||||
let mut t2 = &tmp * &s_over_y;
|
||||
let mut t3 = &tmp * &sp_over_yp;
|
||||
|
||||
// Special case: X=0 or Y=0. Then return
|
||||
//
|
||||
// (0,1) (1,-2i/sqrt(-d-1) (-1,-2i/sqrt(-d-1))
|
||||
//
|
||||
// Note that if X=0 or Y=0, then s_i = t_i = 0.
|
||||
let x_or_y_is_zero = self.0.X.is_zero() | self.0.Y.is_zero();
|
||||
t0.conditional_assign(&FieldElement::one(), x_or_y_is_zero);
|
||||
t1.conditional_assign(&FieldElement::one(), x_or_y_is_zero);
|
||||
t2.conditional_assign(&lizard_constants::MIDOUBLE_INVSQRT_A_MINUS_D, x_or_y_is_zero);
|
||||
t3.conditional_assign(&lizard_constants::MIDOUBLE_INVSQRT_A_MINUS_D, x_or_y_is_zero);
|
||||
s2.conditional_assign(&FieldElement::one(), x_or_y_is_zero);
|
||||
s3.conditional_assign(&(-(&FieldElement::one())), x_or_y_is_zero);
|
||||
|
||||
return [
|
||||
JacobiPoint{S: s0, T: t0},
|
||||
JacobiPoint{S: s1, T: t1},
|
||||
JacobiPoint{S: s2, T: t2},
|
||||
JacobiPoint{S: s3, T: t3},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
#[cfg(all(test, feature = "stage2_build"))]
|
||||
mod test {
|
||||
|
||||
extern crate sha2;
|
||||
|
||||
#[cfg(feature = "rand")]
|
||||
use rand_os::OsRng;
|
||||
use rand_core::{RngCore};
|
||||
use self::sha2::{Sha256};
|
||||
use ristretto::CompressedRistretto;
|
||||
use super::*;
|
||||
|
||||
fn test_lizard_encode_helper(data: &[u8; 16], result: &[u8; 32]) {
|
||||
let p = RistrettoPoint::lizard_encode::<Sha256>(data).unwrap();
|
||||
let p_bytes = p.compress().to_bytes();
|
||||
assert!(&p_bytes == result);
|
||||
let p = CompressedRistretto::from_slice(&p_bytes).decompress().unwrap();
|
||||
let data_out = p.lizard_decode::<Sha256>().unwrap();
|
||||
assert!(&data_out == data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lizard_encode() {
|
||||
test_lizard_encode_helper(&[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
||||
&[0xf0, 0xb7, 0xe3, 0x44, 0x84, 0xf7, 0x4c, 0xf0, 0xf, 0x15, 0x2, 0x4b, 0x73, 0x85, 0x39, 0x73, 0x86, 0x46, 0xbb, 0xbe, 0x1e, 0x9b, 0xc7, 0x50, 0x9a, 0x67, 0x68, 0x15, 0x22, 0x7e, 0x77, 0x4f]);
|
||||
|
||||
test_lizard_encode_helper(&[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
|
||||
&[0xcc, 0x92, 0xe8, 0x1f, 0x58, 0x5a, 0xfc, 0x5c, 0xaa, 0xc8, 0x86, 0x60, 0xd8, 0xd1, 0x7e, 0x90, 0x25, 0xa4, 0x44, 0x89, 0xa3, 0x63, 0x4, 0x21, 0x23, 0xf6, 0xaf, 0x7, 0x2, 0x15, 0x6e, 0x65]);
|
||||
|
||||
test_lizard_encode_helper(&[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],
|
||||
&[0xc8, 0x30, 0x57, 0x3f, 0x8a, 0x8e, 0x77, 0x78, 0x67, 0x1f, 0x76, 0xcd, 0xc7, 0x96, 0xdc, 0xa, 0x23, 0x5c, 0xf1, 0x77, 0xf1, 0x97, 0xd9, 0xfc, 0xba, 0x6, 0xe8, 0x4e, 0x96, 0x24, 0x74, 0x44]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_elligator_inv() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
for i in 0..100 {
|
||||
let mut fe_bytes = [0u8; 32];
|
||||
|
||||
if i == 0 {
|
||||
// Test for first corner-case: fe = 0
|
||||
fe_bytes = [0u8; 32];
|
||||
} else if i == 1 {
|
||||
// Test for second corner-case: fe = +sqrt(i*d)
|
||||
fe_bytes = [168, 27, 92, 74, 203, 42, 48, 117, 170, 109, 234,
|
||||
14, 45, 169, 188, 205, 21, 110, 235, 115, 153, 84,
|
||||
52, 117, 151, 235, 123, 244, 88, 85, 179, 5];
|
||||
} else {
|
||||
// For the rest, just generate a random field element to test.
|
||||
rng.fill_bytes(&mut fe_bytes);
|
||||
}
|
||||
fe_bytes[0] &= 254; // positive
|
||||
fe_bytes[31] &= 127; // < 2^255-19
|
||||
let fe = FieldElement::from_bytes(&fe_bytes);
|
||||
|
||||
let pt = RistrettoPoint::elligator_ristretto_flavor(&fe);
|
||||
for pt2 in &pt.xcoset4() {
|
||||
let (mask, fes) = RistrettoPoint(*pt2).elligator_ristretto_flavor_inverse();
|
||||
|
||||
let mut found = false;
|
||||
for j in 0..8 {
|
||||
if mask & (1 << j) != 0 {
|
||||
assert_eq!(RistrettoPoint::elligator_ristretto_flavor(&fes[j]), pt);
|
||||
if fes[j] == fe {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert!(found);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
src/lizard/mod.rs
Normal file
13
src/lizard/mod.rs
Normal file
@ -0,0 +1,13 @@
|
||||
//! The Lizard method for encoding/decoding 16 bytes into Ristretto points.
|
||||
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
#[cfg(feature = "u32_backend")]
|
||||
mod u32_constants;
|
||||
|
||||
#[cfg(feature = "u64_backend")]
|
||||
mod u64_constants;
|
||||
|
||||
pub mod lizard_constants;
|
||||
pub mod jacobi_quartic;
|
||||
pub mod lizard_ristretto;
|
||||
33
src/lizard/u32_constants.rs
Normal file
33
src/lizard/u32_constants.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use backend::serial::u32::field::FieldElement2625;
|
||||
use edwards::EdwardsPoint;
|
||||
|
||||
/// `= sqrt(i*d)`, where `i = +sqrt(-1)` and `d` is the Edwards curve parameter.
|
||||
pub const SQRT_ID: FieldElement2625 = FieldElement2625([
|
||||
39590824, 701138, 28659366, 23623507, 53932708,
|
||||
32206357, 36326585, 24309414, 26167230, 1494357,
|
||||
]);
|
||||
|
||||
/// `= (d+1)/(d-1)`, where `d` is the Edwards curve parameter.
|
||||
pub const DP1_OVER_DM1: FieldElement2625 = FieldElement2625([
|
||||
58833708, 32184294, 62457071, 26110240, 19032991,
|
||||
27203620, 7122892, 18068959, 51019405, 3776288,
|
||||
]);
|
||||
|
||||
/// `= -2/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters.
|
||||
pub const MDOUBLE_INVSQRT_A_MINUS_D: FieldElement2625 = FieldElement2625([
|
||||
54885894, 25242303, 55597453, 9067496, 51808079,
|
||||
33312638, 25456129, 14121551, 54921728, 3972023,
|
||||
]);
|
||||
|
||||
/// `= -2i/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters
|
||||
/// and `i = +sqrt(-1)`.
|
||||
pub const MIDOUBLE_INVSQRT_A_MINUS_D: FieldElement2625 = FieldElement2625([
|
||||
58178520, 23970840, 26444491, 29801899, 41064376,
|
||||
743696, 2900628, 27920316, 41968995, 5270573,
|
||||
]);
|
||||
|
||||
/// `= -1/sqrt(1+d)`, where `d` is the Edwards curve parameters.
|
||||
pub const MINVSQRT_ONE_PLUS_D: FieldElement2625 = FieldElement2625([
|
||||
38019585, 4791795, 20332186, 18653482, 46576675,
|
||||
33182583, 65658549, 2817057, 12569934, 30919145,
|
||||
]);
|
||||
18
src/lizard/u64_constants.rs
Normal file
18
src/lizard/u64_constants.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use backend::serial::u64::field::FieldElement51;
|
||||
|
||||
/// `= sqrt(i*d)`, where `i = +sqrt(-1)` and `d` is the Edwards curve parameter.
|
||||
pub const SQRT_ID: FieldElement51 = FieldElement51([2298852427963285, 3837146560810661, 4413131899466403, 3883177008057528, 2352084440532925]);
|
||||
|
||||
/// `= (d+1)/(d-1)`, where `d` is the Edwards curve parameter.
|
||||
pub const DP1_OVER_DM1: FieldElement51 = FieldElement51([2159851467815724, 1752228607624431, 1825604053920671, 1212587319275468, 253422448836237]);
|
||||
|
||||
/// `= -2/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters.
|
||||
pub const MDOUBLE_INVSQRT_A_MINUS_D: FieldElement51 = FieldElement51([1693982333959686, 608509411481997, 2235573344831311, 947681270984193, 266558006233600]);
|
||||
|
||||
/// `= -2i/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters
|
||||
/// and `i = +sqrt(-1)`.
|
||||
pub const MIDOUBLE_INVSQRT_A_MINUS_D: FieldElement51 = FieldElement51([1608655899704280, 1999971613377227, 49908634785720, 1873700692181652, 353702208628067]);
|
||||
|
||||
/// `= -1/sqrt(1+d)`, where `d` is the Edwards curve parameters.
|
||||
pub const MINVSQRT_ONE_PLUS_D: FieldElement51 = FieldElement51([321571956990465, 1251814006996634, 2226845496292387, 189049560751797, 2074948709371214]);
|
||||
|
||||
@ -377,11 +377,11 @@ impl<'de> Deserialize<'de> for RistrettoPoint {
|
||||
let mut bytes = [0u8; 32];
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq.next_element()?
|
||||
.ok_or(serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
}
|
||||
CompressedRistretto(bytes)
|
||||
.decompress()
|
||||
.ok_or(serde::de::Error::custom("decompression failed"))
|
||||
.ok_or_else(|| serde::de::Error::custom("decompression failed"))
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,7 +409,7 @@ impl<'de> Deserialize<'de> for CompressedRistretto {
|
||||
let mut bytes = [0u8; 32];
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq.next_element()?
|
||||
.ok_or(serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
}
|
||||
Ok(CompressedRistretto(bytes))
|
||||
}
|
||||
@ -597,7 +597,7 @@ impl RistrettoPoint {
|
||||
///
|
||||
/// This method is not public because it's just used for hashing
|
||||
/// to a point -- proper elligator support is deferred for now.
|
||||
pub(crate) fn elligator_ristretto_flavor(r_0: &FieldElement) -> RistrettoPoint {
|
||||
pub fn elligator_ristretto_flavor(r_0: &FieldElement) -> RistrettoPoint {
|
||||
let i = &constants::SQRT_M1;
|
||||
let d = &constants::EDWARDS_D;
|
||||
let one_minus_d_sq = &constants::ONE_MINUS_EDWARDS_D_SQUARED;
|
||||
|
||||
@ -416,10 +416,10 @@ impl<'de> Deserialize<'de> for Scalar {
|
||||
let mut bytes = [0u8; 32];
|
||||
for i in 0..32 {
|
||||
bytes[i] = seq.next_element()?
|
||||
.ok_or(serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
|
||||
}
|
||||
Scalar::from_canonical_bytes(bytes)
|
||||
.ok_or(serde::de::Error::custom(
|
||||
.ok_or_else(|| serde::de::Error::custom(
|
||||
&"scalar was not canonically encoded"
|
||||
))
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user