mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-09 21:10:44 +00:00
Merge branch 'release/0.8.0'
This commit is contained in:
commit
1218188003
7 changed files with 385 additions and 21 deletions
|
|
@ -7,6 +7,7 @@ rust:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
- TEST_COMMAND=test FEATURES=--features="yolocrypto"
|
- TEST_COMMAND=test FEATURES=--features="yolocrypto"
|
||||||
|
- TEST_COMMAND=test FEATURES=--features="yolocrypto serde"
|
||||||
- TEST_COMMAND=test FEATURES=--features="yolocrypto nightly"
|
- TEST_COMMAND=test FEATURES=--features="yolocrypto nightly"
|
||||||
- TEST_COMMAND=bench FEATURES=--features="yolocrypto bench"
|
- TEST_COMMAND=bench FEATURES=--features="yolocrypto bench"
|
||||||
- TEST_COMMAND=bench FEATURES=--features="yolocrypto nightly bench"
|
- TEST_COMMAND=bench FEATURES=--features="yolocrypto nightly bench"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "curve25519-dalek"
|
name = "curve25519-dalek"
|
||||||
version = "0.7.1"
|
version = "0.8.0"
|
||||||
authors = ["Isis Lovecruft <isis@patternsinthevoid.net>",
|
authors = ["Isis Lovecruft <isis@patternsinthevoid.net>",
|
||||||
"Henry de Valence <hdevalence@hdevalence.ca>"]
|
"Henry de Valence <hdevalence@hdevalence.ca>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
@ -18,6 +18,10 @@ exclude = [
|
||||||
[badges]
|
[badges]
|
||||||
travis-ci = { repository = "isislovecruft/curve25519-dalek", branch = "master"}
|
travis-ci = { repository = "isislovecruft/curve25519-dalek", branch = "master"}
|
||||||
|
|
||||||
|
[dependencies.serde]
|
||||||
|
version = "1.0"
|
||||||
|
optional = true
|
||||||
|
|
||||||
[dependencies.arrayref]
|
[dependencies.arrayref]
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
|
||||||
|
|
@ -35,6 +39,9 @@ version = "^0.6"
|
||||||
[dev-dependencies.sha2]
|
[dev-dependencies.sha2]
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
|
|
||||||
|
[dev-dependencies.serde_cbor]
|
||||||
|
version = "0.6"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
nightly = ["radix_51"]
|
nightly = ["radix_51"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ Extensive documentation is available [here](https://docs.rs/curve25519-dalek).
|
||||||
To install, add the following to the dependencies section of your project's
|
To install, add the following to the dependencies section of your project's
|
||||||
`Cargo.toml`:
|
`Cargo.toml`:
|
||||||
|
|
||||||
curve25519-dalek = "^0.7"
|
curve25519-dalek = "^0.8"
|
||||||
|
|
||||||
Then, in your library or executable source, add:
|
Then, in your library or executable source, add:
|
||||||
|
|
||||||
|
|
|
||||||
98
src/curve.rs
98
src/curve.rs
|
|
@ -87,8 +87,6 @@ use core::ops::{Mul, MulAssign};
|
||||||
use core::ops::Index;
|
use core::ops::Index;
|
||||||
|
|
||||||
use constants;
|
use constants;
|
||||||
#[cfg(feature = "yolocrypto")]
|
|
||||||
use decaf::DecafPoint;
|
|
||||||
use field::FieldElement;
|
use field::FieldElement;
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
use subtle::arrays_equal_ct;
|
use subtle::arrays_equal_ct;
|
||||||
|
|
@ -278,6 +276,59 @@ impl CompressedMontgomeryU {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Serde support
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Serializes to and from `ExtendedPoint` directly, doing compression
|
||||||
|
// and decompression internally. This means that users can create
|
||||||
|
// structs containing `ExtendedPoint`s and use Serde's derived
|
||||||
|
// serializers to serialize those structures.
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{self, Serialize, Deserialize, Serializer, Deserializer};
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::de::Visitor;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl Serialize for ExtendedPoint {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where S: Serializer
|
||||||
|
{
|
||||||
|
serializer.serialize_bytes(self.compress_edwards().as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> Deserialize<'de> for ExtendedPoint {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where D: Deserializer<'de>
|
||||||
|
{
|
||||||
|
struct ExtendedPointVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for ExtendedPointVisitor {
|
||||||
|
type Value = ExtendedPoint;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||||
|
formatter.write_str("a valid point in Edwards y + sign format")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<ExtendedPoint, E>
|
||||||
|
where E: serde::de::Error
|
||||||
|
{
|
||||||
|
if v.len() == 32 {
|
||||||
|
let arr32 = array_ref!(v,0,32); // &[u8;32] from &[u8]
|
||||||
|
CompressedEdwardsY(*arr32).decompress()
|
||||||
|
.ok_or(serde::de::Error::custom("decompression failed"))
|
||||||
|
} else {
|
||||||
|
Err(serde::de::Error::invalid_length(v.len(), &self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_bytes(ExtendedPointVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Internal point representations
|
// Internal point representations
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
@ -308,11 +359,12 @@ pub struct ProjectivePoint {
|
||||||
/// A `CompletedPoint` is a point ((X:Z), (Y:T)) in 𝗣¹(𝔽ₚ)×𝗣¹(𝔽ₚ).
|
/// A `CompletedPoint` is a point ((X:Z), (Y:T)) in 𝗣¹(𝔽ₚ)×𝗣¹(𝔽ₚ).
|
||||||
/// A point (x,y) in the affine model corresponds to ((x:1),(y:1)).
|
/// A point (x,y) in the affine model corresponds to ((x:1),(y:1)).
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
pub struct CompletedPoint {
|
pub struct CompletedPoint {
|
||||||
X: FieldElement,
|
pub X: FieldElement,
|
||||||
Y: FieldElement,
|
pub Y: FieldElement,
|
||||||
Z: FieldElement,
|
pub Z: FieldElement,
|
||||||
T: FieldElement,
|
pub T: FieldElement,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A pre-computed point in the affine model for the curve, represented as
|
/// A pre-computed point in the affine model for the curve, represented as
|
||||||
|
|
@ -843,16 +895,6 @@ impl<'a, 'b> Mul<&'b ExtendedPoint> for &'a Scalar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "yolocrypto")]
|
|
||||||
impl<'a, 'b> Mul<&'b DecafPoint> for &'a Scalar {
|
|
||||||
type Output = DecafPoint;
|
|
||||||
|
|
||||||
/// Scalar multiplication: compute `self * scalar`.
|
|
||||||
fn mul(self, point: &'b DecafPoint) -> DecafPoint {
|
|
||||||
DecafPoint(self * &point.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Precomputation
|
/// Precomputation
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -1576,6 +1618,28 @@ mod test {
|
||||||
assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT);
|
assert_eq!(result.compress_edwards(), DOUBLE_SCALAR_MULT_RESULT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde_cbor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn serde_cbor_basepoint_roundtrip() {
|
||||||
|
let output = serde_cbor::to_vec(&constants::ED25519_BASEPOINT).unwrap();
|
||||||
|
let parsed: ExtendedPoint = serde_cbor::from_slice(&output).unwrap();
|
||||||
|
assert_eq!(parsed.compress_edwards(), constants::BASE_CMPRSSD);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn serde_cbor_decode_invalid_fails() {
|
||||||
|
let mut output = serde_cbor::to_vec(&constants::ED25519_BASEPOINT).unwrap();
|
||||||
|
// CBOR apparently has two bytes of overhead for a 32-byte string.
|
||||||
|
// Set the low byte of the compressed point to 1 to make it invalid.
|
||||||
|
output[2] = 1;
|
||||||
|
let parsed: Result<ExtendedPoint,_> = serde_cbor::from_slice(&output);
|
||||||
|
assert!(parsed.is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
@ -1588,7 +1652,7 @@ mod bench {
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
use constants;
|
use constants;
|
||||||
use super::*;
|
use super::*;
|
||||||
use super::test::{A_SCALAR, A_TIMES_BASEPOINT, B_SCALAR};
|
use super::test::{A_SCALAR};
|
||||||
|
|
||||||
#[bench]
|
#[bench]
|
||||||
fn basepoint_mult(b: &mut Bencher) {
|
fn basepoint_mult(b: &mut Bencher) {
|
||||||
|
|
|
||||||
234
src/decaf.rs
234
src/decaf.rs
|
|
@ -24,6 +24,12 @@
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
use digest::Digest;
|
||||||
|
use generic_array::typenum::U32;
|
||||||
|
|
||||||
use constants;
|
use constants;
|
||||||
use field::FieldElement;
|
use field::FieldElement;
|
||||||
use subtle::CTAssignable;
|
use subtle::CTAssignable;
|
||||||
|
|
@ -33,7 +39,9 @@ use core::ops::{Add, Sub, Neg};
|
||||||
use core::ops::{Mul, MulAssign};
|
use core::ops::{Mul, MulAssign};
|
||||||
|
|
||||||
use curve;
|
use curve;
|
||||||
|
use curve::ValidityCheck;
|
||||||
use curve::ExtendedPoint;
|
use curve::ExtendedPoint;
|
||||||
|
use curve::CompletedPoint;
|
||||||
use curve::EdwardsBasepointTable;
|
use curve::EdwardsBasepointTable;
|
||||||
use curve::Identity;
|
use curve::Identity;
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
|
|
@ -108,6 +116,63 @@ impl Identity for CompressedDecaf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Serde support
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Serializes to and from `DecafPoint` directly, doing compression
|
||||||
|
// and decompression internally. This means that users can create
|
||||||
|
// structs containing `DecafPoint`s and use Serde's derived
|
||||||
|
// serializers to serialize those structures.
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{self, Serialize, Deserialize, Serializer, Deserializer};
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::de::Visitor;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl Serialize for DecafPoint {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where S: Serializer
|
||||||
|
{
|
||||||
|
serializer.serialize_bytes(self.compress().as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> Deserialize<'de> for DecafPoint {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where D: Deserializer<'de>
|
||||||
|
{
|
||||||
|
struct DecafPointVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for DecafPointVisitor {
|
||||||
|
type Value = DecafPoint;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||||
|
formatter.write_str("a valid point in Decaf format")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<DecafPoint, E>
|
||||||
|
where E: serde::de::Error
|
||||||
|
{
|
||||||
|
if v.len() == 32 {
|
||||||
|
let arr32 = array_ref!(v,0,32); // &[u8;32] from &[u8]
|
||||||
|
CompressedDecaf(*arr32).decompress()
|
||||||
|
.ok_or(serde::de::Error::custom("decompression failed"))
|
||||||
|
} else {
|
||||||
|
Err(serde::de::Error::invalid_length(v.len(), &self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_bytes(DecafPointVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Internal point representations
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
/// A point in a prime-order group.
|
/// A point in a prime-order group.
|
||||||
///
|
///
|
||||||
/// XXX think about how this API should work
|
/// XXX think about how this API should work
|
||||||
|
|
@ -192,6 +257,140 @@ impl DecafPoint {
|
||||||
, &self.0 + &constants::EIGHT_TORSION[6]
|
, &self.0 + &constants::EIGHT_TORSION[6]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Computes the Elligator map as described in the Decaf paper.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// This method is not public because it's just used for hashing
|
||||||
|
/// to a point -- proper elligator support is deferred for now.
|
||||||
|
fn elligator_decaf_flavour(r_0: &FieldElement) -> DecafPoint {
|
||||||
|
// Follows Appendix C of the Decaf paper.
|
||||||
|
// Use n = 2 as the quadratic nonresidue so that n*x = x + x.
|
||||||
|
|
||||||
|
// 1. Compute r <--- nr_0^2.
|
||||||
|
let r_0_squared = r_0.square();
|
||||||
|
let r = &r_0_squared + &r_0_squared;
|
||||||
|
|
||||||
|
// 2. Compute D <--- (dr + (a-d)) * (dr - (d + ar))
|
||||||
|
let dr = &constants::d * &r;
|
||||||
|
// D = (dr + (a-d)) * (dr - (d + ar)) = (dr + (a-d))*(dr - (d-r)) since a=-1
|
||||||
|
let D = &(&dr + &constants::a_minus_d) * &(&dr - &(&constants::d - &r));
|
||||||
|
|
||||||
|
// 3. Compute N <--- (r+1) * (a-2d)
|
||||||
|
let minus_one = -&FieldElement::one();
|
||||||
|
let N = &(&r + &FieldElement::one()) * &(&minus_one - &constants::d2);
|
||||||
|
|
||||||
|
// 4. Compute
|
||||||
|
// / +1, 1 / sqrt(ND) if ND is square
|
||||||
|
// c, e <--- | +1, 0 if N or D = 0
|
||||||
|
// \ -1, nr_0 / sqrt(nND) otherwise
|
||||||
|
let ND = &N * &D;
|
||||||
|
let nND = &ND + &ND;
|
||||||
|
let mut c = FieldElement::one();
|
||||||
|
let mut e = FieldElement::zero();
|
||||||
|
let (ND_is_nonzero_square, ND_invsqrt) = ND.invsqrt();
|
||||||
|
e.conditional_assign(&ND_invsqrt, ND_is_nonzero_square);
|
||||||
|
let (nND_is_nonzero_square, nND_invsqrt) = nND.invsqrt();
|
||||||
|
let nr_0_nND_invsqrt = &nND_invsqrt * &(r_0 + r_0);
|
||||||
|
c.conditional_assign(&minus_one, nND_is_nonzero_square);
|
||||||
|
e.conditional_assign(&nr_0_nND_invsqrt, nND_is_nonzero_square);
|
||||||
|
|
||||||
|
// 5. Compute s <--- c*|N*e|
|
||||||
|
let mut s = &N * &e;
|
||||||
|
let neg = s.is_negative_decaf();
|
||||||
|
s.conditional_negate(neg);
|
||||||
|
s *= &c;
|
||||||
|
|
||||||
|
// 6. Compute t <--- -c*N*(r-1)* ((a-2d)*e)^2 -1
|
||||||
|
let a_minus_2d_e_sq = (&(&minus_one-&constants::d2)*&e).square();
|
||||||
|
let c_N_r_minus_1 = &c * &(&N * &(&r + &minus_one));
|
||||||
|
let t = &minus_one - &(&c_N_r_minus_1 * &a_minus_2d_e_sq);
|
||||||
|
|
||||||
|
// 7. Apply the isogeny:
|
||||||
|
// (x,y) = ((2s)/(1+as^2), (1-as^2)/(t))
|
||||||
|
let as_sq = &minus_one * &s.square();
|
||||||
|
let P = CompletedPoint{
|
||||||
|
X: &s + &s,
|
||||||
|
Z: &FieldElement::one() + &as_sq,
|
||||||
|
Y: &FieldElement::one() - &as_sq,
|
||||||
|
T: t,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert to extended and return.
|
||||||
|
DecafPoint(P.to_extended())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a `DecafPoint` chosen uniformly at random using a user-provided RNG.
|
||||||
|
///
|
||||||
|
/// # Inputs
|
||||||
|
///
|
||||||
|
/// * `rng`: any RNG which implements the `rand::Rng` interface.
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A random element of the Decaf group.
|
||||||
|
///
|
||||||
|
/// # Implementation
|
||||||
|
///
|
||||||
|
/// Uses the Decaf-flavoured Elligator 2 map, so that the discrete log of the
|
||||||
|
/// output point with respect to any other point should be unknown.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub fn random<T: Rng>(rng: &mut T) -> Self {
|
||||||
|
let mut field_bytes = [0u8; 32];
|
||||||
|
rng.fill_bytes(&mut field_bytes);
|
||||||
|
let r_0 = FieldElement::from_bytes(&field_bytes);
|
||||||
|
DecafPoint::elligator_decaf_flavour(&r_0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hash a slice of bytes into a `DecafPoint`.
|
||||||
|
///
|
||||||
|
/// Takes a type parameter `D`, which is any `Digest` producing 32
|
||||||
|
/// bytes (256 bits) of output.
|
||||||
|
///
|
||||||
|
/// Convenience wrapper around `from_hash`.
|
||||||
|
///
|
||||||
|
/// # Implementation
|
||||||
|
///
|
||||||
|
/// Uses the Decaf-flavoured Elligator 2 map, so that the discrete log of the
|
||||||
|
/// output point with respect to any other point should be unknown.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # extern crate curve25519_dalek;
|
||||||
|
/// # use curve25519_dalek::decaf::DecafPoint;
|
||||||
|
/// extern crate sha2;
|
||||||
|
/// use sha2::Sha256;
|
||||||
|
///
|
||||||
|
/// # // 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";
|
||||||
|
/// let P = DecafPoint::hash_from_bytes::<Sha256>(msg.as_bytes());
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub fn hash_from_bytes<D>(input: &[u8]) -> DecafPoint
|
||||||
|
where D: Digest<OutputSize=U32> + Default {
|
||||||
|
let mut hash = D::default();
|
||||||
|
hash.input(input);
|
||||||
|
DecafPoint::from_hash(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a `DecafPoint` from an existing `Digest` instance.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
pub fn from_hash<D>(hash: D) -> DecafPoint
|
||||||
|
where D: Digest<OutputSize=U32> + Default {
|
||||||
|
// XXX this seems clumsy
|
||||||
|
let mut output = [0u8; 32];
|
||||||
|
output.copy_from_slice(hash.result().as_slice());
|
||||||
|
let r_0 = FieldElement::from_bytes(&output);
|
||||||
|
DecafPoint::elligator_decaf_flavour(&r_0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Identity for DecafPoint {
|
impl Identity for DecafPoint {
|
||||||
|
|
@ -259,6 +458,16 @@ impl<'a, 'b> Mul<&'b Scalar> for &'a DecafPoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Mul<&'b DecafPoint> for &'a Scalar {
|
||||||
|
type Output = DecafPoint;
|
||||||
|
|
||||||
|
/// Scalar multiplication: compute `self * scalar`.
|
||||||
|
fn mul(self, point: &'b DecafPoint) -> DecafPoint {
|
||||||
|
DecafPoint(self * &point.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Precomputation
|
/// Precomputation
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DecafBasepointTable(pub EdwardsBasepointTable);
|
pub struct DecafBasepointTable(pub EdwardsBasepointTable);
|
||||||
|
|
@ -377,10 +586,21 @@ mod test {
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
use constants;
|
use constants;
|
||||||
use curve::CompressedEdwardsY;
|
use curve::CompressedEdwardsY;
|
||||||
use curve::ExtendedPoint;
|
|
||||||
use curve::Identity;
|
use curve::Identity;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde_cbor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn serde_cbor_basepoint_roundtrip() {
|
||||||
|
let output = serde_cbor::to_vec(&constants::DECAF_ED25519_BASEPOINT).unwrap();
|
||||||
|
let parsed: DecafPoint = serde_cbor::from_slice(&output).unwrap();
|
||||||
|
assert_eq!(parsed, constants::DECAF_ED25519_BASEPOINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn decaf_decompress_negative_s_fails() {
|
fn decaf_decompress_negative_s_fails() {
|
||||||
// constants::d is neg, so decompression should fail as |d| != d.
|
// constants::d is neg, so decompression should fail as |d| != d.
|
||||||
|
|
@ -442,6 +662,18 @@ mod test {
|
||||||
assert_eq!(P, Q);
|
assert_eq!(P, Q);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn decaf_random_is_valid() {
|
||||||
|
let mut rng = OsRng::new().unwrap();
|
||||||
|
for _ in 0..100 {
|
||||||
|
let P = DecafPoint::random(&mut rng);
|
||||||
|
// Check that P is on the curve
|
||||||
|
assert!(P.0.is_valid());
|
||||||
|
// Check that P is in the image of the decaf map
|
||||||
|
P.compress();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(test, feature = "bench"))]
|
#[cfg(all(test, feature = "bench"))]
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,11 @@ extern crate arrayref;
|
||||||
extern crate generic_array;
|
extern crate generic_array;
|
||||||
extern crate digest;
|
extern crate digest;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
extern crate serde;
|
||||||
|
#[cfg(all(test, feature = "serde"))]
|
||||||
|
extern crate serde_cbor;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
extern crate core;
|
extern crate core;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,50 @@ impl CTAssignable for Scalar {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{self, Serialize, Deserialize, Serializer, Deserializer};
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::de::Visitor;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl Serialize for Scalar {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where S: Serializer
|
||||||
|
{
|
||||||
|
serializer.serialize_bytes(self.as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
impl<'de> Deserialize<'de> for Scalar {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where D: Deserializer<'de>
|
||||||
|
{
|
||||||
|
struct ScalarVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for ScalarVisitor {
|
||||||
|
type Value = Scalar;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||||
|
formatter.write_str("a 32-byte scalar value")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, v: &[u8]) -> Result<Scalar, E>
|
||||||
|
where E: serde::de::Error
|
||||||
|
{
|
||||||
|
if v.len() == 32 {
|
||||||
|
// array_ref turns &[u8] into &[u8;32]
|
||||||
|
Ok(Scalar(*array_ref!(v,0,32)))
|
||||||
|
} else {
|
||||||
|
Err(serde::de::Error::invalid_length(v.len(), &self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_bytes(ScalarVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Scalar {
|
impl Scalar {
|
||||||
/// Return a `Scalar` chosen uniformly at random using a user-provided RNG.
|
/// Return a `Scalar` chosen uniformly at random using a user-provided RNG.
|
||||||
///
|
///
|
||||||
|
|
@ -827,6 +871,17 @@ mod test {
|
||||||
|
|
||||||
assert_eq!(should_be_X, X);
|
assert_eq!(should_be_X, X);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde_cbor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
fn serde_cbor_scalar_roundtrip() {
|
||||||
|
let output = serde_cbor::to_vec(&X).unwrap();
|
||||||
|
let parsed: Scalar = serde_cbor::from_slice(&output).unwrap();
|
||||||
|
assert_eq!(parsed, X);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(test, feature = "bench"))]
|
#[cfg(all(test, feature = "bench"))]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue