2017-02-20 07:08:52 +00:00
|
|
|
|
// -*- mode: rust; -*-
|
|
|
|
|
|
//
|
2017-08-15 05:09:20 +00:00
|
|
|
|
// This file is part of curve25519-dalek.
|
2018-07-05 00:24:41 +00:00
|
|
|
|
// Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence
|
2017-08-15 05:09:20 +00:00
|
|
|
|
// See LICENSE for licensing information.
|
2017-02-20 07:08:52 +00:00
|
|
|
|
//
|
|
|
|
|
|
// Authors:
|
|
|
|
|
|
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
|
|
|
|
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
|
|
|
|
|
|
2018-01-26 21:35:23 +00:00
|
|
|
|
// We allow non snake_case names because coordinates in projective space are
|
|
|
|
|
|
// traditionally denoted by the capitalisation of their respective
|
|
|
|
|
|
// counterparts in affine space. Yeah, you heard me, rustc, I'm gonna have my
|
|
|
|
|
|
// affine and projective cakes and eat both of them too.
|
|
|
|
|
|
#![allow(non_snake_case)]
|
|
|
|
|
|
|
2018-07-20 19:24:28 +00:00
|
|
|
|
//! An implementation of [Ristretto][ristretto_main], which provides a
|
|
|
|
|
|
//! prime-order group.
|
2017-07-24 03:47:04 +00:00
|
|
|
|
//!
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//! # The Ristretto Group
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Ristretto is a modification of Mike Hamburg's Decaf scheme to work
|
2018-03-27 22:38:16 +00:00
|
|
|
|
//! with cofactor-\\(8\\) curves, such as Curve25519.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! The introduction of the Decaf paper, [_Decaf:
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//! Eliminating cofactors through point
|
|
|
|
|
|
//! compression_](https://eprint.iacr.org/2015/673.pdf), notes that while
|
|
|
|
|
|
//! most cryptographic systems require a group of prime order, most
|
2018-03-27 21:58:02 +00:00
|
|
|
|
//! concrete implementations using elliptic curve groups fall short –
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//! they either provide a group of prime order, but with incomplete or
|
|
|
|
|
|
//! variable-time addition formulae (for instance, most Weierstrass
|
|
|
|
|
|
//! models), or else they provide a fast and safe implementation of a
|
|
|
|
|
|
//! group whose order is not quite a prime \\(q\\), but \\(hq\\) for a
|
|
|
|
|
|
//! small cofactor \\(h\\) (for instance, Edwards curves, which have
|
|
|
|
|
|
//! cofactor at least \\(4\\)).
|
2017-02-21 20:51:27 +00:00
|
|
|
|
//!
|
2018-03-27 21:58:02 +00:00
|
|
|
|
//! This abstraction mismatch is commonly “handled” by pushing the
|
|
|
|
|
|
//! complexity upwards, adding ad-hoc protocol modifications. But
|
|
|
|
|
|
//! these modifications require careful analysis and are a recurring
|
2018-03-27 22:38:16 +00:00
|
|
|
|
//! source of [vulnerabilities][cryptonote] and [design
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//! complications][ed25519_hkd].
|
2017-07-24 03:47:04 +00:00
|
|
|
|
//!
|
2018-03-27 22:38:16 +00:00
|
|
|
|
//! Instead, Decaf (and Ristretto) use a quotient group to implement a
|
2018-03-27 21:58:02 +00:00
|
|
|
|
//! prime-order group using a non-prime-order curve. This provides
|
|
|
|
|
|
//! the correct abstraction for cryptographic systems, while retaining
|
2018-03-27 22:38:16 +00:00
|
|
|
|
//! the speed and safety benefits of an Edwards curve.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Decaf is named “after the procedure which divides the effect of
|
|
|
|
|
|
//! coffee by \\(4\\)”. However, Curve25519 has a cofactor of
|
|
|
|
|
|
//! \\(8\\). To eliminate its cofactor, Ristretto restricts further;
|
|
|
|
|
|
//! this [additional restriction][ristretto_coffee] gives the
|
|
|
|
|
|
//! _Ristretto_ encoding.
|
|
|
|
|
|
//!
|
2018-07-20 19:24:28 +00:00
|
|
|
|
//! More details on why Ristretto is necessary can be found in the
|
|
|
|
|
|
//! [Why Ristretto?][why_ristretto] section of the Ristretto website.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Ristretto
|
2018-03-27 21:58:02 +00:00
|
|
|
|
//! points are provided in `curve25519-dalek` by the `RistrettoPoint`
|
|
|
|
|
|
//! struct.
|
2017-07-24 03:47:04 +00:00
|
|
|
|
//!
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//! ## Encoding and Decoding
|
2017-07-24 03:47:04 +00:00
|
|
|
|
//!
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//! Encoding is done by converting to and from a `CompressedRistretto`
|
|
|
|
|
|
//! struct, which is a typed wrapper around `[u8; 32]`.
|
2017-07-24 03:47:04 +00:00
|
|
|
|
//!
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//! The encoding is not batchable, but it is possible to
|
|
|
|
|
|
//! double-and-encode in a batch using
|
|
|
|
|
|
//! `RistrettoPoint::double_and_compress_batch`.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ## Equality Testing
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Testing equality of points on an Edwards curve in projective
|
|
|
|
|
|
//! coordinates requires an expensive inversion. By contrast, equality
|
|
|
|
|
|
//! checking in the Ristretto group can be done in projective
|
|
|
|
|
|
//! coordinates without requiring an inversion, so it is much faster.
|
|
|
|
|
|
//!
|
2018-02-07 00:47:30 +00:00
|
|
|
|
//! The `RistrettoPoint` struct implements the
|
|
|
|
|
|
//! `subtle::ConstantTimeEq` trait for constant-time equality
|
|
|
|
|
|
//! checking, and the Rust `Eq` trait for variable-time equality
|
|
|
|
|
|
//! checking.
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//!
|
|
|
|
|
|
//! ## Scalars
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Scalars are represented by the `Scalar` struct. Each scalar has a
|
2018-03-27 22:03:19 +00:00
|
|
|
|
//! canonical representative mod the group order. To attempt to load
|
|
|
|
|
|
//! a supposedly-canonical scalar, use
|
|
|
|
|
|
//! `Scalar::from_canonical_bytes()`. To check whether a
|
|
|
|
|
|
//! representative is canonical, use `Scalar::is_canonical()`.
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//!
|
|
|
|
|
|
//! ## Scalar Multiplication
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Scalar multiplication on Ristretto points is provided by:
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! * the `*` operator between a `Scalar` and a `RistrettoPoint`, which
|
|
|
|
|
|
//! performs constant-time variable-base scalar multiplication;
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! * the `*` operator between a `Scalar` and a
|
|
|
|
|
|
//! `RistrettoBasepointTable`, which performs constant-time fixed-base
|
|
|
|
|
|
//! scalar multiplication;
|
|
|
|
|
|
//!
|
2018-07-25 17:54:31 +00:00
|
|
|
|
//! * an implementation of the
|
|
|
|
|
|
//! [`MultiscalarMul`](../traits/trait.MultiscalarMul.html) trait for
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//! constant-time variable-base multiscalar multiplication;
|
|
|
|
|
|
//!
|
2018-07-25 17:54:31 +00:00
|
|
|
|
//! * an implementation of the
|
|
|
|
|
|
//! [`VartimeMultiscalarMul`](../traits/trait.VartimeMultiscalarMul.html)
|
|
|
|
|
|
//! trait for variable-time variable-base multiscalar multiplication;
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//!
|
|
|
|
|
|
//! ## Random Points and Hashing to Ristretto
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! The Ristretto group comes equipped with an Elligator map. This is
|
|
|
|
|
|
//! used to implement
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! * `RistrettoPoint::random()`, which generates random points from an
|
|
|
|
|
|
//! RNG;
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! * `RistrettoPoint::from_hash()` and
|
|
|
|
|
|
//! `RistrettoPoint::hash_from_bytes()`, which perform hashing to the
|
|
|
|
|
|
//! group.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! The Elligator map itself is not currently exposed.
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! ## Implementation
|
2017-07-24 03:47:04 +00:00
|
|
|
|
//!
|
|
|
|
|
|
//! The Decaf suggestion is to use a quotient group, such as \\(\mathcal
|
|
|
|
|
|
//! E / \mathcal E[4]\\) or \\(2 \mathcal E / \mathcal E[2] \\), to
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//! implement a prime-order group using a non-prime-order curve.
|
2017-07-24 03:47:04 +00:00
|
|
|
|
//!
|
|
|
|
|
|
//! This requires only changing
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! 1. the function for equality checking (so that two representatives
|
|
|
|
|
|
//! of the same coset are considered equal);
|
|
|
|
|
|
//! 2. the function for encoding (so that two representatives of the
|
|
|
|
|
|
//! same coset are encoded as identical bitstrings);
|
|
|
|
|
|
//! 3. the function for decoding (so that only the canonical encoding of
|
|
|
|
|
|
//! a coset is accepted).
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Internally, each coset is represented by a curve point; two points
|
2018-03-27 22:38:16 +00:00
|
|
|
|
//! \\( P, Q \\) may represent the same coset in the same way that two
|
|
|
|
|
|
//! points with different \\(X,Y,Z\\) coordinates may represent the
|
|
|
|
|
|
//! same point. The group operations are carried out with no overhead
|
|
|
|
|
|
//! using Edwards formulas.
|
2017-07-24 03:47:04 +00:00
|
|
|
|
//!
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//! Notes on the details of the encoding can be found in the
|
2018-07-20 19:24:28 +00:00
|
|
|
|
//! [Details][ristretto_notes] section of the Ristretto website.
|
2018-01-26 21:35:23 +00:00
|
|
|
|
//!
|
|
|
|
|
|
//! [cryptonote]:
|
|
|
|
|
|
//! https://moderncrypto.org/mail-archive/curves/2017/000898.html
|
|
|
|
|
|
//! [ed25519_hkd]:
|
|
|
|
|
|
//! https://moderncrypto.org/mail-archive/curves/2017/000858.html
|
2018-01-26 23:35:51 +00:00
|
|
|
|
//! [ristretto_coffee]:
|
|
|
|
|
|
//! https://en.wikipedia.org/wiki/Ristretto
|
2018-02-21 19:11:17 +00:00
|
|
|
|
//! [ristretto_notes]:
|
2018-07-20 19:24:28 +00:00
|
|
|
|
//! https://ristretto.group/details/index.html
|
|
|
|
|
|
//! [why_ristretto]:
|
|
|
|
|
|
//! https://ristretto.group/why_ristretto.html
|
|
|
|
|
|
//! [ristretto_main]:
|
|
|
|
|
|
//! https://ristretto.group/
|
2017-02-20 07:08:52 +00:00
|
|
|
|
|
|
|
|
|
|
use core::fmt::Debug;
|
2018-02-21 02:10:40 +00:00
|
|
|
|
use core::ops::{Add, Sub, Neg};
|
|
|
|
|
|
use core::ops::{AddAssign, SubAssign};
|
|
|
|
|
|
use core::ops::{Mul, MulAssign};
|
2018-05-02 22:41:32 +00:00
|
|
|
|
use core::iter::Sum;
|
2018-02-21 02:10:40 +00:00
|
|
|
|
use core::borrow::Borrow;
|
2017-02-20 07:08:52 +00:00
|
|
|
|
|
2018-05-14 20:24:23 +00:00
|
|
|
|
use rand::{Rng, CryptoRng};
|
2017-05-16 04:06:26 +00:00
|
|
|
|
|
|
|
|
|
|
use digest::Digest;
|
2018-03-19 22:37:38 +00:00
|
|
|
|
use generic_array::typenum::U64;
|
2017-05-16 04:06:26 +00:00
|
|
|
|
|
2017-02-20 07:08:52 +00:00
|
|
|
|
use constants;
|
|
|
|
|
|
use field::FieldElement;
|
|
|
|
|
|
|
2017-11-17 01:34:28 +00:00
|
|
|
|
use subtle::ConditionallyAssignable;
|
|
|
|
|
|
use subtle::ConditionallyNegatable;
|
2018-02-07 00:47:30 +00:00
|
|
|
|
use subtle::ConstantTimeEq;
|
|
|
|
|
|
use subtle::Choice;
|
2017-11-17 01:34:28 +00:00
|
|
|
|
|
2018-01-25 02:56:38 +00:00
|
|
|
|
use edwards::EdwardsPoint;
|
2017-08-03 05:35:12 +00:00
|
|
|
|
use edwards::EdwardsBasepointTable;
|
2017-11-17 01:34:28 +00:00
|
|
|
|
|
2018-07-20 21:43:31 +00:00
|
|
|
|
#[allow(unused_imports)]
|
|
|
|
|
|
use prelude::*;
|
|
|
|
|
|
|
2017-02-20 11:06:16 +00:00
|
|
|
|
use scalar::Scalar;
|
2017-02-20 07:08:52 +00:00
|
|
|
|
|
2017-11-17 01:34:28 +00:00
|
|
|
|
use curve_models::CompletedPoint;
|
|
|
|
|
|
|
2018-07-19 23:50:00 +00:00
|
|
|
|
use traits::Identity;
|
|
|
|
|
|
#[cfg(any(feature = "alloc", feature = "std"))]
|
|
|
|
|
|
use traits::{MultiscalarMul, VartimeMultiscalarMul};
|
2017-08-01 02:09:34 +00:00
|
|
|
|
|
2017-02-20 07:08:52 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Compressed points
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2018-01-26 01:34:57 +00:00
|
|
|
|
/// A Ristretto point, in compressed wire format.
|
2017-02-20 07:08:52 +00:00
|
|
|
|
///
|
2018-01-26 01:34:57 +00:00
|
|
|
|
/// The Ristretto encoding is canonical, so two points are equal if and
|
|
|
|
|
|
/// only if their encodings are equal.
|
2017-02-20 07:08:52 +00:00
|
|
|
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
2017-07-09 01:22:28 +00:00
|
|
|
|
pub struct CompressedRistretto(pub [u8; 32]);
|
2017-02-20 07:08:52 +00:00
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl CompressedRistretto {
|
2018-01-26 01:34:57 +00:00
|
|
|
|
/// Copy the bytes of this `CompressedRistretto`.
|
2017-11-26 03:21:49 +00:00
|
|
|
|
pub fn to_bytes(&self) -> [u8; 32] {
|
|
|
|
|
|
self.0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
/// View this `CompressedRistretto` as an array of bytes.
|
2018-01-26 01:34:57 +00:00
|
|
|
|
pub fn as_bytes(&self) -> &[u8; 32] {
|
2017-02-23 06:12:57 +00:00
|
|
|
|
&self.0
|
2017-02-20 07:08:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-09-26 02:07:15 +00:00
|
|
|
|
/// Construct a `CompressedRistretto` from a slice of bytes.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Panics
|
|
|
|
|
|
///
|
|
|
|
|
|
/// If the input `bytes` slice does not have a length of 32.
|
|
|
|
|
|
pub fn from_slice(bytes: &[u8]) -> CompressedRistretto {
|
|
|
|
|
|
let mut tmp = [0u8; 32];
|
|
|
|
|
|
|
|
|
|
|
|
tmp.copy_from_slice(bytes);
|
|
|
|
|
|
|
|
|
|
|
|
CompressedRistretto(tmp)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
/// Attempt to decompress to an `RistrettoPoint`.
|
2017-07-19 05:03:30 +00:00
|
|
|
|
///
|
2018-01-26 01:34:57 +00:00
|
|
|
|
/// # Return
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - `Some(RistrettoPoint)` if `self` was the canonical encoding of a point;
|
|
|
|
|
|
///
|
|
|
|
|
|
/// - `None` if `self` was not the canonical encoding of a point.
|
2017-07-09 01:22:28 +00:00
|
|
|
|
pub fn decompress(&self) -> Option<RistrettoPoint> {
|
2017-07-19 05:03:30 +00:00
|
|
|
|
// Step 1. Check s for validity:
|
|
|
|
|
|
// 1.a) s must be 32 bytes (we get this from the type system)
|
|
|
|
|
|
// 1.b) s < p
|
|
|
|
|
|
// 1.c) s is nonnegative
|
|
|
|
|
|
//
|
|
|
|
|
|
// Our decoding routine ignores the high bit, so the only
|
|
|
|
|
|
// possible failure for 1.b) is if someone encodes s in 0..18
|
|
|
|
|
|
// as s+p in 2^255-19..2^255-1. We can check this by
|
|
|
|
|
|
// converting back to bytes, and checking that we get the
|
|
|
|
|
|
// original input, since our encoding routine is canonical.
|
|
|
|
|
|
|
2017-02-23 06:12:57 +00:00
|
|
|
|
let s = FieldElement::from_bytes(self.as_bytes());
|
2017-07-19 05:03:30 +00:00
|
|
|
|
let s_bytes_check = s.to_bytes();
|
|
|
|
|
|
let s_encoding_is_canonical =
|
2018-02-07 00:47:30 +00:00
|
|
|
|
&s_bytes_check[..].ct_eq(self.as_bytes());
|
2017-10-03 00:08:00 +00:00
|
|
|
|
let s_is_negative = s.is_negative();
|
2017-02-21 00:58:01 +00:00
|
|
|
|
|
2018-02-07 00:47:30 +00:00
|
|
|
|
if s_encoding_is_canonical.unwrap_u8() == 0u8 || s_is_negative.unwrap_u8() == 1u8 {
|
2017-07-19 05:03:30 +00:00
|
|
|
|
return None;
|
|
|
|
|
|
}
|
2017-02-21 00:58:01 +00:00
|
|
|
|
|
2018-08-15 21:15:11 +00:00
|
|
|
|
// Step 2. Compute (X:Y:Z:T).
|
2017-07-24 03:47:04 +00:00
|
|
|
|
let one = FieldElement::one();
|
2017-02-20 07:08:52 +00:00
|
|
|
|
let ss = s.square();
|
2018-08-15 21:15:11 +00:00
|
|
|
|
let u1 = &one + &ss; // 1 - as² where a=-1
|
|
|
|
|
|
let u2 = &one - &ss; // 1 + as²
|
|
|
|
|
|
let u1_sqr = u1.square(); // (1 + as²)²
|
2017-02-23 00:24:11 +00:00
|
|
|
|
|
2018-08-15 21:15:11 +00:00
|
|
|
|
// v == ad(1-as²)² - (1+as²)² where d=-121665/121666
|
|
|
|
|
|
let v = &(&(-&constants::EDWARDS_D) * &u2.square()) - &u1_sqr;
|
2017-05-20 22:52:00 +00:00
|
|
|
|
|
2018-08-15 21:15:11 +00:00
|
|
|
|
let (ok, I) = (&v * &u1_sqr).invsqrt(); // 1/sqrt(vu1²)
|
2017-07-19 05:03:30 +00:00
|
|
|
|
|
2018-08-15 21:15:11 +00:00
|
|
|
|
let Dx = &I * &u1; // 1/sqrt(v)
|
|
|
|
|
|
let Dy = &I * &(&Dx * &v); // 1/u2
|
2017-07-19 05:03:30 +00:00
|
|
|
|
|
2018-08-15 21:15:11 +00:00
|
|
|
|
// x == | 2s/sqrt(v) | == + sqrt(4s²/(ad(1+as²)² - (1-as²)²))
|
|
|
|
|
|
let mut x = &(&s + &s) * &Dx;
|
|
|
|
|
|
let x_neg = x.is_negative();
|
|
|
|
|
|
x.conditional_negate(x_neg);
|
|
|
|
|
|
|
|
|
|
|
|
// y == (1-as²)/(1+as²)
|
|
|
|
|
|
let y = &u2 * &Dy;
|
|
|
|
|
|
|
|
|
|
|
|
// t == ((1+as²) sqrt(4s²/(ad(1+as²)² - (1-as²)²)))/(1-as²)
|
2017-07-19 05:03:30 +00:00
|
|
|
|
let t = &x * &y;
|
|
|
|
|
|
|
2018-02-07 00:47:30 +00:00
|
|
|
|
if ok.unwrap_u8() == 0u8 || t.is_negative().unwrap_u8() == 1u8 || y.is_zero().unwrap_u8() == 1u8 {
|
2017-07-19 05:03:30 +00:00
|
|
|
|
return None;
|
2017-02-21 20:33:57 +00:00
|
|
|
|
} else {
|
2018-01-25 02:56:38 +00:00
|
|
|
|
return Some(RistrettoPoint(EdwardsPoint{X: x, Y: y, Z: one, T: t}));
|
2017-02-21 20:33:57 +00:00
|
|
|
|
}
|
2017-02-20 07:08:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl Identity for CompressedRistretto {
|
|
|
|
|
|
fn identity() -> CompressedRistretto {
|
2017-10-03 00:02:12 +00:00
|
|
|
|
CompressedRistretto([0u8; 32])
|
2017-02-20 11:45:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-07-20 00:47:33 +00:00
|
|
|
|
impl Default for CompressedRistretto {
|
|
|
|
|
|
fn default() -> CompressedRistretto {
|
|
|
|
|
|
CompressedRistretto::identity()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-05-14 05:40:51 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Serde support
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
2017-07-09 01:22:28 +00:00
|
|
|
|
// Serializes to and from `RistrettoPoint` directly, doing compression
|
2017-05-14 05:40:51 +00:00
|
|
|
|
// and decompression internally. This means that users can create
|
2017-07-09 01:22:28 +00:00
|
|
|
|
// structs containing `RistrettoPoint`s and use Serde's derived
|
2017-05-14 05:40:51 +00:00
|
|
|
|
// serializers to serialize those structures.
|
|
|
|
|
|
|
2017-05-15 01:06:59 +00:00
|
|
|
|
#[cfg(feature = "serde")]
|
|
|
|
|
|
use serde::{self, Serialize, Deserialize, Serializer, Deserializer};
|
|
|
|
|
|
#[cfg(feature = "serde")]
|
2017-05-14 05:40:51 +00:00
|
|
|
|
use serde::de::Visitor;
|
|
|
|
|
|
|
2017-05-15 01:06:59 +00:00
|
|
|
|
#[cfg(feature = "serde")]
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl Serialize for RistrettoPoint {
|
2017-05-14 05:40:51 +00:00
|
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
|
|
|
where S: Serializer
|
|
|
|
|
|
{
|
|
|
|
|
|
serializer.serialize_bytes(self.compress().as_bytes())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-09-26 22:28:47 +00:00
|
|
|
|
#[cfg(feature = "serde")]
|
|
|
|
|
|
impl Serialize for CompressedRistretto {
|
|
|
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
|
|
|
where S: Serializer
|
|
|
|
|
|
{
|
|
|
|
|
|
serializer.serialize_bytes(self.as_bytes())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-05-15 01:06:59 +00:00
|
|
|
|
#[cfg(feature = "serde")]
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'de> Deserialize<'de> for RistrettoPoint {
|
2017-05-14 05:40:51 +00:00
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
|
|
|
where D: Deserializer<'de>
|
|
|
|
|
|
{
|
2017-07-09 01:22:28 +00:00
|
|
|
|
struct RistrettoPointVisitor;
|
2017-05-14 05:40:51 +00:00
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'de> Visitor<'de> for RistrettoPointVisitor {
|
|
|
|
|
|
type Value = RistrettoPoint;
|
2017-05-14 05:40:51 +00:00
|
|
|
|
|
|
|
|
|
|
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
2017-07-09 01:22:28 +00:00
|
|
|
|
formatter.write_str("a valid point in Ristretto format")
|
2017-05-14 05:40:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<RistrettoPoint, E>
|
2017-05-14 05:40:51 +00:00
|
|
|
|
where E: serde::de::Error
|
|
|
|
|
|
{
|
|
|
|
|
|
if v.len() == 32 {
|
2017-11-20 22:53:08 +00:00
|
|
|
|
let mut arr32 = [0u8; 32];
|
|
|
|
|
|
arr32[0..32].copy_from_slice(v);
|
|
|
|
|
|
CompressedRistretto(arr32)
|
2017-05-28 22:42:09 +00:00
|
|
|
|
.decompress()
|
2017-05-14 05:40:51 +00:00
|
|
|
|
.ok_or(serde::de::Error::custom("decompression failed"))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Err(serde::de::Error::invalid_length(v.len(), &self))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
deserializer.deserialize_bytes(RistrettoPointVisitor)
|
2017-05-14 05:40:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-09-26 22:28:47 +00:00
|
|
|
|
#[cfg(feature = "serde")]
|
|
|
|
|
|
impl<'de> Deserialize<'de> for CompressedRistretto {
|
|
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
|
|
|
where D: Deserializer<'de>
|
|
|
|
|
|
{
|
|
|
|
|
|
struct CompressedRistrettoVisitor;
|
|
|
|
|
|
|
|
|
|
|
|
impl<'de> Visitor<'de> for CompressedRistrettoVisitor {
|
|
|
|
|
|
type Value = CompressedRistretto;
|
|
|
|
|
|
|
|
|
|
|
|
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
|
|
|
|
|
formatter.write_str("32 bytes of data")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<CompressedRistretto, E>
|
|
|
|
|
|
where E: serde::de::Error
|
|
|
|
|
|
{
|
|
|
|
|
|
if v.len() == 32 {
|
|
|
|
|
|
let mut arr32 = [0u8; 32];
|
|
|
|
|
|
arr32[0..32].copy_from_slice(v);
|
|
|
|
|
|
Ok(CompressedRistretto(arr32))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Err(serde::de::Error::invalid_length(v.len(), &self))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
deserializer.deserialize_bytes(CompressedRistrettoVisitor)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-05-14 05:40:51 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Internal point representations
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2017-07-24 03:47:04 +00:00
|
|
|
|
/// A `RistrettoPoint` represents a point in the Ristretto group for
|
|
|
|
|
|
/// Curve25519. Ristretto, a variant of Decaf, constructs a
|
|
|
|
|
|
/// prime-order group as a quotient group of a subgroup of (the
|
|
|
|
|
|
/// Edwards form of) Curve25519.
|
2017-02-20 07:25:04 +00:00
|
|
|
|
///
|
2018-01-26 01:34:57 +00:00
|
|
|
|
/// Internally, a `RistrettoPoint` is implemented as a wrapper type
|
|
|
|
|
|
/// around `EdwardsPoint`, with custom equality, compression, and
|
|
|
|
|
|
/// decompression routines to account for the quotient. This means that
|
|
|
|
|
|
/// operations on `RistrettoPoint`s are exactly as fast as operations on
|
|
|
|
|
|
/// `EdwardsPoint`s.
|
|
|
|
|
|
///
|
2017-02-20 07:25:04 +00:00
|
|
|
|
#[derive(Copy, Clone)]
|
2018-01-25 02:56:38 +00:00
|
|
|
|
pub struct RistrettoPoint(pub(crate) EdwardsPoint);
|
2017-02-20 07:25:04 +00:00
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl RistrettoPoint {
|
2018-01-26 01:34:57 +00:00
|
|
|
|
/// Compress this point using the Ristretto encoding.
|
2017-07-09 01:22:28 +00:00
|
|
|
|
pub fn compress(&self) -> CompressedRistretto {
|
2017-02-20 07:25:04 +00:00
|
|
|
|
let mut X = self.0.X;
|
|
|
|
|
|
let mut Y = self.0.Y;
|
2017-05-21 03:14:55 +00:00
|
|
|
|
let Z = &self.0.Z;
|
2017-07-19 05:03:30 +00:00
|
|
|
|
let T = &self.0.T;
|
2017-02-20 07:08:52 +00:00
|
|
|
|
|
2017-07-19 05:03:30 +00:00
|
|
|
|
let u1 = &(Z + &Y) * &(Z - &Y);
|
|
|
|
|
|
let u2 = &X * &Y;
|
|
|
|
|
|
// Ignore return value since this is always square
|
|
|
|
|
|
let (_, invsqrt) = (&u1 * &u2.square()).invsqrt();
|
|
|
|
|
|
let i1 = &invsqrt * &u1;
|
|
|
|
|
|
let i2 = &invsqrt * &u2;
|
|
|
|
|
|
let z_inv = &i1 * &(&i2 * T);
|
|
|
|
|
|
let mut den_inv = i2;
|
2017-02-20 07:08:52 +00:00
|
|
|
|
|
|
|
|
|
|
let iX = &X * &constants::SQRT_M1;
|
|
|
|
|
|
let iY = &Y * &constants::SQRT_M1;
|
2017-10-31 00:13:25 +00:00
|
|
|
|
let ristretto_magic = &constants::INVSQRT_A_MINUS_D;
|
2017-07-19 05:03:30 +00:00
|
|
|
|
let enchanted_denominator = &i1 * ristretto_magic;
|
|
|
|
|
|
|
2017-10-03 00:08:00 +00:00
|
|
|
|
let rotate = (T * &z_inv).is_negative();
|
2017-07-19 05:03:30 +00:00
|
|
|
|
|
|
|
|
|
|
X.conditional_assign(&iY, rotate);
|
|
|
|
|
|
Y.conditional_assign(&iX, rotate);
|
|
|
|
|
|
den_inv.conditional_assign(&enchanted_denominator, rotate);
|
|
|
|
|
|
|
2017-10-03 00:08:00 +00:00
|
|
|
|
Y.conditional_negate((&X * &z_inv).is_negative());
|
2017-07-19 05:03:30 +00:00
|
|
|
|
|
|
|
|
|
|
let mut s = &den_inv * &(Z - &Y);
|
2017-10-03 00:08:00 +00:00
|
|
|
|
let s_is_negative = s.is_negative();
|
2017-07-19 05:03:30 +00:00
|
|
|
|
s.conditional_negate(s_is_negative);
|
2017-05-21 03:14:55 +00:00
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
CompressedRistretto(s.to_bytes())
|
2017-02-20 07:08:52 +00:00
|
|
|
|
}
|
2017-02-20 11:38:31 +00:00
|
|
|
|
|
2018-01-26 01:34:57 +00:00
|
|
|
|
/// Double-and-compress a batch of points. The Ristretto encoding
|
|
|
|
|
|
/// is not batchable, since it requires an inverse square root.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// However, given input points \\( P\_1, \ldots, P\_n, \\)
|
|
|
|
|
|
/// it is possible to compute the encodings of their doubles \\(
|
|
|
|
|
|
/// \mathrm{enc}( [2]P\_1), \ldots, \mathrm{enc}( [2]P\_n ) \\)
|
|
|
|
|
|
/// in a batch.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
/// # extern crate curve25519_dalek;
|
|
|
|
|
|
/// # use curve25519_dalek::ristretto::RistrettoPoint;
|
|
|
|
|
|
/// extern crate rand;
|
2018-05-14 20:24:23 +00:00
|
|
|
|
/// use rand::rngs::OsRng;
|
2018-01-26 01:34:57 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// # // Need fn main() here in comment so the doctest compiles
|
|
|
|
|
|
/// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests
|
|
|
|
|
|
/// # fn main() {
|
|
|
|
|
|
/// let mut rng = OsRng::new().unwrap();
|
2018-01-31 02:19:53 +00:00
|
|
|
|
/// let points: Vec<RistrettoPoint> =
|
2018-01-26 01:34:57 +00:00
|
|
|
|
/// (0..32).map(|_| RistrettoPoint::random(&mut rng)).collect();
|
|
|
|
|
|
///
|
|
|
|
|
|
/// let compressed = RistrettoPoint::double_and_compress_batch(&points);
|
2018-01-31 02:19:53 +00:00
|
|
|
|
///
|
2018-01-26 01:34:57 +00:00
|
|
|
|
/// for (P, P2_compressed) in points.iter().zip(compressed.iter()) {
|
|
|
|
|
|
/// assert_eq!(*P2_compressed, (P + P).compress());
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// # }
|
|
|
|
|
|
/// ```
|
2018-07-20 21:43:31 +00:00
|
|
|
|
#[cfg(feature = "alloc")]
|
2018-01-31 02:19:53 +00:00
|
|
|
|
pub fn double_and_compress_batch<'a, I>(points: I) -> Vec<CompressedRistretto>
|
2018-01-16 23:41:05 +00:00
|
|
|
|
where I: IntoIterator<Item = &'a RistrettoPoint>
|
|
|
|
|
|
{
|
|
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
|
|
|
|
struct BatchCompressState {
|
|
|
|
|
|
e: FieldElement,
|
|
|
|
|
|
f: FieldElement,
|
|
|
|
|
|
g: FieldElement,
|
|
|
|
|
|
h: FieldElement,
|
|
|
|
|
|
eg: FieldElement,
|
|
|
|
|
|
fh: FieldElement,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl BatchCompressState {
|
|
|
|
|
|
fn efgh(&self) -> FieldElement {
|
|
|
|
|
|
&self.eg * &self.fh
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl<'a> From<&'a RistrettoPoint> for BatchCompressState {
|
|
|
|
|
|
fn from(P: &'a RistrettoPoint) -> BatchCompressState {
|
|
|
|
|
|
let XX = P.0.X.square();
|
|
|
|
|
|
let YY = P.0.Y.square();
|
|
|
|
|
|
let ZZ = P.0.Z.square();
|
|
|
|
|
|
let dTT = &P.0.T.square() * &constants::EDWARDS_D;
|
|
|
|
|
|
|
|
|
|
|
|
let e = &P.0.X * &(&P.0.Y + &P.0.Y); // = 2*X*Y
|
|
|
|
|
|
let f = &ZZ + &dTT; // = Z^2 + d*T^2
|
|
|
|
|
|
let g = &YY + &XX; // = Y^2 - a*X^2
|
|
|
|
|
|
let h = &ZZ - &dTT; // = Z^2 - d*T^2
|
|
|
|
|
|
|
|
|
|
|
|
let eg = &e * &g;
|
|
|
|
|
|
let fh = &f * &h;
|
|
|
|
|
|
|
|
|
|
|
|
BatchCompressState{ e: e, f: f, g: g, h: h, eg: eg, fh: fh }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let states: Vec<BatchCompressState> = points.into_iter().map(|P| BatchCompressState::from(P)).collect();
|
|
|
|
|
|
|
|
|
|
|
|
let mut invs: Vec<FieldElement> = states.iter().map(|state| state.efgh()).collect();
|
|
|
|
|
|
|
|
|
|
|
|
FieldElement::batch_invert(&mut invs[..]);
|
|
|
|
|
|
|
|
|
|
|
|
states.iter().zip(invs.iter()).map(|(state, inv): (&BatchCompressState, &FieldElement)| {
|
|
|
|
|
|
let Zinv = &state.eg * &inv;
|
|
|
|
|
|
let Tinv = &state.fh * &inv;
|
|
|
|
|
|
|
|
|
|
|
|
let mut magic = constants::INVSQRT_A_MINUS_D;
|
|
|
|
|
|
|
|
|
|
|
|
let negcheck1 = (&state.eg * &Zinv).is_negative();
|
|
|
|
|
|
|
|
|
|
|
|
let mut e = state.e;
|
|
|
|
|
|
let mut g = state.g;
|
|
|
|
|
|
let mut h = state.h;
|
|
|
|
|
|
|
|
|
|
|
|
let minus_e = -&e;
|
|
|
|
|
|
let f_times_sqrta = &state.f * &constants::SQRT_M1;
|
|
|
|
|
|
|
|
|
|
|
|
e.conditional_assign(&state.g, negcheck1);
|
|
|
|
|
|
g.conditional_assign(&minus_e, negcheck1);
|
|
|
|
|
|
h.conditional_assign(&f_times_sqrta, negcheck1);
|
|
|
|
|
|
|
|
|
|
|
|
magic.conditional_assign(&constants::SQRT_M1, negcheck1);
|
|
|
|
|
|
|
|
|
|
|
|
let negcheck2 = (&(&h * &e) * &Zinv).is_negative();
|
|
|
|
|
|
|
|
|
|
|
|
g.conditional_negate(negcheck2);
|
2018-01-31 02:19:53 +00:00
|
|
|
|
|
2018-01-16 23:41:05 +00:00
|
|
|
|
let mut s = &(&h - &g) * &(&magic * &(&g * &Tinv));
|
|
|
|
|
|
|
|
|
|
|
|
let s_is_negative = s.is_negative();
|
|
|
|
|
|
s.conditional_negate(s_is_negative);
|
|
|
|
|
|
|
|
|
|
|
|
CompressedRistretto(s.to_bytes())
|
|
|
|
|
|
}).collect()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-20 11:38:31 +00:00
|
|
|
|
/// Return the coset self + E[4], for debugging.
|
2018-01-25 02:56:38 +00:00
|
|
|
|
fn coset4(&self) -> [EdwardsPoint; 4] {
|
2017-02-20 11:38:31 +00:00
|
|
|
|
[ self.0
|
|
|
|
|
|
, &self.0 + &constants::EIGHT_TORSION[2]
|
|
|
|
|
|
, &self.0 + &constants::EIGHT_TORSION[4]
|
|
|
|
|
|
, &self.0 + &constants::EIGHT_TORSION[6]
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
2017-05-16 04:06:26 +00:00
|
|
|
|
|
2017-10-03 00:02:12 +00:00
|
|
|
|
/// Computes the Ristretto Elligator map.
|
2017-05-16 04:06:26 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// # Note
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This method is not public because it's just used for hashing
|
|
|
|
|
|
/// to a point -- proper elligator support is deferred for now.
|
2018-03-19 22:37:38 +00:00
|
|
|
|
pub(crate) fn elligator_ristretto_flavor(r_0: &FieldElement) -> RistrettoPoint {
|
2017-10-31 00:13:25 +00:00
|
|
|
|
let (i, d) = (&constants::SQRT_M1, &constants::EDWARDS_D);
|
2017-10-03 00:02:12 +00:00
|
|
|
|
let one = FieldElement::one();
|
|
|
|
|
|
|
|
|
|
|
|
let r = i * &r_0.square();
|
|
|
|
|
|
|
2018-01-31 02:19:53 +00:00
|
|
|
|
// D = (dr -a)(ar-d) = -(dr+1)(r+d)
|
2017-10-03 00:02:12 +00:00
|
|
|
|
let D = -&( &(&(d * &r) + &one) * &(&r + d) );
|
|
|
|
|
|
// N = a(d-a)(d+a)(r+1) = -(r+1)(d^2 -1)
|
|
|
|
|
|
let d_sq = d.square();
|
|
|
|
|
|
let N = -&( &(&d_sq - &one) * &(&r + &one) );
|
|
|
|
|
|
|
|
|
|
|
|
let mut s = FieldElement::zero();
|
|
|
|
|
|
let mut c = -&one;
|
|
|
|
|
|
|
|
|
|
|
|
let (N_over_D_is_square, maybe_s) = FieldElement::sqrt_ratio(&N, &D);
|
|
|
|
|
|
// s = sqrt(N/D) if N/D is square
|
|
|
|
|
|
s.conditional_assign(&maybe_s, N_over_D_is_square);
|
|
|
|
|
|
|
2018-01-26 01:34:57 +00:00
|
|
|
|
// XXX how exactly do we reuse the computation of sqrt(N/D) to find sqrt(rN/D) ?
|
2017-10-03 00:02:12 +00:00
|
|
|
|
let (rN_over_D_is_square, mut maybe_s) = FieldElement::sqrt_ratio(&(&r*&N), &D);
|
|
|
|
|
|
maybe_s.negate();
|
|
|
|
|
|
|
|
|
|
|
|
// s = -sqrt(rN/D) if rN/D is square (should happen exactly when N/D is nonsquare)
|
2018-02-07 00:47:30 +00:00
|
|
|
|
debug_assert_eq!((N_over_D_is_square ^ rN_over_D_is_square).unwrap_u8(), 1u8);
|
2017-10-03 00:02:12 +00:00
|
|
|
|
s.conditional_assign(&maybe_s, rN_over_D_is_square);
|
|
|
|
|
|
c.conditional_assign(&r, rN_over_D_is_square);
|
|
|
|
|
|
|
|
|
|
|
|
// T = (c * (r - one) * (d-one).square()) - D;
|
|
|
|
|
|
let T = &(&c * &(&(&r - &one) * &((d - &one).square()))) - &D;
|
|
|
|
|
|
|
|
|
|
|
|
let s_sq = s.square();
|
2017-05-16 04:06:26 +00:00
|
|
|
|
let P = CompletedPoint{
|
2017-10-03 20:27:08 +00:00
|
|
|
|
X: &(&s + &s) * &D,
|
2017-10-31 00:13:25 +00:00
|
|
|
|
Z: &T * &constants::SQRT_AD_MINUS_ONE,
|
2017-10-03 00:02:12 +00:00
|
|
|
|
Y: &FieldElement::one() - &s_sq,
|
|
|
|
|
|
T: &FieldElement::one() + &s_sq,
|
2017-05-16 04:06:26 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Convert to extended and return.
|
2017-07-09 01:22:28 +00:00
|
|
|
|
RistrettoPoint(P.to_extended())
|
2017-05-16 04:06:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
/// Return a `RistrettoPoint` chosen uniformly at random using a user-provided RNG.
|
2017-05-16 04:06:26 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// # Inputs
|
|
|
|
|
|
///
|
|
|
|
|
|
/// * `rng`: any RNG which implements the `rand::Rng` interface.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Returns
|
|
|
|
|
|
///
|
2017-07-09 01:22:28 +00:00
|
|
|
|
/// A random element of the Ristretto group.
|
2017-05-16 04:06:26 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// # Implementation
|
|
|
|
|
|
///
|
2018-03-19 22:37:38 +00:00
|
|
|
|
/// Uses the Ristretto-flavoured Elligator 2 map, so that the
|
|
|
|
|
|
/// discrete log of the output point with respect to any other
|
|
|
|
|
|
/// point should be unknown. The map is applied twice and the
|
|
|
|
|
|
/// results are added, to ensure a uniform distribution.
|
2018-05-14 20:24:23 +00:00
|
|
|
|
pub fn random<T: Rng + CryptoRng>(rng: &mut T) -> Self {
|
2018-07-27 02:04:56 +00:00
|
|
|
|
let mut uniform_bytes = [0u8; 64];
|
|
|
|
|
|
rng.fill(&mut uniform_bytes);
|
2018-03-19 22:37:38 +00:00
|
|
|
|
|
2018-07-27 02:04:56 +00:00
|
|
|
|
RistrettoPoint::from_uniform_bytes(&uniform_bytes)
|
2017-05-16 04:06:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
/// Hash a slice of bytes into a `RistrettoPoint`.
|
2017-05-16 04:06:26 +00:00
|
|
|
|
///
|
2018-03-19 22:37:38 +00:00
|
|
|
|
/// Takes a type parameter `D`, which is any `Digest` producing 64
|
|
|
|
|
|
/// bytes of output.
|
2017-05-16 04:06:26 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// Convenience wrapper around `from_hash`.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Implementation
|
|
|
|
|
|
///
|
2018-03-19 22:37:38 +00:00
|
|
|
|
/// Uses the Ristretto-flavoured Elligator 2 map, so that the
|
|
|
|
|
|
/// discrete log of the output point with respect to any other
|
|
|
|
|
|
/// point should be unknown. The map is applied twice and the
|
|
|
|
|
|
/// results are added, to ensure a uniform distribution.
|
2017-05-16 04:06:26 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// # Example
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
/// # extern crate curve25519_dalek;
|
2017-07-09 01:22:28 +00:00
|
|
|
|
/// # use curve25519_dalek::ristretto::RistrettoPoint;
|
2017-05-16 04:06:26 +00:00
|
|
|
|
/// extern crate sha2;
|
2018-03-19 22:37:38 +00:00
|
|
|
|
/// use sha2::Sha512;
|
2017-05-16 04:06:26 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// # // Need fn main() here in comment so the doctest compiles
|
|
|
|
|
|
/// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests
|
|
|
|
|
|
/// # fn main() {
|
|
|
|
|
|
/// let msg = "To really appreciate architecture, you may even need to commit a murder";
|
2018-03-19 22:37:38 +00:00
|
|
|
|
/// let P = RistrettoPoint::hash_from_bytes::<Sha512>(msg.as_bytes());
|
2017-05-16 04:06:26 +00:00
|
|
|
|
/// # }
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
///
|
2017-07-09 01:22:28 +00:00
|
|
|
|
pub fn hash_from_bytes<D>(input: &[u8]) -> RistrettoPoint
|
2018-03-19 22:37:38 +00:00
|
|
|
|
where D: Digest<OutputSize = U64> + Default
|
2017-05-28 22:42:09 +00:00
|
|
|
|
{
|
2017-05-16 04:06:26 +00:00
|
|
|
|
let mut hash = D::default();
|
|
|
|
|
|
hash.input(input);
|
2017-07-09 01:22:28 +00:00
|
|
|
|
RistrettoPoint::from_hash(hash)
|
2017-05-16 04:06:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
/// Construct a `RistrettoPoint` from an existing `Digest` instance.
|
2017-05-16 04:06:26 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// Use this instead of `hash_from_bytes` if it is more convenient
|
|
|
|
|
|
/// to stream data into the `Digest` than to pass a single byte
|
|
|
|
|
|
/// slice.
|
2017-07-09 01:22:28 +00:00
|
|
|
|
pub fn from_hash<D>(hash: D) -> RistrettoPoint
|
2018-03-19 22:37:38 +00:00
|
|
|
|
where D: Digest<OutputSize = U64> + Default
|
2017-05-28 22:42:09 +00:00
|
|
|
|
{
|
2018-03-19 22:37:38 +00:00
|
|
|
|
// dealing with generic arrays is clumsy, until const generics land
|
|
|
|
|
|
let output = hash.result();
|
2018-07-27 02:04:56 +00:00
|
|
|
|
let mut output_bytes = [0u8; 64];
|
|
|
|
|
|
output_bytes.copy_from_slice(&output.as_slice());
|
2018-03-19 22:37:38 +00:00
|
|
|
|
|
2018-07-27 02:04:56 +00:00
|
|
|
|
RistrettoPoint::from_uniform_bytes(&output_bytes)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Construct a `RistrettoPoint` from 64 bytes of data.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// If the input bytes are uniformly distributed, the resulting
|
|
|
|
|
|
/// point will be uniformly distributed over the group, and its
|
|
|
|
|
|
/// discrete log with respect to other points should be unknown.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Implementation
|
|
|
|
|
|
///
|
|
|
|
|
|
/// This function splits the input array into two 32-byte halves,
|
|
|
|
|
|
/// takes the low 255 bits of each half mod p, applies the
|
|
|
|
|
|
/// Ristretto-flavored Elligator map to each, and adds the results.
|
|
|
|
|
|
pub fn from_uniform_bytes(bytes: &[u8; 64]) -> RistrettoPoint {
|
2018-03-19 22:37:38 +00:00
|
|
|
|
let mut r_1_bytes = [0u8; 32];
|
2018-07-27 02:04:56 +00:00
|
|
|
|
r_1_bytes.copy_from_slice(&bytes[0..32]);
|
2018-03-19 22:37:38 +00:00
|
|
|
|
let r_1 = FieldElement::from_bytes(&r_1_bytes);
|
|
|
|
|
|
let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1);
|
|
|
|
|
|
|
|
|
|
|
|
let mut r_2_bytes = [0u8; 32];
|
2018-07-27 02:04:56 +00:00
|
|
|
|
r_2_bytes.copy_from_slice(&bytes[32..64]);
|
2018-03-19 22:37:38 +00:00
|
|
|
|
let r_2 = FieldElement::from_bytes(&r_2_bytes);
|
|
|
|
|
|
let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2);
|
|
|
|
|
|
|
|
|
|
|
|
// Applying Elligator twice and adding the results ensures a
|
|
|
|
|
|
// uniform distribution.
|
|
|
|
|
|
&R_1 + &R_2
|
2017-05-16 04:06:26 +00:00
|
|
|
|
}
|
2017-02-20 07:08:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl Identity for RistrettoPoint {
|
|
|
|
|
|
fn identity() -> RistrettoPoint {
|
2018-01-25 02:56:38 +00:00
|
|
|
|
RistrettoPoint(EdwardsPoint::identity())
|
2017-02-20 11:45:49 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-07-20 00:47:33 +00:00
|
|
|
|
impl Default for RistrettoPoint {
|
|
|
|
|
|
fn default() -> RistrettoPoint {
|
|
|
|
|
|
RistrettoPoint::identity()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-20 11:14:25 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Equality
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl PartialEq for RistrettoPoint {
|
|
|
|
|
|
fn eq(&self, other: &RistrettoPoint) -> bool {
|
2018-02-07 00:47:30 +00:00
|
|
|
|
self.ct_eq(other).unwrap_u8() == 1u8
|
2017-07-24 03:45:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-07 00:47:30 +00:00
|
|
|
|
impl ConstantTimeEq for RistrettoPoint {
|
2017-07-24 03:45:54 +00:00
|
|
|
|
/// Test equality between two `RistrettoPoint`s.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Returns
|
|
|
|
|
|
///
|
2018-02-07 00:47:30 +00:00
|
|
|
|
/// * `Choice(1)` if the two `RistrettoPoint`s are equal;
|
|
|
|
|
|
/// * `Choice(0)` otherwise.
|
|
|
|
|
|
fn ct_eq(&self, other: &RistrettoPoint) -> Choice {
|
2017-07-24 03:45:54 +00:00
|
|
|
|
let X1Y2 = &self.0.X * &other.0.Y;
|
|
|
|
|
|
let Y1X2 = &self.0.Y * &other.0.X;
|
|
|
|
|
|
let X1X2 = &self.0.X * &other.0.X;
|
|
|
|
|
|
let Y1Y2 = &self.0.Y * &other.0.Y;
|
2018-01-31 02:19:53 +00:00
|
|
|
|
|
2017-07-24 03:45:54 +00:00
|
|
|
|
X1Y2.ct_eq(&Y1X2) | X1X2.ct_eq(&Y1Y2)
|
2017-02-20 11:14:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl Eq for RistrettoPoint {}
|
2017-02-20 11:14:25 +00:00
|
|
|
|
|
2017-02-20 10:37:12 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Arithmetic
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'a, 'b> Add<&'b RistrettoPoint> for &'a RistrettoPoint {
|
|
|
|
|
|
type Output = RistrettoPoint;
|
2017-02-20 10:37:12 +00:00
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn add(self, other: &'b RistrettoPoint) -> RistrettoPoint {
|
|
|
|
|
|
RistrettoPoint(&self.0 + &other.0)
|
2017-02-20 10:37:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-24 18:58:22 +00:00
|
|
|
|
define_add_variants!(LHS = RistrettoPoint, RHS = RistrettoPoint, Output = RistrettoPoint);
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'b> AddAssign<&'b RistrettoPoint> for RistrettoPoint {
|
|
|
|
|
|
fn add_assign(&mut self, _rhs: &RistrettoPoint) {
|
|
|
|
|
|
*self = (self as &RistrettoPoint) + _rhs;
|
2017-05-18 23:02:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-24 18:58:22 +00:00
|
|
|
|
define_add_assign_variants!(LHS = RistrettoPoint, RHS = RistrettoPoint);
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'a, 'b> Sub<&'b RistrettoPoint> for &'a RistrettoPoint {
|
|
|
|
|
|
type Output = RistrettoPoint;
|
2017-02-20 10:37:12 +00:00
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn sub(self, other: &'b RistrettoPoint) -> RistrettoPoint {
|
|
|
|
|
|
RistrettoPoint(&self.0 - &other.0)
|
2017-02-20 10:37:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-24 18:58:22 +00:00
|
|
|
|
define_sub_variants!(LHS = RistrettoPoint, RHS = RistrettoPoint, Output = RistrettoPoint);
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'b> SubAssign<&'b RistrettoPoint> for RistrettoPoint {
|
|
|
|
|
|
fn sub_assign(&mut self, _rhs: &RistrettoPoint) {
|
|
|
|
|
|
*self = (self as &RistrettoPoint) - _rhs;
|
2017-05-18 23:02:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-24 18:58:22 +00:00
|
|
|
|
define_sub_assign_variants!(LHS = RistrettoPoint, RHS = RistrettoPoint);
|
|
|
|
|
|
|
2018-05-02 22:41:32 +00:00
|
|
|
|
impl<T> Sum<T> for RistrettoPoint
|
|
|
|
|
|
where
|
|
|
|
|
|
T: Borrow<RistrettoPoint>
|
|
|
|
|
|
{
|
|
|
|
|
|
fn sum<I>(iter: I) -> Self
|
|
|
|
|
|
where
|
|
|
|
|
|
I: Iterator<Item = T>
|
|
|
|
|
|
{
|
|
|
|
|
|
iter.fold(RistrettoPoint::identity(), |acc, item| acc + item.borrow())
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'a> Neg for &'a RistrettoPoint {
|
|
|
|
|
|
type Output = RistrettoPoint;
|
2017-02-20 10:37:12 +00:00
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn neg(self) -> RistrettoPoint {
|
|
|
|
|
|
RistrettoPoint(-&self.0)
|
2017-02-20 10:37:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-02-20 07:08:52 +00:00
|
|
|
|
|
2018-01-24 18:58:22 +00:00
|
|
|
|
impl Neg for RistrettoPoint {
|
|
|
|
|
|
type Output = RistrettoPoint;
|
|
|
|
|
|
|
|
|
|
|
|
fn neg(self) -> RistrettoPoint {
|
|
|
|
|
|
-&self
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'b> MulAssign<&'b Scalar> for RistrettoPoint {
|
2017-04-25 23:43:15 +00:00
|
|
|
|
fn mul_assign(&mut self, scalar: &'b Scalar) {
|
2017-07-09 01:22:28 +00:00
|
|
|
|
let result = (self as &RistrettoPoint) * scalar;
|
2017-04-25 23:43:15 +00:00
|
|
|
|
*self = result;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'a, 'b> Mul<&'b Scalar> for &'a RistrettoPoint {
|
|
|
|
|
|
type Output = RistrettoPoint;
|
2017-04-25 23:43:15 +00:00
|
|
|
|
/// Scalar multiplication: compute `scalar * self`.
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn mul(self, scalar: &'b Scalar) -> RistrettoPoint {
|
|
|
|
|
|
RistrettoPoint(&self.0 * scalar)
|
2017-02-20 11:06:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'a, 'b> Mul<&'b RistrettoPoint> for &'a Scalar {
|
|
|
|
|
|
type Output = RistrettoPoint;
|
2017-05-18 02:57:10 +00:00
|
|
|
|
|
|
|
|
|
|
/// Scalar multiplication: compute `self * scalar`.
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn mul(self, point: &'b RistrettoPoint) -> RistrettoPoint {
|
|
|
|
|
|
RistrettoPoint(self * &point.0)
|
2017-05-18 02:57:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-24 18:58:22 +00:00
|
|
|
|
define_mul_assign_variants!(LHS = RistrettoPoint, RHS = Scalar);
|
|
|
|
|
|
|
|
|
|
|
|
define_mul_variants!(LHS = RistrettoPoint, RHS = Scalar, Output = RistrettoPoint);
|
|
|
|
|
|
define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint);
|
|
|
|
|
|
|
2018-05-02 21:54:46 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Multiscalar Multiplication impls
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
// These use iterator combinators to unwrap the underlying points and
|
|
|
|
|
|
// forward to the EdwardsPoint implementations.
|
2018-01-24 18:58:22 +00:00
|
|
|
|
|
2018-07-20 21:43:31 +00:00
|
|
|
|
#[cfg(feature = "alloc")]
|
2018-05-02 21:54:46 +00:00
|
|
|
|
impl MultiscalarMul for RistrettoPoint {
|
|
|
|
|
|
type Point = RistrettoPoint;
|
|
|
|
|
|
|
|
|
|
|
|
fn multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
|
|
|
|
|
|
where
|
|
|
|
|
|
I: IntoIterator,
|
|
|
|
|
|
I::Item: Borrow<Scalar>,
|
|
|
|
|
|
J: IntoIterator,
|
|
|
|
|
|
J::Item: Borrow<RistrettoPoint>,
|
|
|
|
|
|
{
|
|
|
|
|
|
let extended_points = points.into_iter().map(|P| P.borrow().0);
|
|
|
|
|
|
RistrettoPoint(
|
|
|
|
|
|
EdwardsPoint::multiscalar_mul(scalars, extended_points)
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-07-20 21:43:31 +00:00
|
|
|
|
#[cfg(feature = "alloc")]
|
2018-05-02 21:54:46 +00:00
|
|
|
|
impl VartimeMultiscalarMul for RistrettoPoint {
|
|
|
|
|
|
type Point = RistrettoPoint;
|
|
|
|
|
|
|
2018-07-17 15:19:48 +00:00
|
|
|
|
fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<RistrettoPoint>
|
2018-05-02 21:54:46 +00:00
|
|
|
|
where
|
|
|
|
|
|
I: IntoIterator,
|
|
|
|
|
|
I::Item: Borrow<Scalar>,
|
2018-07-17 15:19:48 +00:00
|
|
|
|
J: IntoIterator<Item = Option<RistrettoPoint>>,
|
2018-05-02 21:54:46 +00:00
|
|
|
|
{
|
2018-07-17 15:19:48 +00:00
|
|
|
|
let extended_points = points.into_iter().map(|opt_P| opt_P.map(|P| P.borrow().0));
|
|
|
|
|
|
|
|
|
|
|
|
EdwardsPoint::optional_multiscalar_mul(scalars, extended_points)
|
|
|
|
|
|
.map(|P| RistrettoPoint(P))
|
2018-05-02 21:54:46 +00:00
|
|
|
|
}
|
2017-08-01 01:40:11 +00:00
|
|
|
|
}
|
2017-05-18 02:57:10 +00:00
|
|
|
|
|
2018-07-17 05:22:21 +00:00
|
|
|
|
impl RistrettoPoint {
|
|
|
|
|
|
/// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the
|
|
|
|
|
|
/// Ristretto basepoint.
|
|
|
|
|
|
#[cfg(feature = "stage2_build")]
|
|
|
|
|
|
pub fn vartime_double_scalar_mul_basepoint(
|
|
|
|
|
|
a: &Scalar,
|
|
|
|
|
|
A: &RistrettoPoint,
|
|
|
|
|
|
b: &Scalar,
|
|
|
|
|
|
) -> RistrettoPoint {
|
|
|
|
|
|
RistrettoPoint(
|
|
|
|
|
|
EdwardsPoint::vartime_double_scalar_mul_basepoint(a, &A.0, b)
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-01-26 01:34:57 +00:00
|
|
|
|
/// A precomputed table of multiples of a basepoint, used to accelerate
|
|
|
|
|
|
/// scalar multiplication.
|
2018-03-27 22:10:04 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// A precomputed table of multiples of the Ristretto basepoint is
|
|
|
|
|
|
/// available in the `constants` module:
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
/// use curve25519_dalek::constants;
|
|
|
|
|
|
/// use curve25519_dalek::scalar::Scalar;
|
|
|
|
|
|
///
|
2018-07-17 17:53:02 +00:00
|
|
|
|
/// let a = Scalar::from(87329482u64);
|
2018-03-27 22:10:04 +00:00
|
|
|
|
/// let P = &a * &constants::RISTRETTO_BASEPOINT_TABLE;
|
|
|
|
|
|
/// ```
|
2017-04-26 01:52:45 +00:00
|
|
|
|
#[derive(Clone)]
|
2018-01-26 01:34:57 +00:00
|
|
|
|
pub struct RistrettoBasepointTable(pub(crate) EdwardsBasepointTable);
|
2017-04-26 01:52:45 +00:00
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'a, 'b> Mul<&'b Scalar> for &'a RistrettoBasepointTable {
|
|
|
|
|
|
type Output = RistrettoPoint;
|
2017-02-20 11:06:16 +00:00
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn mul(self, scalar: &'b Scalar) -> RistrettoPoint {
|
|
|
|
|
|
RistrettoPoint(&self.0 * scalar)
|
2017-02-20 11:06:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl<'a, 'b> Mul<&'a RistrettoBasepointTable> for &'b Scalar {
|
|
|
|
|
|
type Output = RistrettoPoint;
|
2017-05-14 03:10:26 +00:00
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn mul(self, basepoint_table: &'a RistrettoBasepointTable) -> RistrettoPoint {
|
|
|
|
|
|
RistrettoPoint(self * &basepoint_table.0)
|
2017-05-14 03:10:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl RistrettoBasepointTable {
|
2017-03-11 06:43:07 +00:00
|
|
|
|
/// Create a precomputed table of multiples of the given `basepoint`.
|
2017-07-09 01:22:28 +00:00
|
|
|
|
pub fn create(basepoint: &RistrettoPoint) -> RistrettoBasepointTable {
|
|
|
|
|
|
RistrettoBasepointTable(EdwardsBasepointTable::create(&basepoint.0))
|
2017-03-11 06:43:07 +00:00
|
|
|
|
}
|
2017-05-04 01:18:00 +00:00
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
/// Get the basepoint for this table as a `RistrettoPoint`.
|
|
|
|
|
|
pub fn basepoint(&self) -> RistrettoPoint {
|
|
|
|
|
|
RistrettoPoint(self.0.basepoint())
|
2017-05-04 01:18:00 +00:00
|
|
|
|
}
|
2017-03-11 06:43:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-05-14 02:59:54 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Constant-time conditional assignment
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl ConditionallyAssignable for RistrettoPoint {
|
2018-02-07 00:47:30 +00:00
|
|
|
|
/// Conditionally assign `other` to `self`, if `choice == Choice(1)`.
|
2017-05-14 02:59:54 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// # Example
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ```
|
2017-05-31 04:37:16 +00:00
|
|
|
|
/// # extern crate subtle;
|
|
|
|
|
|
/// # extern crate curve25519_dalek;
|
|
|
|
|
|
/// #
|
2018-02-07 00:47:30 +00:00
|
|
|
|
/// use subtle::ConditionallyAssignable;
|
|
|
|
|
|
/// use subtle::Choice;
|
2017-05-31 04:37:16 +00:00
|
|
|
|
/// #
|
2017-11-17 01:34:28 +00:00
|
|
|
|
/// # use curve25519_dalek::traits::Identity;
|
2017-07-09 01:22:28 +00:00
|
|
|
|
/// # use curve25519_dalek::ristretto::RistrettoPoint;
|
2017-05-14 02:59:54 +00:00
|
|
|
|
/// # use curve25519_dalek::constants;
|
2017-05-31 04:37:16 +00:00
|
|
|
|
/// # fn main() {
|
2018-02-07 00:47:30 +00:00
|
|
|
|
///
|
2017-07-09 01:22:28 +00:00
|
|
|
|
/// let A = RistrettoPoint::identity();
|
|
|
|
|
|
/// let B = constants::RISTRETTO_BASEPOINT_POINT;
|
2017-05-14 02:59:54 +00:00
|
|
|
|
///
|
|
|
|
|
|
/// let mut P = A;
|
|
|
|
|
|
///
|
2018-02-07 00:47:30 +00:00
|
|
|
|
/// P.conditional_assign(&B, Choice::from(0));
|
|
|
|
|
|
/// assert_eq!(P, A);
|
|
|
|
|
|
/// P.conditional_assign(&B, Choice::from(1));
|
|
|
|
|
|
/// assert_eq!(P, B);
|
2017-05-31 04:37:16 +00:00
|
|
|
|
/// # }
|
2017-05-14 02:59:54 +00:00
|
|
|
|
/// ```
|
2018-02-07 00:47:30 +00:00
|
|
|
|
fn conditional_assign(&mut self, other: &RistrettoPoint, choice: Choice) {
|
2017-05-14 02:59:54 +00:00
|
|
|
|
self.0.X.conditional_assign(&other.0.X, choice);
|
|
|
|
|
|
self.0.Y.conditional_assign(&other.0.Y, choice);
|
|
|
|
|
|
self.0.Z.conditional_assign(&other.0.Z, choice);
|
|
|
|
|
|
self.0.T.conditional_assign(&other.0.T, choice);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-20 07:08:52 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Debug traits
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl Debug for CompressedRistretto {
|
2017-02-20 07:08:52 +00:00
|
|
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
2017-07-09 01:22:28 +00:00
|
|
|
|
write!(f, "CompressedRistretto: {:?}", self.as_bytes())
|
2017-02-20 07:08:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
impl Debug for RistrettoPoint {
|
2017-02-20 07:25:04 +00:00
|
|
|
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
2017-02-20 11:38:31 +00:00
|
|
|
|
let coset = self.coset4();
|
2017-07-09 01:22:28 +00:00
|
|
|
|
write!(f, "RistrettoPoint: coset \n{:?}\n{:?}\n{:?}\n{:?}",
|
2017-02-20 11:38:31 +00:00
|
|
|
|
coset[0], coset[1], coset[2], coset[3])
|
2017-02-20 07:25:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-02-20 07:08:52 +00:00
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
// Tests
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
2018-05-14 22:41:45 +00:00
|
|
|
|
#[cfg(all(test, feature = "stage2_build"))]
|
2017-02-20 07:08:52 +00:00
|
|
|
|
mod test {
|
2018-07-20 21:43:31 +00:00
|
|
|
|
#[cfg(feature = "rand")]
|
2018-05-14 20:24:23 +00:00
|
|
|
|
use rand::rngs::OsRng;
|
2017-02-20 07:08:52 +00:00
|
|
|
|
|
|
|
|
|
|
use scalar::Scalar;
|
|
|
|
|
|
use constants;
|
2017-08-03 05:35:12 +00:00
|
|
|
|
use edwards::CompressedEdwardsY;
|
2017-11-17 01:34:28 +00:00
|
|
|
|
use traits::{Identity, ValidityCheck};
|
2017-02-20 07:08:52 +00:00
|
|
|
|
use super::*;
|
|
|
|
|
|
|
2017-05-14 05:40:51 +00:00
|
|
|
|
#[test]
|
2017-05-15 01:06:59 +00:00
|
|
|
|
#[cfg(feature = "serde")]
|
2018-09-26 22:28:47 +00:00
|
|
|
|
fn serde_bincode_basepoint_roundtrip() {
|
|
|
|
|
|
use bincode;
|
|
|
|
|
|
|
|
|
|
|
|
let encoded = bincode::serialize(&constants::RISTRETTO_BASEPOINT_POINT).unwrap();
|
|
|
|
|
|
let enc_compressed = bincode::serialize(&constants::RISTRETTO_BASEPOINT_COMPRESSED).unwrap();
|
|
|
|
|
|
assert_eq!(encoded, enc_compressed);
|
|
|
|
|
|
|
|
|
|
|
|
let dec_uncompressed: RistrettoPoint = bincode::deserialize(&encoded).unwrap();
|
|
|
|
|
|
let dec_compressed: CompressedRistretto = bincode::deserialize(&encoded).unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(dec_uncompressed, constants::RISTRETTO_BASEPOINT_POINT);
|
|
|
|
|
|
assert_eq!(dec_compressed, constants::RISTRETTO_BASEPOINT_COMPRESSED);
|
2017-05-14 05:40:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-07-09 01:22:28 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn scalarmult_ristrettopoint_works_both_ways() {
|
|
|
|
|
|
let P = constants::RISTRETTO_BASEPOINT_POINT;
|
2018-07-17 17:53:02 +00:00
|
|
|
|
let s = Scalar::from(999u64);
|
2017-07-09 01:22:28 +00:00
|
|
|
|
|
|
|
|
|
|
let P1 = &P * &s;
|
|
|
|
|
|
let P2 = &s * &P;
|
|
|
|
|
|
|
|
|
|
|
|
assert!(P1.compress().as_bytes() == P2.compress().as_bytes());
|
|
|
|
|
|
}
|
2017-05-14 05:40:51 +00:00
|
|
|
|
|
2018-05-02 22:41:32 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn impl_sum() {
|
|
|
|
|
|
|
|
|
|
|
|
// Test that sum works for non-empty iterators
|
|
|
|
|
|
let BASE = constants::RISTRETTO_BASEPOINT_POINT;
|
|
|
|
|
|
|
2018-07-17 17:53:02 +00:00
|
|
|
|
let s1 = Scalar::from(999u64);
|
2018-05-02 22:41:32 +00:00
|
|
|
|
let P1 = &BASE * &s1;
|
|
|
|
|
|
|
2018-07-17 17:53:02 +00:00
|
|
|
|
let s2 = Scalar::from(333u64);
|
2018-05-02 22:41:32 +00:00
|
|
|
|
let P2 = &BASE * &s2;
|
|
|
|
|
|
|
|
|
|
|
|
let vec = vec![P1.clone(), P2.clone()];
|
|
|
|
|
|
let sum: RistrettoPoint = vec.iter().sum();
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(sum, P1 + P2);
|
|
|
|
|
|
|
|
|
|
|
|
// Test that sum works for the empty iterator
|
|
|
|
|
|
let empty_vector: Vec<RistrettoPoint> = vec![];
|
|
|
|
|
|
let sum: RistrettoPoint = empty_vector.iter().sum();
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(sum, RistrettoPoint::identity());
|
|
|
|
|
|
|
|
|
|
|
|
// Test that sum works on owning iterators
|
2018-07-17 17:53:02 +00:00
|
|
|
|
let s = Scalar::from(2u64);
|
2018-05-02 22:41:32 +00:00
|
|
|
|
let mapped = vec.iter().map(|x| x * &s);
|
|
|
|
|
|
let sum: RistrettoPoint = mapped.sum();
|
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(sum, &P1 * &s + &P2 * &s);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-21 00:58:01 +00:00
|
|
|
|
#[test]
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn decompress_negative_s_fails() {
|
2017-02-21 00:58:01 +00:00
|
|
|
|
// constants::d is neg, so decompression should fail as |d| != d.
|
2017-10-31 00:13:25 +00:00
|
|
|
|
let bad_compressed = CompressedRistretto(constants::EDWARDS_D.to_bytes());
|
2017-03-07 09:28:38 +00:00
|
|
|
|
assert!(bad_compressed.decompress().is_none());
|
2017-02-21 00:58:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-20 07:08:52 +00:00
|
|
|
|
#[test]
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn decompress_id() {
|
|
|
|
|
|
let compressed_id = CompressedRistretto::identity();
|
2017-02-20 07:08:52 +00:00
|
|
|
|
let id = compressed_id.decompress().unwrap();
|
2017-07-19 05:03:30 +00:00
|
|
|
|
let mut identity_in_coset = false;
|
|
|
|
|
|
for P in &id.coset4() {
|
|
|
|
|
|
if P.compress() == CompressedEdwardsY::identity() {
|
|
|
|
|
|
identity_in_coset = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
assert!(identity_in_coset);
|
2017-02-20 07:08:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn compress_id() {
|
|
|
|
|
|
let id = RistrettoPoint::identity();
|
|
|
|
|
|
assert_eq!(id.compress(), CompressedRistretto::identity());
|
2017-02-20 07:08:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn basepoint_roundtrip() {
|
|
|
|
|
|
let bp_compressed_ristretto = constants::RISTRETTO_BASEPOINT_POINT.compress();
|
|
|
|
|
|
let bp_recaf = bp_compressed_ristretto.decompress().unwrap().0;
|
2017-02-20 11:15:08 +00:00
|
|
|
|
// Check that bp_recaf differs from bp by a point of order 4
|
2017-07-09 01:22:28 +00:00
|
|
|
|
let diff = &constants::RISTRETTO_BASEPOINT_POINT.0 - &bp_recaf;
|
2018-03-22 18:35:42 +00:00
|
|
|
|
let diff4 = diff.mul_by_pow_2(2);
|
2017-09-14 01:54:28 +00:00
|
|
|
|
assert_eq!(diff4.compress(), CompressedEdwardsY::identity());
|
2017-02-20 07:08:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-05-20 09:34:43 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn encodings_of_small_multiples_of_basepoint() {
|
2017-07-24 03:46:24 +00:00
|
|
|
|
// Table of encodings of i*basepoint
|
|
|
|
|
|
// Generated using ristretto.sage
|
2017-05-20 09:34:43 +00:00
|
|
|
|
let compressed = [
|
2017-10-03 00:02:12 +00:00
|
|
|
|
CompressedRistretto([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, 0]),
|
2017-07-24 03:46:24 +00:00
|
|
|
|
CompressedRistretto([226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227, 11, 106, 165, 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118]),
|
|
|
|
|
|
CompressedRistretto([106, 73, 50, 16, 247, 73, 156, 209, 127, 236, 181, 16, 174, 12, 234, 35, 161, 16, 232, 213, 185, 1, 248, 172, 173, 211, 9, 92, 115, 163, 185, 25]),
|
|
|
|
|
|
CompressedRistretto([148, 116, 31, 93, 93, 82, 117, 94, 206, 79, 35, 240, 68, 238, 39, 213, 209, 234, 30, 43, 209, 150, 180, 98, 22, 107, 22, 21, 42, 157, 2, 89]),
|
|
|
|
|
|
CompressedRistretto([218, 128, 134, 39, 115, 53, 139, 70, 111, 250, 223, 224, 179, 41, 58, 179, 217, 253, 83, 197, 234, 108, 149, 83, 88, 245, 104, 50, 45, 175, 106, 87]),
|
|
|
|
|
|
CompressedRistretto([232, 130, 177, 49, 1, 107, 82, 193, 211, 51, 112, 128, 24, 124, 247, 104, 66, 62, 252, 203, 181, 23, 187, 73, 90, 184, 18, 196, 22, 15, 244, 78]),
|
|
|
|
|
|
CompressedRistretto([246, 71, 70, 211, 201, 43, 19, 5, 14, 216, 216, 2, 54, 167, 240, 0, 124, 59, 63, 150, 47, 91, 167, 147, 209, 154, 96, 30, 187, 29, 244, 3]),
|
|
|
|
|
|
CompressedRistretto([68, 245, 53, 32, 146, 110, 200, 31, 189, 90, 56, 120, 69, 190, 183, 223, 133, 169, 106, 36, 236, 225, 135, 56, 189, 207, 166, 167, 130, 42, 23, 109]),
|
|
|
|
|
|
CompressedRistretto([144, 50, 147, 216, 242, 40, 126, 190, 16, 226, 55, 77, 193, 165, 62, 11, 200, 135, 229, 146, 105, 159, 2, 208, 119, 213, 38, 60, 221, 85, 96, 28]),
|
|
|
|
|
|
CompressedRistretto([2, 98, 42, 206, 143, 115, 3, 163, 28, 175, 198, 63, 143, 196, 143, 220, 22, 225, 200, 200, 210, 52, 178, 240, 214, 104, 82, 130, 169, 7, 96, 49]),
|
|
|
|
|
|
CompressedRistretto([32, 112, 111, 215, 136, 178, 114, 10, 30, 210, 165, 218, 212, 149, 43, 1, 244, 19, 188, 240, 231, 86, 77, 232, 205, 200, 22, 104, 158, 45, 185, 95]),
|
|
|
|
|
|
CompressedRistretto([188, 232, 63, 139, 165, 221, 47, 165, 114, 134, 76, 36, 186, 24, 16, 249, 82, 43, 198, 0, 74, 254, 149, 135, 122, 199, 50, 65, 202, 253, 171, 66]),
|
|
|
|
|
|
CompressedRistretto([228, 84, 158, 225, 107, 154, 160, 48, 153, 202, 32, 140, 103, 173, 175, 202, 250, 76, 63, 62, 78, 83, 3, 222, 96, 38, 227, 202, 143, 248, 68, 96]),
|
|
|
|
|
|
CompressedRistretto([170, 82, 224, 0, 223, 46, 22, 245, 95, 177, 3, 47, 195, 59, 196, 39, 66, 218, 214, 189, 90, 143, 192, 190, 1, 103, 67, 108, 89, 72, 80, 31]),
|
|
|
|
|
|
CompressedRistretto([70, 55, 107, 128, 244, 9, 178, 157, 194, 181, 246, 240, 197, 37, 145, 153, 8, 150, 229, 113, 111, 65, 71, 124, 211, 0, 133, 171, 127, 16, 48, 30]),
|
|
|
|
|
|
CompressedRistretto([224, 196, 24, 247, 200, 217, 196, 205, 215, 57, 91, 147, 234, 18, 79, 58, 217, 144, 33, 187, 104, 29, 252, 51, 2, 169, 217, 154, 46, 83, 230, 78]),
|
2017-05-20 09:34:43 +00:00
|
|
|
|
];
|
2017-07-09 01:22:28 +00:00
|
|
|
|
let mut bp = RistrettoPoint::identity();
|
2017-05-20 09:34:43 +00:00
|
|
|
|
for i in 0..16 {
|
|
|
|
|
|
assert_eq!(bp.compress(), compressed[i]);
|
2017-07-09 01:22:28 +00:00
|
|
|
|
bp = &bp + &constants::RISTRETTO_BASEPOINT_POINT;
|
2017-05-20 09:34:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-02-20 07:08:52 +00:00
|
|
|
|
#[test]
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn four_torsion_basepoint() {
|
|
|
|
|
|
let bp = constants::RISTRETTO_BASEPOINT_POINT;
|
2017-02-20 11:38:31 +00:00
|
|
|
|
let bp_coset = bp.coset4();
|
|
|
|
|
|
for i in 0..4 {
|
2017-07-09 01:22:28 +00:00
|
|
|
|
assert_eq!(bp, RistrettoPoint(bp_coset[i]));
|
2017-02-20 07:08:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-07-20 21:43:31 +00:00
|
|
|
|
#[cfg(feature = "rand")]
|
2017-02-20 07:08:52 +00:00
|
|
|
|
#[test]
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn four_torsion_random() {
|
2017-02-20 07:08:52 +00:00
|
|
|
|
let mut rng = OsRng::new().unwrap();
|
2017-07-09 01:22:28 +00:00
|
|
|
|
let B = &constants::RISTRETTO_BASEPOINT_TABLE;
|
2017-04-26 01:52:45 +00:00
|
|
|
|
let P = B * &Scalar::random(&mut rng);
|
2017-02-20 11:38:31 +00:00
|
|
|
|
let P_coset = P.coset4();
|
|
|
|
|
|
for i in 0..4 {
|
2017-07-09 01:22:28 +00:00
|
|
|
|
assert_eq!(P, RistrettoPoint(P_coset[i]));
|
2017-02-20 07:08:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-02-21 02:22:58 +00:00
|
|
|
|
|
2017-10-03 00:02:12 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn elligator_vs_ristretto_sage() {
|
2017-10-03 20:27:08 +00:00
|
|
|
|
// Test vectors extracted from ristretto.sage.
|
|
|
|
|
|
//
|
|
|
|
|
|
// Notice that all of the byte sequences have bit 255 set to 0; this is because
|
|
|
|
|
|
// ristretto.sage does not mask the high bit of a field element. When the high bit is set,
|
|
|
|
|
|
// the ristretto.sage elligator implementation gives different results, since it takes a
|
|
|
|
|
|
// different field element as input.
|
|
|
|
|
|
let bytes: [[u8;32]; 16] = [
|
|
|
|
|
|
[184, 249, 135, 49, 253, 123, 89, 113, 67, 160, 6, 239, 7, 105, 211, 41, 192, 249, 185, 57, 9, 102, 70, 198, 15, 127, 7, 26, 160, 102, 134, 71],
|
|
|
|
|
|
[229, 14, 241, 227, 75, 9, 118, 60, 128, 153, 226, 21, 183, 217, 91, 136, 98, 0, 231, 156, 124, 77, 82, 139, 142, 134, 164, 169, 169, 62, 250, 52],
|
|
|
|
|
|
[115, 109, 36, 220, 180, 223, 99, 6, 204, 169, 19, 29, 169, 68, 84, 23, 21, 109, 189, 149, 127, 205, 91, 102, 172, 35, 112, 35, 134, 69, 186, 34],
|
|
|
|
|
|
[16, 49, 96, 107, 171, 199, 164, 9, 129, 16, 64, 62, 241, 63, 132, 173, 209, 160, 112, 215, 105, 50, 157, 81, 253, 105, 1, 154, 229, 25, 120, 83],
|
|
|
|
|
|
[156, 131, 161, 162, 236, 251, 5, 187, 167, 171, 17, 178, 148, 210, 90, 207, 86, 21, 79, 161, 167, 215, 234, 1, 136, 242, 182, 248, 38, 85, 79, 86],
|
|
|
|
|
|
[251, 177, 124, 54, 18, 101, 75, 235, 245, 186, 19, 46, 133, 157, 229, 64, 10, 136, 181, 185, 78, 144, 254, 167, 137, 49, 107, 10, 61, 10, 21, 25],
|
|
|
|
|
|
[232, 193, 20, 68, 240, 77, 186, 77, 183, 40, 44, 86, 150, 31, 198, 212, 76, 81, 3, 217, 197, 8, 126, 128, 126, 152, 164, 208, 153, 44, 189, 77],
|
|
|
|
|
|
[173, 229, 149, 177, 37, 230, 30, 69, 61, 56, 172, 190, 219, 115, 167, 194, 71, 134, 59, 75, 28, 244, 118, 26, 162, 97, 64, 16, 15, 189, 30, 64],
|
|
|
|
|
|
[106, 71, 61, 107, 250, 117, 42, 151, 91, 202, 212, 100, 52, 188, 190, 21, 125, 218, 31, 18, 253, 241, 160, 133, 57, 242, 3, 164, 189, 68, 111, 75],
|
|
|
|
|
|
[112, 204, 182, 90, 220, 198, 120, 73, 173, 107, 193, 17, 227, 40, 162, 36, 150, 141, 235, 55, 172, 183, 12, 39, 194, 136, 43, 153, 244, 118, 91, 89],
|
|
|
|
|
|
[111, 24, 203, 123, 254, 189, 11, 162, 51, 196, 163, 136, 204, 143, 10, 222, 33, 112, 81, 205, 34, 35, 8, 66, 90, 6, 164, 58, 170, 177, 34, 25],
|
|
|
|
|
|
[225, 183, 30, 52, 236, 82, 6, 183, 109, 25, 227, 181, 25, 82, 41, 193, 80, 77, 161, 80, 242, 203, 79, 204, 136, 245, 131, 110, 237, 106, 3, 58],
|
|
|
|
|
|
[207, 246, 38, 56, 30, 86, 176, 90, 27, 200, 61, 42, 221, 27, 56, 210, 79, 178, 189, 120, 68, 193, 120, 167, 77, 185, 53, 197, 124, 128, 191, 126],
|
|
|
|
|
|
[1, 136, 215, 80, 240, 46, 63, 147, 16, 244, 230, 207, 82, 189, 74, 50, 106, 169, 138, 86, 30, 131, 214, 202, 166, 125, 251, 228, 98, 24, 36, 21],
|
|
|
|
|
|
[210, 207, 228, 56, 155, 116, 207, 54, 84, 195, 251, 215, 249, 199, 116, 75, 109, 239, 196, 251, 194, 246, 252, 228, 70, 146, 156, 35, 25, 39, 241, 4],
|
|
|
|
|
|
[34, 116, 123, 9, 8, 40, 93, 189, 9, 103, 57, 103, 66, 227, 3, 2, 157, 107, 134, 219, 202, 74, 230, 154, 78, 107, 219, 195, 214, 14, 84, 80],
|
2017-10-03 00:02:12 +00:00
|
|
|
|
];
|
2017-10-03 20:27:08 +00:00
|
|
|
|
let encoded_images: [CompressedRistretto; 16] = [
|
|
|
|
|
|
CompressedRistretto([176, 157, 237, 97, 66, 29, 140, 166, 168, 94, 26, 157, 212, 216, 229, 160, 195, 246, 232, 239, 169, 112, 63, 193, 64, 32, 152, 69, 11, 190, 246, 86]),
|
|
|
|
|
|
CompressedRistretto([234, 141, 77, 203, 181, 225, 250, 74, 171, 62, 15, 118, 78, 212, 150, 19, 131, 14, 188, 238, 194, 244, 141, 138, 166, 162, 83, 122, 228, 201, 19, 26]),
|
|
|
|
|
|
CompressedRistretto([232, 231, 51, 92, 5, 168, 80, 36, 173, 179, 104, 68, 186, 149, 68, 40, 140, 170, 27, 103, 99, 140, 21, 242, 43, 62, 250, 134, 208, 255, 61, 89]),
|
|
|
|
|
|
CompressedRistretto([208, 120, 140, 129, 177, 179, 237, 159, 252, 160, 28, 13, 206, 5, 211, 241, 192, 218, 1, 97, 130, 241, 20, 169, 119, 46, 246, 29, 79, 80, 77, 84]),
|
|
|
|
|
|
CompressedRistretto([202, 11, 236, 145, 58, 12, 181, 157, 209, 6, 213, 88, 75, 147, 11, 119, 191, 139, 47, 142, 33, 36, 153, 193, 223, 183, 178, 8, 205, 120, 248, 110]),
|
|
|
|
|
|
CompressedRistretto([26, 66, 231, 67, 203, 175, 116, 130, 32, 136, 62, 253, 215, 46, 5, 214, 166, 248, 108, 237, 216, 71, 244, 173, 72, 133, 82, 6, 143, 240, 104, 41]),
|
|
|
|
|
|
CompressedRistretto([40, 157, 102, 96, 201, 223, 200, 197, 150, 181, 106, 83, 103, 126, 143, 33, 145, 230, 78, 6, 171, 146, 210, 143, 112, 5, 245, 23, 183, 138, 18, 120]),
|
|
|
|
|
|
CompressedRistretto([220, 37, 27, 203, 239, 196, 176, 131, 37, 66, 188, 243, 185, 250, 113, 23, 167, 211, 154, 243, 168, 215, 54, 171, 159, 36, 195, 81, 13, 150, 43, 43]),
|
|
|
|
|
|
CompressedRistretto([232, 121, 176, 222, 183, 196, 159, 90, 238, 193, 105, 52, 101, 167, 244, 170, 121, 114, 196, 6, 67, 152, 80, 185, 221, 7, 83, 105, 176, 208, 224, 121]),
|
|
|
|
|
|
CompressedRistretto([226, 181, 183, 52, 241, 163, 61, 179, 221, 207, 220, 73, 245, 242, 25, 236, 67, 84, 179, 222, 167, 62, 167, 182, 32, 9, 92, 30, 165, 127, 204, 68]),
|
|
|
|
|
|
CompressedRistretto([226, 119, 16, 242, 200, 139, 240, 87, 11, 222, 92, 146, 156, 243, 46, 119, 65, 59, 1, 248, 92, 183, 50, 175, 87, 40, 206, 53, 208, 220, 148, 13]),
|
|
|
|
|
|
CompressedRistretto([70, 240, 79, 112, 54, 157, 228, 146, 74, 122, 216, 88, 232, 62, 158, 13, 14, 146, 115, 117, 176, 222, 90, 225, 244, 23, 94, 190, 150, 7, 136, 96]),
|
|
|
|
|
|
CompressedRistretto([22, 71, 241, 103, 45, 193, 195, 144, 183, 101, 154, 50, 39, 68, 49, 110, 51, 44, 62, 0, 229, 113, 72, 81, 168, 29, 73, 106, 102, 40, 132, 24]),
|
|
|
|
|
|
CompressedRistretto([196, 133, 107, 11, 130, 105, 74, 33, 204, 171, 133, 221, 174, 193, 241, 36, 38, 179, 196, 107, 219, 185, 181, 253, 228, 47, 155, 42, 231, 73, 41, 78]),
|
|
|
|
|
|
CompressedRistretto([58, 255, 225, 197, 115, 208, 160, 143, 39, 197, 82, 69, 143, 235, 92, 170, 74, 40, 57, 11, 171, 227, 26, 185, 217, 207, 90, 185, 197, 190, 35, 60]),
|
|
|
|
|
|
CompressedRistretto([88, 43, 92, 118, 223, 136, 105, 145, 238, 186, 115, 8, 214, 112, 153, 253, 38, 108, 205, 230, 157, 130, 11, 66, 101, 85, 253, 110, 110, 14, 148, 112]),
|
2017-10-03 00:02:12 +00:00
|
|
|
|
];
|
2017-10-03 20:27:08 +00:00
|
|
|
|
for i in 0..16 {
|
2017-10-03 00:02:12 +00:00
|
|
|
|
let r_0 = FieldElement::from_bytes(&bytes[i]);
|
2018-03-19 22:37:38 +00:00
|
|
|
|
let Q = RistrettoPoint::elligator_ristretto_flavor(&r_0);
|
2017-10-03 00:02:12 +00:00
|
|
|
|
assert_eq!(Q.compress(), encoded_images[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-07-20 21:43:31 +00:00
|
|
|
|
#[cfg(feature = "rand")]
|
2017-02-21 02:22:58 +00:00
|
|
|
|
#[test]
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn random_roundtrip() {
|
2017-02-21 02:22:58 +00:00
|
|
|
|
let mut rng = OsRng::new().unwrap();
|
2017-07-09 01:22:28 +00:00
|
|
|
|
let B = &constants::RISTRETTO_BASEPOINT_TABLE;
|
2017-03-07 09:32:11 +00:00
|
|
|
|
for _ in 0..100 {
|
2017-04-26 01:52:45 +00:00
|
|
|
|
let P = B * &Scalar::random(&mut rng);
|
2017-03-07 09:28:38 +00:00
|
|
|
|
let compressed_P = P.compress();
|
|
|
|
|
|
let Q = compressed_P.decompress().unwrap();
|
2017-02-21 02:22:58 +00:00
|
|
|
|
assert_eq!(P, Q);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-05-16 04:06:26 +00:00
|
|
|
|
|
2018-07-20 21:43:31 +00:00
|
|
|
|
#[cfg(feature = "rand")]
|
2018-01-16 23:41:05 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn double_and_compress_1024_random_points() {
|
|
|
|
|
|
let mut rng = OsRng::new().unwrap();
|
|
|
|
|
|
|
2018-01-31 02:19:53 +00:00
|
|
|
|
let points: Vec<RistrettoPoint> =
|
2018-01-16 23:41:05 +00:00
|
|
|
|
(0..1024).map(|_| RistrettoPoint::random(&mut rng)).collect();
|
|
|
|
|
|
|
|
|
|
|
|
let compressed = RistrettoPoint::double_and_compress_batch(&points);
|
2018-01-31 02:19:53 +00:00
|
|
|
|
|
2018-01-16 23:41:05 +00:00
|
|
|
|
for (P, P2_compressed) in points.iter().zip(compressed.iter()) {
|
|
|
|
|
|
assert_eq!(*P2_compressed, (P + P).compress());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-07-20 21:43:31 +00:00
|
|
|
|
#[cfg(feature = "rand")]
|
2017-05-16 04:06:26 +00:00
|
|
|
|
#[test]
|
2017-07-09 01:22:28 +00:00
|
|
|
|
fn random_is_valid() {
|
2017-05-16 04:06:26 +00:00
|
|
|
|
let mut rng = OsRng::new().unwrap();
|
2017-05-21 03:16:11 +00:00
|
|
|
|
for _ in 0..100 {
|
2017-07-09 01:22:28 +00:00
|
|
|
|
let P = RistrettoPoint::random(&mut rng);
|
2017-05-16 04:06:26 +00:00
|
|
|
|
// Check that P is on the curve
|
2017-10-05 00:56:40 +00:00
|
|
|
|
assert!(P.0.is_valid());
|
2017-07-09 01:22:28 +00:00
|
|
|
|
// Check that P is in the image of the ristretto map
|
2017-05-18 03:43:15 +00:00
|
|
|
|
P.compress();
|
2017-05-16 04:06:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-02-20 07:08:52 +00:00
|
|
|
|
}
|