mirror of
https://github.com/saymrwulf/risc0-curve25519-dalek-source.git
synced 2026-09-07 20:30:39 +00:00
Merge branch 'release/0.6.0'
This commit is contained in:
commit
89f12399a3
10 changed files with 3237 additions and 879 deletions
35
.travis.yml
Normal file
35
.travis.yml
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
language: rust
|
||||||
|
|
||||||
|
rust:
|
||||||
|
- stable
|
||||||
|
- beta
|
||||||
|
- nightly
|
||||||
|
|
||||||
|
env:
|
||||||
|
- TEST_COMMAND=test FEATURES='yolocrypto'
|
||||||
|
- TEST_COMMAND=test FEATURES='yolocrypto nightly'
|
||||||
|
- TEST_COMMAND=bench FEATURES='yolocrypto bench'
|
||||||
|
- TEST_COMMAND=bench FEATURES='yolocrypto nightly bench'
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
exclude:
|
||||||
|
# We can probably remove this, as we reasonably expect dalek to work on
|
||||||
|
# stable and beta, but currently we require "test" feature in order to
|
||||||
|
# run benchmarks, which causes dalek not to build on stable. See
|
||||||
|
# https://github.com/isislovecruft/curve25519-dalek/pull/38#issuecomment-286027562
|
||||||
|
- rust: stable
|
||||||
|
env: TEST_COMMAND=bench FEATURES='yolocrypto bench'
|
||||||
|
- rust: beta
|
||||||
|
env: TEST_COMMAND=bench FEATURES='yolocrypto bench'
|
||||||
|
- rust: stable
|
||||||
|
env: TEST_COMMAND=bench FEATURES='yolocrypto nightly bench'
|
||||||
|
- rust: beta
|
||||||
|
env: TEST_COMMAND=bench FEATURES='yolocrypto nightly bench'
|
||||||
|
# Test nightly features, such as radix_51, only on nightly.
|
||||||
|
- rust: stable
|
||||||
|
env: TEST_COMMAND=test FEATURES='yolocrypto nightly'
|
||||||
|
- rust: beta
|
||||||
|
env: TEST_COMMAND=test FEATURES='yolocrypto nightly'
|
||||||
|
|
||||||
|
script:
|
||||||
|
- cargo $TEST_COMMAND --features="$FEATURES"
|
||||||
22
Cargo.toml
22
Cargo.toml
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "curve25519-dalek"
|
name = "curve25519-dalek"
|
||||||
version = "0.5.0"
|
version = "0.6.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"
|
||||||
|
|
@ -15,6 +15,9 @@ exclude = [
|
||||||
".gitignore"
|
".gitignore"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[badges]
|
||||||
|
travis-ci = { repository = "isislovecruft/curve25519-dalek", branch = "master"}
|
||||||
|
|
||||||
[dependencies.arrayref]
|
[dependencies.arrayref]
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
|
||||||
|
|
@ -22,10 +25,26 @@ version = "0.3.3"
|
||||||
optional = true
|
optional = true
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
|
||||||
|
[dependencies.digest]
|
||||||
|
version = "0.4"
|
||||||
|
|
||||||
|
[dependencies.generic-array]
|
||||||
|
# same version that digest depends on
|
||||||
|
version = "^0.6"
|
||||||
|
|
||||||
|
[dev-dependencies.sha2]
|
||||||
|
version = "0.4"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
nightly = ["basepoint_table_creation", "radix_51"]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = ["rand"]
|
std = ["rand"]
|
||||||
yolocrypto = []
|
yolocrypto = []
|
||||||
|
# Needs nightly for placement new
|
||||||
|
basepoint_table_creation = []
|
||||||
|
bench = []
|
||||||
|
# Radix-51 arithmetic using u128
|
||||||
|
radix_51 = []
|
||||||
|
|
||||||
# The development profile, used for `cargo build`.
|
# The development profile, used for `cargo build`.
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|
@ -57,6 +76,7 @@ lto = false
|
||||||
debug-assertions = true
|
debug-assertions = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
panic = 'unwind'
|
panic = 'unwind'
|
||||||
|
required-features = ['yolocrypto']
|
||||||
|
|
||||||
# The benchmarking profile, used for `cargo bench`.
|
# The benchmarking profile, used for `cargo bench`.
|
||||||
[profile.bench]
|
[profile.bench]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
# curve25519-dalek  
|
# curve25519-dalek   
|
||||||
|
|
||||||
**A low-level cryptographic library for point, group, field, and scalar
|
**A low-level cryptographic library for point, group, field, and scalar
|
||||||
operations on a curve isomorphic to the twisted Edwards curve defined by -x²+y²
|
operations on a curve isomorphic to the twisted Edwards curve defined by -x²+y²
|
||||||
|
|
@ -44,12 +44,16 @@ 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.5"
|
curve25519-dalek = "^0.6"
|
||||||
|
|
||||||
Then, in your library or executable source, add:
|
Then, in your library or executable source, add:
|
||||||
|
|
||||||
extern crate curve25519_dalek
|
extern crate curve25519_dalek
|
||||||
|
|
||||||
|
On nightly Rust, using the `nightly` feature enables a radix-51 field
|
||||||
|
arithmetic implementation using `u128`s, which is approximately twice as
|
||||||
|
fast.
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
* Implement hashing to a point on the curve (Elligator).
|
* Implement hashing to a point on the curve (Elligator).
|
||||||
|
|
|
||||||
2163
src/constants.rs
2163
src/constants.rs
File diff suppressed because it is too large
Load diff
838
src/curve.rs
838
src/curve.rs
File diff suppressed because it is too large
Load diff
100
src/decaf.rs
100
src/decaf.rs
|
|
@ -31,7 +31,13 @@ use subtle::CTNegatable;
|
||||||
|
|
||||||
use core::ops::{Add, Sub, Neg};
|
use core::ops::{Add, Sub, Neg};
|
||||||
|
|
||||||
|
#[cfg(all(not(feature = "std"), feature = "basepoint_table_creation"))]
|
||||||
|
use collections::boxed::Box;
|
||||||
|
#[cfg(all(feature = "std", feature = "basepoint_table_creation"))]
|
||||||
|
use std::boxed::Box;
|
||||||
|
|
||||||
use curve::ExtendedPoint;
|
use curve::ExtendedPoint;
|
||||||
|
use curve::EdwardsBasepointTable;
|
||||||
use curve::BasepointMult;
|
use curve::BasepointMult;
|
||||||
use curve::ScalarMult;
|
use curve::ScalarMult;
|
||||||
use curve::Identity;
|
use curve::Identity;
|
||||||
|
|
@ -50,15 +56,15 @@ pub struct CompressedDecaf(pub [u8; 32]);
|
||||||
/// The result of compressing a `DecafPoint`.
|
/// The result of compressing a `DecafPoint`.
|
||||||
impl CompressedDecaf {
|
impl CompressedDecaf {
|
||||||
/// View this `CompressedDecaf` as an array of bytes.
|
/// View this `CompressedDecaf` as an array of bytes.
|
||||||
pub fn to_bytes(&self) -> [u8;32] {
|
pub fn as_bytes<'a>(&'a self) -> &'a [u8;32] {
|
||||||
self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to decompress to an `DecafPoint`.
|
/// Attempt to decompress to an `DecafPoint`.
|
||||||
pub fn decompress(&self) -> Option<DecafPoint> {
|
pub fn decompress(&self) -> Option<DecafPoint> {
|
||||||
// XXX should decoding be CT ?
|
// XXX should decoding be CT ?
|
||||||
// XXX need to check that xy is nonnegative and reject otherwise
|
// XXX need to check that xy is nonnegative and reject otherwise
|
||||||
let s = FieldElement::from_bytes(&self.0);
|
let s = FieldElement::from_bytes(self.as_bytes());
|
||||||
|
|
||||||
// Check that s = |s| and reject otherwise.
|
// Check that s = |s| and reject otherwise.
|
||||||
let mut abs_s = s;
|
let mut abs_s = s;
|
||||||
|
|
@ -71,10 +77,12 @@ impl CompressedDecaf {
|
||||||
let Z = &FieldElement::one() - &ss; // Z = 1+as^2
|
let Z = &FieldElement::one() - &ss; // Z = 1+as^2
|
||||||
let u = &(&Z * &Z) - &(&constants::d4 * &ss); // u = Z^2 - 4ds^2
|
let u = &(&Z * &Z) - &(&constants::d4 * &ss); // u = Z^2 - 4ds^2
|
||||||
let uss = &u * &ss;
|
let uss = &u * &ss;
|
||||||
let mut v = match uss.invsqrt() {
|
|
||||||
Some(v) => v,
|
let (uss_is_nonzero_square, mut v) = uss.invsqrt();
|
||||||
None => return None,
|
if (uss_is_nonzero_square | uss.is_zero()) == 0u8 {
|
||||||
};
|
return None; // us^2 is nonzero nonsquare
|
||||||
|
}
|
||||||
|
|
||||||
// Now v = 1/sqrt(us^2) if us^2 is a nonzero square, 0 if us^2 is zero.
|
// Now v = 1/sqrt(us^2) if us^2 is a nonzero square, 0 if us^2 is zero.
|
||||||
let uv = &v * &u;
|
let uv = &v * &u;
|
||||||
if uv.is_negative_decaf() == 1u8 {
|
if uv.is_negative_decaf() == 1u8 {
|
||||||
|
|
@ -158,9 +166,9 @@ impl DecafPoint {
|
||||||
let Z_plus_Y = &self.0.Z + &Y;
|
let Z_plus_Y = &self.0.Z + &Y;
|
||||||
let Z_minus_Y = &self.0.Z - &Y;
|
let Z_minus_Y = &self.0.Z - &Y;
|
||||||
let t = &constants::a_minus_d * &(&Z_plus_Y * &Z_minus_Y);
|
let t = &constants::a_minus_d * &(&Z_plus_Y * &Z_minus_Y);
|
||||||
|
let (t_is_nonzero_square, mut r) = t.invsqrt();
|
||||||
// t should always be square (why?)
|
// t should always be square (why?)
|
||||||
// XXX is it safe to use option types here?
|
debug_assert_eq!( t_is_nonzero_square | t.is_zero(), 1u8 );
|
||||||
let mut r = t.invsqrt().unwrap();
|
|
||||||
|
|
||||||
// Step 2: Compute u = (a-d)r
|
// Step 2: Compute u = (a-d)r
|
||||||
let u = &constants::a_minus_d * &r;
|
let u = &constants::a_minus_d * &r;
|
||||||
|
|
@ -251,7 +259,7 @@ impl BasepointMult<Scalar> for DecafPoint {
|
||||||
// XXX is this actually in the image of the isogeny,
|
// XXX is this actually in the image of the isogeny,
|
||||||
// or do we need a different basepoint?
|
// or do we need a different basepoint?
|
||||||
fn basepoint() -> DecafPoint {
|
fn basepoint() -> DecafPoint {
|
||||||
DecafPoint(constants::BASEPOINT)
|
DecafPoint(ExtendedPoint::basepoint())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn basepoint_mult(scalar: &Scalar) -> DecafPoint {
|
fn basepoint_mult(scalar: &Scalar) -> DecafPoint {
|
||||||
|
|
@ -259,13 +267,32 @@ impl BasepointMult<Scalar> for DecafPoint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Precomputation
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DecafBasepointTable(EdwardsBasepointTable);
|
||||||
|
|
||||||
|
impl DecafBasepointTable {
|
||||||
|
/// Create a precomputed table of multiples of the given `basepoint`.
|
||||||
|
#[cfg(feature = "basepoint_table_creation")]
|
||||||
|
pub fn create(basepoint: &DecafPoint) -> Box<DecafBasepointTable> {
|
||||||
|
let edwards_table = EdwardsBasepointTable::create(&basepoint.0);
|
||||||
|
box DecafBasepointTable(*edwards_table)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use the precomputed table to quickly compute `scalar * basepoint`
|
||||||
|
pub fn basepoint_mult(&self, scalar: &Scalar) -> DecafPoint {
|
||||||
|
DecafPoint(self.0.basepoint_mult(scalar))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
// Debug traits
|
// Debug traits
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
impl Debug for CompressedDecaf {
|
impl Debug for CompressedDecaf {
|
||||||
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||||
write!(f, "CompressedDecaf: {:?}", &self.0[..])
|
write!(f, "CompressedDecaf: {:?}", self.as_bytes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,7 +314,6 @@ mod test {
|
||||||
|
|
||||||
use scalar::Scalar;
|
use scalar::Scalar;
|
||||||
use constants;
|
use constants;
|
||||||
use constants::BASE_CMPRSSD;
|
|
||||||
use curve::CompressedEdwardsY;
|
use curve::CompressedEdwardsY;
|
||||||
use curve::ExtendedPoint;
|
use curve::ExtendedPoint;
|
||||||
use curve::BasepointMult;
|
use curve::BasepointMult;
|
||||||
|
|
@ -295,40 +321,37 @@ mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
fn decaf_decompress_negative_s_fails() {
|
||||||
fn test_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.
|
||||||
let bad_compressed = CompressedDecaf(constants::d.to_bytes());
|
let bad_compressed = CompressedDecaf(constants::d.to_bytes());
|
||||||
bad_compressed.decompress().unwrap();
|
assert!(bad_compressed.decompress().is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decaf_decompress_id() {
|
fn decaf_decompress_id() {
|
||||||
let compressed_id = CompressedDecaf::identity();
|
let compressed_id = CompressedDecaf::identity();
|
||||||
let id = compressed_id.decompress().unwrap();
|
let id = compressed_id.decompress().unwrap();
|
||||||
// This should compress (as ed25519) to the following:
|
assert_eq!(id.0.compress_edwards(), CompressedEdwardsY::identity());
|
||||||
let mut bytes = [0u8; 32]; bytes[0] = 1;
|
|
||||||
assert_eq!(id.0.compress(), CompressedEdwardsY(bytes));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decaf_compress_id() {
|
fn decaf_compress_id() {
|
||||||
let id = DecafPoint::identity();
|
let id = DecafPoint::identity();
|
||||||
assert_eq!(id.compress(), CompressedDecaf::identity());
|
assert_eq!(id.compress(), CompressedDecaf::identity());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decaf_basepoint_roundtrip() {
|
fn decaf_basepoint_roundtrip() {
|
||||||
let bp_compressed_decaf = DecafPoint::basepoint().compress();
|
let bp_compressed_decaf = DecafPoint::basepoint().compress();
|
||||||
let bp_recaf = bp_compressed_decaf.decompress().unwrap().0;
|
let bp_recaf = bp_compressed_decaf.decompress().unwrap().0;
|
||||||
// Check that bp_recaf differs from bp by a point of order 4
|
// Check that bp_recaf differs from bp by a point of order 4
|
||||||
let diff = &ExtendedPoint::basepoint() - &bp_recaf;
|
let diff = &ExtendedPoint::basepoint() - &bp_recaf;
|
||||||
let diff4 = diff.mult_by_pow_2(4);
|
let diff4 = diff.mult_by_pow_2(4);
|
||||||
assert_eq!(diff4.compress(), ExtendedPoint::identity().compress());
|
assert_eq!(diff4.compress_edwards(), CompressedEdwardsY::identity());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decaf_four_torsion_basepoint() {
|
fn decaf_four_torsion_basepoint() {
|
||||||
let bp = DecafPoint::basepoint();
|
let bp = DecafPoint::basepoint();
|
||||||
let bp_coset = bp.coset4();
|
let bp_coset = bp.coset4();
|
||||||
for i in 0..4 {
|
for i in 0..4 {
|
||||||
|
|
@ -337,7 +360,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decaf_four_torsion_random() {
|
fn decaf_four_torsion_random() {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
let s = Scalar::random(&mut rng);
|
let s = Scalar::random(&mut rng);
|
||||||
let P = DecafPoint::basepoint_mult(&s);
|
let P = DecafPoint::basepoint_mult(&s);
|
||||||
|
|
@ -348,21 +371,32 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_decaf_random_roundtrip() {
|
fn decaf_random_roundtrip() {
|
||||||
let mut rng = OsRng::new().unwrap();
|
let mut rng = OsRng::new().unwrap();
|
||||||
for j in 0..100 {
|
for _ in 0..100 {
|
||||||
let s = Scalar::random(&mut rng);
|
let s = Scalar::random(&mut rng);
|
||||||
let P = DecafPoint::basepoint_mult(&s);
|
let P = DecafPoint::basepoint_mult(&s);
|
||||||
let compressed_P = P.compress();
|
let compressed_P = P.compress();
|
||||||
let Q = compressed_P.decompress().unwrap();
|
let Q = compressed_P.decompress().unwrap();
|
||||||
for i in 0..4 {
|
|
||||||
assert_eq!(P, Q);
|
assert_eq!(P, Q);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test basepoint_mult versus a newly-generated DecafBasepointTable
|
||||||
|
#[test]
|
||||||
|
#[cfg(feature = "basepoint_table_creation")]
|
||||||
|
fn basepoint_mult_vs_decafbasepointtable() {
|
||||||
|
let table = DecafBasepointTable::create(&DecafPoint::basepoint());
|
||||||
|
let mut rng = OsRng::new().unwrap();
|
||||||
|
let s = Scalar::random(&mut rng);
|
||||||
|
let basepoint_mult_s = DecafPoint::basepoint_mult(&s);
|
||||||
|
let table_basepoint_mult_s = table.basepoint_mult(&s);
|
||||||
|
|
||||||
|
assert_eq!(basepoint_mult_s, table_basepoint_mult_s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "bench"))]
|
||||||
mod bench {
|
mod bench {
|
||||||
use rand::OsRng;
|
use rand::OsRng;
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
|
|
|
||||||
792
src/field.rs
792
src/field.rs
File diff suppressed because it is too large
Load diff
23
src/lib.rs
23
src/lib.rs
|
|
@ -9,9 +9,12 @@
|
||||||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
#![no_std]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
#![cfg_attr(not(feature = "std"), feature(collections))]
|
||||||
|
#![cfg_attr(feature = "nightly", feature(box_syntax))]
|
||||||
|
#![cfg_attr(feature = "nightly", feature(i128_type))]
|
||||||
#![allow(unused_features)]
|
#![allow(unused_features)]
|
||||||
#![feature(test)]
|
#![cfg_attr(feature = "bench", feature(test))]
|
||||||
#![deny(missing_docs)] // refuse to compile if documentation is missing
|
#![deny(missing_docs)] // refuse to compile if documentation is missing
|
||||||
|
|
||||||
//! # curve25519-dalek
|
//! # curve25519-dalek
|
||||||
|
|
@ -32,19 +35,27 @@
|
||||||
//! hatred of the Daleks. Rusty destroys the other Daleks and departs the
|
//! hatred of the Daleks. Rusty destroys the other Daleks and departs the
|
||||||
//! ship, determined to track down and bring an end to the Dalek race.
|
//! ship, determined to track down and bring an end to the Dalek race.
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(all(test, feature = "bench"))]
|
||||||
#[macro_use]
|
extern crate test;
|
||||||
extern crate std;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate test;
|
extern crate sha2;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate arrayref;
|
extern crate arrayref;
|
||||||
|
|
||||||
|
extern crate generic_array;
|
||||||
|
extern crate digest;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
extern crate core;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
extern crate rand;
|
extern crate rand;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
extern crate collections;
|
||||||
|
|
||||||
// Modules for low-level operations directly on field elements and curve points.
|
// Modules for low-level operations directly on field elements and curve points.
|
||||||
|
|
||||||
pub mod field;
|
pub mod field;
|
||||||
|
|
|
||||||
119
src/scalar.rs
119
src/scalar.rs
|
|
@ -30,12 +30,15 @@
|
||||||
//! limbs.
|
//! limbs.
|
||||||
|
|
||||||
use core::cmp::{Eq, PartialEq};
|
use core::cmp::{Eq, PartialEq};
|
||||||
use core::ops::{Index, IndexMut};
|
use core::ops::{Neg, Index, IndexMut};
|
||||||
use core::ops::{Neg};
|
use core::fmt::Debug;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
|
use digest::Digest;
|
||||||
|
use generic_array::typenum::U64;
|
||||||
|
|
||||||
use constants;
|
use constants;
|
||||||
use utils::{load3, load4};
|
use utils::{load3, load4};
|
||||||
use subtle::CTAssignable;
|
use subtle::CTAssignable;
|
||||||
|
|
@ -50,6 +53,12 @@ use subtle::arrays_equal_ct;
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Scalar(pub [u8; 32]);
|
pub struct Scalar(pub [u8; 32]);
|
||||||
|
|
||||||
|
impl Debug for Scalar {
|
||||||
|
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
|
||||||
|
write!(f, "Scalar: {:?}", &self.0[..])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Eq for Scalar{}
|
impl Eq for Scalar{}
|
||||||
impl PartialEq for Scalar {
|
impl PartialEq for Scalar {
|
||||||
/// Test equality between two `Scalar`s.
|
/// Test equality between two `Scalar`s.
|
||||||
|
|
@ -159,6 +168,42 @@ impl Scalar {
|
||||||
Scalar::reduce(&scalar_bytes)
|
Scalar::reduce(&scalar_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Hash a slice of bytes into a scalar.
|
||||||
|
///
|
||||||
|
/// Takes a type parameter `D`, which is any `Digest` producing 64
|
||||||
|
/// bytes (512 bits) of output.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// # extern crate curve25519_dalek;
|
||||||
|
/// # use curve25519_dalek::scalar::Scalar;
|
||||||
|
/// extern crate sha2;
|
||||||
|
/// use sha2::Sha512;
|
||||||
|
///
|
||||||
|
/// # // 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 s = Scalar::hash_from_bytes::<Sha512>(msg.as_bytes());
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
pub fn hash_from_bytes<D>(input: &[u8]) -> Scalar
|
||||||
|
where D: Digest<OutputSize=U64> + Default {
|
||||||
|
let mut hash = D::default();
|
||||||
|
hash.input(input);
|
||||||
|
// XXX this seems clumsy
|
||||||
|
let mut output = [0u8;64];
|
||||||
|
output.copy_from_slice(hash.result().as_slice());
|
||||||
|
Scalar::reduce(&output)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// View this `Scalar` as a sequence of bytes.
|
||||||
|
pub fn as_bytes<'a>(&'a self) -> &'a [u8;32] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct the additive identity
|
/// Construct the additive identity
|
||||||
pub fn zero() -> Self {
|
pub fn zero() -> Self {
|
||||||
Scalar([0u8; 32])
|
Scalar([0u8; 32])
|
||||||
|
|
@ -547,45 +592,22 @@ impl UnpackedScalar {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use rand::Rng;
|
|
||||||
use rand::OsRng;
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use test::Bencher;
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_scalar_random(b: &mut Bencher) {
|
|
||||||
let mut csprng: OsRng = OsRng::new().unwrap();
|
|
||||||
|
|
||||||
b.iter(|| Scalar::random(&mut csprng));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_scalar_multiply_add(b: &mut Bencher) {
|
|
||||||
b.iter(|| Scalar::multiply_add(&X, &Y, &Z) );
|
|
||||||
}
|
|
||||||
|
|
||||||
#[bench]
|
|
||||||
fn bench_scalar_unpacked_multiply_add(b: &mut Bencher) {
|
|
||||||
let x = X.unpack();
|
|
||||||
let y = Y.unpack();
|
|
||||||
let z = Z.unpack();
|
|
||||||
b.iter(|| UnpackedScalar::multiply_add(&x, &y, &z) );
|
|
||||||
}
|
|
||||||
|
|
||||||
/// x = 2238329342913194256032495932344128051776374960164957527413114840482143558222
|
/// x = 2238329342913194256032495932344128051776374960164957527413114840482143558222
|
||||||
static X: Scalar = Scalar(
|
pub static X: Scalar = Scalar(
|
||||||
[0x4e, 0x5a, 0xb4, 0x34, 0x5d, 0x47, 0x08, 0x84,
|
[0x4e, 0x5a, 0xb4, 0x34, 0x5d, 0x47, 0x08, 0x84,
|
||||||
0x59, 0x13, 0xb4, 0x64, 0x1b, 0xc2, 0x7d, 0x52,
|
0x59, 0x13, 0xb4, 0x64, 0x1b, 0xc2, 0x7d, 0x52,
|
||||||
0x52, 0xa5, 0x85, 0x10, 0x1b, 0xcc, 0x42, 0x44,
|
0x52, 0xa5, 0x85, 0x10, 0x1b, 0xcc, 0x42, 0x44,
|
||||||
0xd4, 0x49, 0xf4, 0xa8, 0x79, 0xd9, 0xf2, 0x04]);
|
0xd4, 0x49, 0xf4, 0xa8, 0x79, 0xd9, 0xf2, 0x04]);
|
||||||
/// y = 2592331292931086675770238855846338635550719849568364935475441891787804997264
|
/// y = 2592331292931086675770238855846338635550719849568364935475441891787804997264
|
||||||
static Y: Scalar = Scalar(
|
pub static Y: Scalar = Scalar(
|
||||||
[0x90, 0x76, 0x33, 0xfe, 0x1c, 0x4b, 0x66, 0xa4,
|
[0x90, 0x76, 0x33, 0xfe, 0x1c, 0x4b, 0x66, 0xa4,
|
||||||
0xa2, 0x8d, 0x2d, 0xd7, 0x67, 0x83, 0x86, 0xc3,
|
0xa2, 0x8d, 0x2d, 0xd7, 0x67, 0x83, 0x86, 0xc3,
|
||||||
0x53, 0xd0, 0xde, 0x54, 0x55, 0xd4, 0xfc, 0x9d,
|
0x53, 0xd0, 0xde, 0x54, 0x55, 0xd4, 0xfc, 0x9d,
|
||||||
0xe8, 0xef, 0x7a, 0xc3, 0x1f, 0x35, 0xbb, 0x05]);
|
0xe8, 0xef, 0x7a, 0xc3, 0x1f, 0x35, 0xbb, 0x05]);
|
||||||
/// z = 5033871415930814945849241457262266927579821285980625165479289807629491019013
|
/// z = 5033871415930814945849241457262266927579821285980625165479289807629491019013
|
||||||
static Z: Scalar = Scalar(
|
pub static Z: Scalar = Scalar(
|
||||||
[0x05, 0x9d, 0x3e, 0x0b, 0x09, 0x26, 0x50, 0x3d,
|
[0x05, 0x9d, 0x3e, 0x0b, 0x09, 0x26, 0x50, 0x3d,
|
||||||
0xa3, 0x84, 0xa1, 0x3c, 0x92, 0x7a, 0xc2, 0x06,
|
0xa3, 0x84, 0xa1, 0x3c, 0x92, 0x7a, 0xc2, 0x06,
|
||||||
0x41, 0x98, 0xcf, 0x34, 0x3a, 0x24, 0xd5, 0xb7,
|
0x41, 0x98, 0xcf, 0x34, 0x3a, 0x24, 0xd5, 0xb7,
|
||||||
|
|
@ -621,7 +643,7 @@ mod test {
|
||||||
0,0,0,0,0,-15,0,0,0,0,0,15,0,0,0,0,15,0,0,0,0,15,0,0,0,0,0,1,0,0,0,0];
|
0,0,0,0,0,-15,0,0,0,0,0,15,0,0,0,0,15,0,0,0,0,15,0,0,0,0,0,1,0,0,0,0];
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_non_adjacent_form() {
|
fn non_adjacent_form() {
|
||||||
let naf = A_SCALAR.non_adjacent_form();
|
let naf = A_SCALAR.non_adjacent_form();
|
||||||
for i in 0..256 {
|
for i in 0..256 {
|
||||||
assert_eq!(naf[i], A_NAF[i]);
|
assert_eq!(naf[i], A_NAF[i]);
|
||||||
|
|
@ -629,7 +651,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scalar_multiply_by_one() {
|
fn scalar_multiply_by_one() {
|
||||||
let one = Scalar::one();
|
let one = Scalar::one();
|
||||||
let zero = Scalar::zero();
|
let zero = Scalar::zero();
|
||||||
let test_scalar = Scalar::multiply_add(&X, &one, &zero);
|
let test_scalar = Scalar::multiply_add(&X, &one, &zero);
|
||||||
|
|
@ -639,7 +661,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scalar_multiply_only() {
|
fn scalar_multiply_only() {
|
||||||
let zero = Scalar::zero();
|
let zero = Scalar::zero();
|
||||||
let test_scalar = Scalar::multiply_add(&X, &Y, &zero);
|
let test_scalar = Scalar::multiply_add(&X, &Y, &zero);
|
||||||
for i in 0..32 {
|
for i in 0..32 {
|
||||||
|
|
@ -648,7 +670,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scalar_multiply_add() {
|
fn scalar_multiply_add() {
|
||||||
let test_scalar = Scalar::multiply_add(&X, &Y, &Z);
|
let test_scalar = Scalar::multiply_add(&X, &Y, &Z);
|
||||||
for i in 0..32 {
|
for i in 0..32 {
|
||||||
assert!(test_scalar[i] == W[i]);
|
assert!(test_scalar[i] == W[i]);
|
||||||
|
|
@ -656,7 +678,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scalar_reduce() {
|
fn scalar_reduce() {
|
||||||
let mut bignum = [0u8;64];
|
let mut bignum = [0u8;64];
|
||||||
// set bignum = x + 2^256x
|
// set bignum = x + 2^256x
|
||||||
for i in 0..32 {
|
for i in 0..32 {
|
||||||
|
|
@ -677,10 +699,39 @@ mod test {
|
||||||
|
|
||||||
// Negating a scalar twice should result in the original scalar.
|
// Negating a scalar twice should result in the original scalar.
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scalar_neg() {
|
fn scalar_neg() {
|
||||||
let negative_x: Scalar = -X;
|
let negative_x: Scalar = -X;
|
||||||
let orig: Scalar = -negative_x;
|
let orig: Scalar = -negative_x;
|
||||||
|
|
||||||
assert!(orig == X);
|
assert!(orig == X);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(test, feature = "bench"))]
|
||||||
|
mod bench {
|
||||||
|
use rand::OsRng;
|
||||||
|
use test::Bencher;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use super::test::{X, Y, Z};
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn scalar_random(b: &mut Bencher) {
|
||||||
|
let mut csprng: OsRng = OsRng::new().unwrap();
|
||||||
|
|
||||||
|
b.iter(|| Scalar::random(&mut csprng));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn scalar_multiply_add(b: &mut Bencher) {
|
||||||
|
b.iter(|| Scalar::multiply_add(&X, &Y, &Z) );
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bench]
|
||||||
|
fn scalar_unpacked_multiply_add(b: &mut Bencher) {
|
||||||
|
let x = X.unpack();
|
||||||
|
let y = Y.unpack();
|
||||||
|
let z = Z.unpack();
|
||||||
|
b.iter(|| UnpackedScalar::multiply_add(&x, &y, &z) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
16
src/utils.rs
16
src/utils.rs
|
|
@ -9,7 +9,7 @@
|
||||||
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
// - Isis Agora Lovecruft <isis@patternsinthevoid.net>
|
||||||
// - Henry de Valence <hdevalence@hdevalence.ca>
|
// - Henry de Valence <hdevalence@hdevalence.ca>
|
||||||
|
|
||||||
//! Miscellaneous common utility function.
|
//! Miscellaneous common utility functions.
|
||||||
|
|
||||||
/// Convert an array of (at least) three bytes into an i64.
|
/// Convert an array of (at least) three bytes into an i64.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -29,3 +29,17 @@ pub fn load4(input: &[u8]) -> i64 {
|
||||||
| ((input[2] as i64) << 16)
|
| ((input[2] as i64) << 16)
|
||||||
| ((input[3] as i64) << 24)
|
| ((input[3] as i64) << 24)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert an array of (at least) eight bytes into a u64.
|
||||||
|
#[inline]
|
||||||
|
//#[allow(dead_code)]
|
||||||
|
pub fn load8(input: &[u8]) -> u64 {
|
||||||
|
(input[0] as u64)
|
||||||
|
| ((input[1] as u64) << 8)
|
||||||
|
| ((input[2] as u64) << 16)
|
||||||
|
| ((input[3] as u64) << 24)
|
||||||
|
| ((input[4] as u64) << 32)
|
||||||
|
| ((input[5] as u64) << 40)
|
||||||
|
| ((input[6] as u64) << 48)
|
||||||
|
| ((input[7] as u64) << 56)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue