Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make static context a feature and allow dynamic context generation #41

Merged
merged 14 commits into from
Mar 27, 2020
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ script:
- cargo build --verbose --all
- cargo test --verbose --all
- cargo build --verbose --no-default-features
- cargo build --verbose --no-default-features --features hmac
- cargo build --verbose --no-default-features --features static-context
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ libsecp256k1-gen-ecmult = { version = "0.1.0", path = "gen/ecmult" }
libsecp256k1-gen-genmult = { version = "0.1.0", path = "gen/genmult" }

[features]
default = ["std", "hmac"]
default = ["std", "hmac", "static-context"]
std = ["libsecp256k1-core/std", "sha2/std", "rand/std", "serde/std", "base64/std"]
hmac = ["hmac-drbg", "sha2", "typenum"]
static-context = []

[workspace]
members = [
Expand Down
229 changes: 219 additions & 10 deletions core/src/ecmult.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use alloc::alloc::{alloc, Layout};
use subtle::Choice;
use crate::group::{Affine, Jacobian, AffineStorage, globalz_set_table_gej};
use crate::group::{
Affine, Jacobian, AffineStorage, globalz_set_table_gej, set_table_gej_var,
AFFINE_G,
};
use crate::field::Field;
use crate::scalar::Scalar;

Expand All @@ -9,18 +13,149 @@ pub const ECMULT_TABLE_SIZE_A: usize = 1 << (WINDOW_A - 2);
pub const ECMULT_TABLE_SIZE_G: usize = 1 << (WINDOW_G - 2);
pub const WNAF_BITS: usize = 256;

fn odd_multiples_table_storage_var(
pre: &mut [AffineStorage],
a: &Jacobian
) {
let mut prej: Vec<Jacobian> = Vec::with_capacity(pre.len());
for _ in 0..pre.len() {
prej.push(Jacobian::default());
}
let mut prea: Vec<Affine> = Vec::with_capacity(pre.len());
for _ in 0..pre.len() {
prea.push(Affine::default());
}
let mut zr: Vec<Field> = Vec::with_capacity(pre.len());
for _ in 0..pre.len() {
zr.push(Field::default());
}

odd_multiples_table(&mut prej, &mut zr, a);
set_table_gej_var(&mut prea, &prej, &zr);

for i in 0..pre.len() {
pre[i] = prea[i].clone().into();
}
}

/// Context for accelerating the computation of a*P + b*G.
pub struct ECMultContext {
pre_g: [AffineStorage; ECMULT_TABLE_SIZE_G],
}

impl ECMultContext {
/// Create a new `ECMultContext`.
pub const fn new(pre_g: [AffineStorage; ECMULT_TABLE_SIZE_G]) -> Self {
/// Create a new `ECMultContext` from raw values.
pub const unsafe fn new_raw(pre_g: [AffineStorage; ECMULT_TABLE_SIZE_G]) -> Self {
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what makes it unsafe? is it due to potential misuse in crypto logic and not in rust terms?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the table is not filled in the correct data of pre_g, then usage of it would be invalid. So yeah it's in crypto logic, but also not sure if it will lead to anything panicking.

Self { pre_g }
}

/// Inspect raw values of `ECMultContext`.
pub fn inspect_raw(&self) -> &[AffineStorage; ECMULT_TABLE_SIZE_G] {
&self.pre_g
}

/// Deconstruct the `ECMultContext` into raw `AffineStorage`.
pub fn deconstruct_raw(self) -> [AffineStorage; ECMULT_TABLE_SIZE_G] {
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
self.pre_g
}

/// Generate a new `ECMultContext` in the heap. Note that this function is expensive.
pub fn new_boxed() -> Box<Self> {
let mut this = unsafe {
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
let ptr = alloc(Layout::new::<ECMultContext>()) as *mut ECMultContext;
let mut this = Box::from_raw(ptr);

for i in 0..ECMULT_TABLE_SIZE_G {
this.pre_g[i] = AffineStorage::default();
}

this
};

let mut gj = Jacobian::default();
gj.set_ge(&AFFINE_G);
odd_multiples_table_storage_var(&mut this.pre_g, &gj);

this
}
}

pub fn set_all_gej_var(a: &[Jacobian]) -> Vec<Affine> {
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
let mut az: Vec<Field> = Vec::with_capacity(a.len());
for i in 0..a.len() {
if !a[i].is_infinity() {
az.push(a[i].z.clone());
}
}
let azi: Vec<Field> = inv_all_var(&az);

let mut ret = Vec::with_capacity(a.len());
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
for _ in 0..a.len() {
ret.push(Affine::default());
}

let mut count = 0;
for i in 0..a.len() {
ret[i].infinity = a[i].infinity;
if !a[i].is_infinity() {
ret[i].set_gej_zinv(&a[i], &azi[count]);
count += 1;
}
}
ret
}

/// Calculate the (modular) inverses of a batch of field
/// elements. Requires the inputs' magnitudes to be at most 8. The
/// output magnitudes are 1 (but not guaranteed to be
/// normalized). The inputs and outputs must not overlap in
/// memory.
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
pub fn inv_all_var(fields: &[Field]) -> Vec<Field> {
if fields.len() == 0 {
return Vec::new();
}

let mut ret = Vec::new();
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
ret.push(fields[0].clone());

for i in 1..fields.len() {
ret.push(Field::default());
ret[i] = &ret[i - 1] * &fields[i];
}

let mut u = ret[fields.len() - 1].inv_var();

for i in (1..fields.len()).rev() {
let j = i;
let i = i - 1;
ret[j] = &ret[i] * &u;
u = &u * &fields[j];
}

ret[0] = u;
ret
}

const GEN_BLIND: Scalar = Scalar(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the un-initiated reader it would be useful to have links to where these numbers come from (if such a canonical source exists; a comment explaining how to generate them if not)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an issue #43. Let me do this documentation work in a separate PR.

[2217680822, 850875797, 1046150361, 1330484644,
4015777837, 2466086288, 2052467175, 2084507480]
);
const GEN_INITIAL: Jacobian = Jacobian {
x: Field::new_raw(
586608, 43357028, 207667908, 262670128, 142222828,
38529388, 267186148, 45417712, 115291924, 13447464
),
y: Field::new_raw(
12696548, 208302564, 112025180, 191752716, 143238548,
145482948, 228906000, 69755164, 243572800, 210897016
),
z: Field::new_raw(
3685368, 75404844, 20246216, 5748944, 73206666,
107661790, 110806176, 73488774, 5707384, 104448710
),
infinity: false,
};

/// Context for accelerating the computation of a*G.
pub struct ECMultGenContext {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe dumb q but does this have to be pub?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Library users need it when they use dynamic context. Something like this:

let context: Box<ECMultGenContext> = ECMultGenContext::new_boxed();
let public = PublicKey::from_secret_key_with_context(&secret_key, &context);

prec: [[AffineStorage; 16]; 64],
Expand All @@ -29,13 +164,87 @@ pub struct ECMultGenContext {
}

impl ECMultGenContext {
/// Create a new `ECMultGenContext`.
pub const fn new(
prec: [[AffineStorage; 16]; 64],
blind: Scalar,
initial: Jacobian
) -> Self {
Self { prec, blind, initial }
/// Create a new `ECMultGenContext` from raw values.
pub const unsafe fn new_raw(prec: [[AffineStorage; 16]; 64]) -> Self {
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
Self { prec, blind: GEN_BLIND, initial: GEN_INITIAL }
}

/// Inspect `ECMultGenContext` values.
pub fn inspect_raw(&self) -> &[[AffineStorage; 16]; 64] {
&self.prec
}

/// Deconstruct `ECMultGenContext` into raw values.
pub fn deconstruct_raw(self) -> [[AffineStorage; 16]; 64] {
self.prec
}

/// Generate a new `ECMultGenContext` in the heap. Note that this function is expensive.
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
pub fn new_boxed() -> Box<Self> {
let mut this = unsafe {
sorpaas marked this conversation as resolved.
Show resolved Hide resolved
let ptr = alloc(Layout::new::<ECMultGenContext>()) as *mut ECMultGenContext;
let mut this = Box::from_raw(ptr);

for j in 0..64 {
for i in 0..16 {
this.prec[j][i] = AffineStorage::default();
}
}

this.blind = GEN_BLIND;
this.initial = GEN_INITIAL;

this
};

let mut gj = Jacobian::default();
gj.set_ge(&AFFINE_G);

// Construct a group element with no known corresponding scalar (nothing up my sleeve).
let mut nums_32 = [0u8; 32];
debug_assert!("The scalar for this x is unknown".as_bytes().len() == 32);
for (i, v) in "The scalar for this x is unknown".as_bytes().iter().enumerate() {
nums_32[i] = *v;
}
let mut nums_x = Field::default();
debug_assert!(nums_x.set_b32(&nums_32));
let mut nums_ge = Affine::default();
debug_assert!(nums_ge.set_xo_var(&nums_x, false));
let mut nums_gej = Jacobian::default();
nums_gej.set_ge(&nums_ge);
nums_gej = nums_gej.add_ge_var(&AFFINE_G, None);

// Compute prec.
let mut precj: Vec<Jacobian> = Vec::with_capacity(1024);
for _ in 0..1024 {
precj.push(Jacobian::default());
}
let mut gbase = gj.clone();
let mut numsbase = nums_gej.clone();
for j in 0..64 {
precj[j*16] = numsbase.clone();
for i in 1..16 {
precj[j*16 + i] = precj[j*16 + i - 1].add_var(&gbase, None);
}
for _ in 0..4 {
gbase = gbase.double_var(None);
}
numsbase = numsbase.double_var(None);
if j == 62 {
numsbase = numsbase.neg();
numsbase = numsbase.add_var(&nums_gej, None);
}
}
let prec = set_all_gej_var(&precj);

for j in 0..64 {
for i in 0..16 {
let pg: AffineStorage = prec[j*16 + i].clone().into();
this.prec[j][i] = pg;
}
}

this
}
}

Expand Down
7 changes: 2 additions & 5 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
//! Pure Rust implementation of the secp256k1 curve and fast ECDSA
//! signatures. The secp256k1 curve is used exclusively in Bitcoin and
//! Ethereum alike cryptocurrencies.
//! Core libraries for libsecp256k1.
#![deny(unused_import_braces, unused_imports,
unused_comparisons, unused_must_use,
unused_variables, non_shorthand_field_patterns,
unreachable_code, unused_parens)]

#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;

#[macro_use]
mod field;
Expand All @@ -20,8 +19,6 @@ mod ecmult;
mod ecdsa;
mod ecdh;

extern crate alloc;

pub use crate::error::Error;

/// Curve related structs.
Expand Down
37 changes: 4 additions & 33 deletions gen/ecmult/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,10 @@
use std::{io::{Write, Error}, fs::File};
use libsecp256k1_core::curve::{Jacobian, Field, AffineStorage, Affine, AFFINE_G};
use libsecp256k1_core::util::{odd_multiples_table, ECMULT_TABLE_SIZE_G,
set_table_gej_var};

fn odd_multiples_table_storage_var(pre: &mut [AffineStorage],
a: &Jacobian) {
let mut prej: Vec<Jacobian> = Vec::with_capacity(pre.len());
for _ in 0..pre.len() {
prej.push(Jacobian::default());
}
let mut prea: Vec<Affine> = Vec::with_capacity(pre.len());
for _ in 0..pre.len() {
prea.push(Affine::default());
}
let mut zr: Vec<Field> = Vec::with_capacity(pre.len());
for _ in 0..pre.len() {
zr.push(Field::default());
}

odd_multiples_table(&mut prej, &mut zr, a);
set_table_gej_var(&mut prea, &prej, &zr);

for i in 0..pre.len() {
pre[i] = prea[i].clone().into();
}
}
use libsecp256k1_core::curve::ECMultContext;

pub fn generate_to(file: &mut File) -> Result<(), Error> {
let mut gj = Jacobian::default();
gj.set_ge(&AFFINE_G);
let mut pre_g = Vec::with_capacity(ECMULT_TABLE_SIZE_G);
for _ in 0..ECMULT_TABLE_SIZE_G {
pre_g.push(AffineStorage::default());
}
odd_multiples_table_storage_var(&mut pre_g, &gj);
let context = ECMultContext::new_boxed();
let pre_g = context.inspect_raw().as_ref();

file.write_fmt(format_args!("["))?;
for pg in pre_g {
file.write_fmt(
Expand Down
Loading