mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-04 20:24:10 +00:00
First draft of decaf decompression
This commit is contained in:
parent
a415caa9fb
commit
f18ebe9430
1 changed files with 181 additions and 0 deletions
181
src/curve.rs
181
src/curve.rs
|
|
@ -80,6 +80,7 @@
|
|||
use core::fmt::Debug;
|
||||
use core::iter::Iterator;
|
||||
use core::ops::{Add, Sub, Neg, Index};
|
||||
use core::cmp::{PartialEq, Eq};
|
||||
|
||||
use constants;
|
||||
use field::FieldElement;
|
||||
|
|
@ -168,6 +169,48 @@ impl CompressedEdwardsY {
|
|||
}
|
||||
}
|
||||
|
||||
/// A point serialized using Mike Hamburg's Decaf scheme.
|
||||
///
|
||||
/// XXX think about how this API should work
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct DecafPoint(pub [u8; 32]);
|
||||
|
||||
impl DecafPoint {
|
||||
/// View this `DecafPoint` as an array of bytes.
|
||||
pub fn to_bytes(&self) -> [u8;32] {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Attempt to decompress to an `ExtendedPoint`.
|
||||
pub fn decompress(&self) -> Option<ExtendedPoint> {
|
||||
// XXX should decoding be CT ?
|
||||
// XXX should reject unless s = |s|
|
||||
// XXX need to check that xy is nonnegative and reject otherwise
|
||||
let s = FieldElement::from_bytes(&self.0);
|
||||
let ss = s.square();
|
||||
let X = &s + &s; // X = 2s
|
||||
let Z = &FieldElement::one() - &ss; // Z = 1+as^2
|
||||
let u = &(&Z * &Z) - &(&constants::d4 * &ss); // u = Z^2 - 4ds^2
|
||||
let uss = &u * &ss;
|
||||
let mut v = match uss.invsqrt() {
|
||||
Some(v) => v,
|
||||
None => return None,
|
||||
};
|
||||
// Now v = 1/sqrt(us^2) if us^2 is a nonzero square, 0 if us^2 is zero.
|
||||
let uv = &v * &u;
|
||||
if uv.is_negative_decaf() == 1u8 {
|
||||
v.negate();
|
||||
}
|
||||
let mut two_minus_Z = -&Z; two_minus_Z[0] += 2;
|
||||
let mut w = &v * &(&s * &two_minus_Z);
|
||||
w.conditional_assign(&FieldElement::one(), s.is_zero());
|
||||
let Y = &w * &Z;
|
||||
let T = &w * &X;
|
||||
|
||||
Some(ExtendedPoint{ X: X, Y: Y, Z: Z, T: T })
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Internal point representations
|
||||
// ------------------------------------------------------------------------
|
||||
|
|
@ -425,6 +468,75 @@ impl ExtendedPoint {
|
|||
self.to_projective().compress()
|
||||
}
|
||||
|
||||
/// Compress in Decaf format.
|
||||
pub fn compress_decaf(&self) -> DecafPoint {
|
||||
// Q: Do we want to encode twisted or untwisted?
|
||||
//
|
||||
// Notes:
|
||||
// Recall that the twisted Edwards curve E_{a,d} is of the form
|
||||
//
|
||||
// ax^2 + y^2 = 1 + dx^2y^2.
|
||||
//
|
||||
// Internally, we operate on the curve with a = -1, d =
|
||||
// -121665/121666, a.k.a., the twist. But maybe we would like
|
||||
// to use Decaf on the untwisted curve with a = 1, d =
|
||||
// 121665/121666. (why? interop?)
|
||||
//
|
||||
// Fix i, a square root of -1 (mod p).
|
||||
//
|
||||
// The map x -> ix is an isomorphism from E_{a,d} to E_{-a,-d}.
|
||||
// Its inverse is x -> -ix.
|
||||
// let untwisted_X = &self.X * &constants::MSQRT_M1;
|
||||
// etc.
|
||||
|
||||
// Step 0: pre-rotation, needed for Decaf with E[8] = Z/8
|
||||
|
||||
let mut X = self.X;
|
||||
let mut Y = self.Y;
|
||||
let mut XY = self.T;
|
||||
|
||||
// If y nonzero and xy nonnegative, continue.
|
||||
// Otherwise, add Q_6 = (i,0) = constants::EIGHT_TORSION[6]
|
||||
// (x,y) + Q_6 = (iy,ix)
|
||||
// (X:Y:Z:T) + Q_6 = (iY:iX:Z:-T)
|
||||
|
||||
// XXX it should be possible to avoid this inversion, but
|
||||
// let's make sure the code is correct first
|
||||
let xy = &XY * &self.Z.invert();
|
||||
let is_neg_mask = 1u8 & !(Y.is_nonzero() & xy.is_nonnegative_decaf());
|
||||
let iX = &X * &constants::SQRT_M1;
|
||||
let iY = &Y * &constants::SQRT_M1;
|
||||
X.conditional_assign(&iY, is_neg_mask);
|
||||
Y.conditional_assign(&iX, is_neg_mask);
|
||||
let minus_XY = -&XY;
|
||||
XY.conditional_assign(&minus_XY, is_neg_mask);
|
||||
|
||||
// Step 1: Compute r = 1/sqrt((a-d)(Z+Y)(Z-Y))
|
||||
let Z_plus_Y = &self.Z + &Y;
|
||||
let Z_minus_Y = &self.Z - &Y;
|
||||
let t = &constants::a_minus_d * &(&Z_plus_Y * &Z_minus_Y);
|
||||
// t should always be square (why?)
|
||||
// XXX is it safe to use option types here?
|
||||
let mut r = t.invsqrt().unwrap();
|
||||
|
||||
// Step 2: Compute u = (a-d)r
|
||||
let u = &constants::a_minus_d * &r;
|
||||
|
||||
// Step 3: Negate r if -2uZ is negative.
|
||||
let uZ = &u * &self.Z;
|
||||
let minus_r = -&r;
|
||||
let m2uZ = -&(&uZ + &uZ);
|
||||
let mask = m2uZ.is_negative_decaf();
|
||||
r.conditional_assign(&minus_r, mask);
|
||||
|
||||
// Step 4: Compute s = |u(r(aZX - dYT)+Y)/a|
|
||||
let minus_ZX = -&(&self.Z * &X);
|
||||
let dYT = &constants::d * &(&Y * &XY);
|
||||
let mut s = &u * &(&(&r * &(&minus_ZX - &dYT)) + &Y);
|
||||
s.negate();
|
||||
DecafPoint(s.abs_decaf().to_bytes())
|
||||
}
|
||||
|
||||
/// Dehomogenize to a PreComputedPoint.
|
||||
/// Mainly for testing.
|
||||
pub fn to_precomputed(&self) -> PreComputedPoint {
|
||||
|
|
@ -856,6 +968,12 @@ impl ExtendedPoint {
|
|||
// Debug traits
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
impl Debug for DecafPoint {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
write!(f, "DecafPoint: {:?}", &self.0[..])
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ExtendedPoint {
|
||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||
write!(f, "ExtendedPoint(\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n)",
|
||||
|
|
@ -898,6 +1016,8 @@ impl Debug for CachedPoint {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use test::Bencher;
|
||||
use rand::OsRng;
|
||||
|
||||
use field::FieldElement;
|
||||
use scalar::Scalar;
|
||||
use subtle::CTAssignable;
|
||||
|
|
@ -1162,6 +1282,67 @@ mod test {
|
|||
assert!(ExtendedPoint::identity().is_identity());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decaf_decompress_id() {
|
||||
let compressed_id = DecafPoint([0u8; 32]);
|
||||
let id = compressed_id.decompress().unwrap();
|
||||
// This should compress (as ed25519) to the following:
|
||||
let mut bytes = [0u8; 32]; bytes[0] = 1;
|
||||
assert_eq!(id.compress(), CompressedEdwardsY(bytes));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decaf_compress_id() {
|
||||
let id = ExtendedPoint::identity();
|
||||
assert_eq!(id.compress_decaf(), DecafPoint([0u8; 32]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decaf_basepoint_roundtrip() {
|
||||
// XXX fix up this test
|
||||
let bp = BASE_CMPRSSD.decompress().unwrap();
|
||||
let bp_decaf = bp.compress_decaf();
|
||||
let bp_recaf = bp_decaf.decompress().unwrap();
|
||||
let diff = &bp - &bp_recaf;
|
||||
let diff2 = diff.double();
|
||||
let diff4 = diff2.double();
|
||||
//println!("bp {:?}", bp);
|
||||
//println!("bp_decaf {:?}", bp_decaf);
|
||||
//println!("bp_recaf {:?}", bp_recaf);
|
||||
//println!("diff {:?}", diff.compress());
|
||||
//println!("diff2 {:?}", diff2.compress());
|
||||
//println!("diff4 {:?}", diff4.compress());
|
||||
assert_eq!(diff4.compress(), ExtendedPoint::identity().compress());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decaf_four_torsion_basepoint() {
|
||||
//println!("");
|
||||
let bp = BASE_CMPRSSD.decompress().unwrap();
|
||||
let bp_decaf = bp.compress_decaf();
|
||||
//println!("orig, {:?}", bp.compress_decaf());
|
||||
for i in (0..8).filter(|x| x % 2 == 0) {
|
||||
let Q = &bp + &constants::EIGHT_TORSION[i];
|
||||
//println!("{}, {:?}", i, Q.compress_decaf());
|
||||
assert_eq!(Q.compress_decaf(), bp_decaf);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decaf_four_torsion_random() {
|
||||
//println!("");
|
||||
let mut rng = OsRng::new().unwrap();
|
||||
let s = Scalar::random(&mut rng);
|
||||
let P = ExtendedPoint::basepoint_mult(&s);
|
||||
let P_decaf = P.compress_decaf();
|
||||
//println!("orig, {:?}", P.compress_decaf());
|
||||
for i in (0..8).filter(|x| x % 2 == 0) {
|
||||
let Q = &P + &constants::EIGHT_TORSION[i];
|
||||
//println!("{}, {:?}", i, Q.compress_decaf());
|
||||
assert_eq!(Q.compress_decaf(), P_decaf);
|
||||
}
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_basepoint_mult(b: &mut Bencher) {
|
||||
b.iter(|| ExtendedPoint::basepoint_mult(&A_SCALAR));
|
||||
|
|
|
|||
Loading…
Reference in a new issue