Compare commits

...

5 Commits

Author SHA1 Message Date
Luke Parker c14b406197
Merge a46b5be95c into df67e299e6 2023-12-20 11:16:42 -07:00
str4d df67e299e6
Merge pull request #80 from zcash/rfc-process
Point to the RFC process
2023-12-20 16:30:51 +00:00
Jack Grigg 43598cf303 Point to the RFC process 2023-12-19 15:36:50 +00:00
Luke Parker a46b5be95c
Add zeroize 2023-05-18 18:26:06 -04:00
Luke Parker f89331c06e
Optimize scalar multiplication with a 4-bit window
This moves from 255 doubles and 255 additions to 259 doubles and 71 additions.
If doubling is twice as fast, which is roughly the case as far as I can tell,
this shifts the function from executing in (255 + (255 * 2)) = 765 time to
(259 + (71 * 2)) = 401 time, a 48% speedup.
2023-05-18 18:21:34 -04:00
5 changed files with 76 additions and 51 deletions

View File

@ -49,6 +49,7 @@ group = { version = "0.13", default-features = false }
rand = { version = "0.8", default-features = false } rand = { version = "0.8", default-features = false }
static_assertions = "1.1.0" static_assertions = "1.1.0"
subtle = { version = "2.3", default-features = false } subtle = { version = "2.3", default-features = false }
zeroize = { version = "^1.5", default-features = false, features = ["derive"] }
# alloc dependencies # alloc dependencies
blake2b_simd = { version = "1", optional = true, default-features = false } blake2b_simd = { version = "1", optional = true, default-features = false }

View File

@ -4,6 +4,12 @@ This crate provides an implementation of the Pasta elliptic curve constructions,
Pallas and Vesta. More details about the Pasta curves can be found Pallas and Vesta. More details about the Pasta curves can be found
[in this blog post](https://electriccoin.co/blog/the-pasta-curves-for-halo-2-and-beyond/). [in this blog post](https://electriccoin.co/blog/the-pasta-curves-for-halo-2-and-beyond/).
## RFC process
This crate follows the [zkcrypto RFC process](https://zkcrypto.github.io/rfcs/).
If you want to propose "substantial" changes to this crate, please
[create an RFC](https://github.com/zkcrypto/rfcs) for wider discussion.
## [Documentation](https://docs.rs/pasta_curves) ## [Documentation](https://docs.rs/pasta_curves)
## Minimum Supported Rust Version ## Minimum Supported Rust Version

View File

@ -30,7 +30,7 @@ macro_rules! new_curve_impl {
(($($privacy:tt)*), $name:ident, $name_affine:ident, $iso:ident, $base:ident, $scalar:ident, (($($privacy:tt)*), $name:ident, $name_affine:ident, $iso:ident, $base:ident, $scalar:ident,
$curve_id:literal, $a_raw:expr, $b_raw:expr, $curve_type:ident) => { $curve_id:literal, $a_raw:expr, $b_raw:expr, $curve_type:ident) => {
/// Represents a point in the projective coordinate space. /// Represents a point in the projective coordinate space.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug, zeroize::Zeroize)]
#[cfg_attr(feature = "repr-c", repr(C))] #[cfg_attr(feature = "repr-c", repr(C))]
$($privacy)* struct $name { $($privacy)* struct $name {
x: $base, x: $base,
@ -466,32 +466,41 @@ macro_rules! new_curve_impl {
type Output = $name; type Output = $name;
fn mul(self, other: &'b $scalar) -> Self::Output { fn mul(self, other: &'b $scalar) -> Self::Output {
// TODO: make this faster // Create a table out of the point
// TODO: This can be made more efficient with a 5-bit window
let mut acc = $name::identity(); let mut arr = [$name::identity(); 16];
arr[1] = *self;
// This is a simple double-and-add implementation of point for i in 2 .. 16 {
// multiplication, moving from most significant to least arr[i] = if (i % 2) == 0 {
// significant bit of the scalar. arr[i / 2].double()
// } else {
// We don't use `PrimeFieldBits::.to_le_bits` here, because that would arr[i - 1] + arr[1]
// force users of this crate to depend on `bitvec` where they otherwise };
// might not need to.
//
// NOTE: We skip the leading bit because it's always unset (we are turning
// the 32-byte repr into 256 bits, and $scalar::NUM_BITS = 255).
for bit in other
.to_repr()
.iter()
.rev()
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
.skip(1)
{
acc = acc.double();
acc = $name::conditional_select(&acc, &(acc + self), bit);
} }
acc let mut res = $name::identity();
let mut first = true;
// Iterate from significant byte to least significant byte
for byte in other.to_repr().iter().rev() {
// Shift the result over 4 bits
if !first {
for _ in 0 .. 4 {
res = res.double();
}
}
first = false;
// Add the top-nibble from this byte into the result
res += arr[usize::from(byte >> 4)];
// Shift the result over
for _ in 0 .. 4 {
res = res.double();
}
// Add the bottom-nibble from this byte into the result
res += arr[usize::from(byte & 0b1111)];
}
res
} }
} }
@ -581,32 +590,41 @@ macro_rules! new_curve_impl {
type Output = $name; type Output = $name;
fn mul(self, other: &'b $scalar) -> Self::Output { fn mul(self, other: &'b $scalar) -> Self::Output {
// TODO: make this faster // Create a table out of the point
// TODO: This can be made more efficient with a 5-bit window
let mut acc = $name::identity(); let mut arr = [$name::identity(); 16];
arr[1] = (*self).into();
// This is a simple double-and-add implementation of point for i in 2 .. 16 {
// multiplication, moving from most significant to least arr[i] = if (i % 2) == 0 {
// significant bit of the scalar. arr[i / 2].double()
// } else {
// We don't use `PrimeFieldBits::.to_le_bits` here, because that would arr[i - 1] + arr[1]
// force users of this crate to depend on `bitvec` where they otherwise };
// might not need to.
//
// NOTE: We skip the leading bit because it's always unset (we are turning
// the 32-byte repr into 256 bits, and $scalar::NUM_BITS = 255).
for bit in other
.to_repr()
.iter()
.rev()
.flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
.skip(1)
{
acc = acc.double();
acc = $name::conditional_select(&acc, &(acc + self), bit);
} }
acc let mut res = $name::identity();
let mut first = true;
// Iterate from significant byte to least significant byte
for byte in other.to_repr().iter().rev() {
// Shift the result over 4 bits
if !first {
for _ in 0 .. 4 {
res = res.double();
}
}
first = false;
// Add the top-nibble from this byte into the result
res += arr[usize::from(byte >> 4)];
// Shift the result over
for _ in 0 .. 4 {
res = res.double();
}
// Add the bottom-nibble from this byte into the result
res += arr[usize::from(byte & 0b1111)];
}
res
} }
} }

View File

@ -24,7 +24,7 @@ use crate::arithmetic::SqrtTables;
// The internal representation of this type is four 64-bit unsigned // The internal representation of this type is four 64-bit unsigned
// integers in little-endian order. `Fp` values are always in // integers in little-endian order. `Fp` values are always in
// Montgomery form; i.e., Fp(a) = aR mod p, with R = 2^256. // Montgomery form; i.e., Fp(a) = aR mod p, with R = 2^256.
#[derive(Clone, Copy, Eq)] #[derive(Clone, Copy, Eq, zeroize::Zeroize)]
#[repr(transparent)] #[repr(transparent)]
pub struct Fp(pub(crate) [u64; 4]); pub struct Fp(pub(crate) [u64; 4]);

View File

@ -24,7 +24,7 @@ use crate::arithmetic::SqrtTables;
// The internal representation of this type is four 64-bit unsigned // The internal representation of this type is four 64-bit unsigned
// integers in little-endian order. `Fq` values are always in // integers in little-endian order. `Fq` values are always in
// Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256. // Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256.
#[derive(Clone, Copy, Eq)] #[derive(Clone, Copy, Eq, zeroize::Zeroize)]
#[repr(transparent)] #[repr(transparent)]
pub struct Fq(pub(crate) [u64; 4]); pub struct Fq(pub(crate) [u64; 4]);