-
Notifications
You must be signed in to change notification settings - Fork 0
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
Plain ECDSA implementation #21
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,25 +16,38 @@ use crate::{ | |
shares::{NatShares, PointShares, Shares}, | ||
}; | ||
|
||
/// Run a ECDSA protocol | ||
// TODO: remame to threshold instead of plain. | ||
|
||
/// Run a ECDSA protocol with BeDOZa | ||
/// | ||
/// Uses the protocol from Securing DNSSEC Keys via Threshold ECDSA From Generic MPC | ||
/// | ||
/// 1. Sign message | ||
/// 2. Verify signature | ||
/// 3. PROFIT! | ||
pub fn run_ecdsa(message: Nat) { | ||
pub fn run_ecdsa_bedoza(message: Nat) { | ||
// Generate a keys | ||
let (_, sk_shared, pk) = keygen(); | ||
|
||
// Sign message | ||
let signature = sign_message_bedoza(message, sk_shared); | ||
|
||
// Verify signature | ||
assert!(verify_signature(message, signature, pk)); | ||
} | ||
|
||
/// Run a ECDSA protocol without BeDOZa | ||
/// Used for benchmarking | ||
/// Uses the protocol from https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages | ||
#[allow(dead_code)] | ||
pub fn run_ecdsa_plain(message: Nat) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we're only using this for benchmarking, couldn't we just put it in the test module? I.e under
|
||
// Generate a keys | ||
let (sk_shared, pk) = keygen(); | ||
let (sk, _, pk) = keygen(); | ||
|
||
// Sign message | ||
let signature = sign_message(message, sk_shared); | ||
let signature = sign_message_plain(message, sk); | ||
|
||
// Verify signature | ||
assert!(verify_signature(message, signature, pk)); | ||
} | ||
|
||
/// Sign a message | ||
/// Sign a message with BeDOZa | ||
/// | ||
/// Using ABB+ protocol from Securing DNSSEC Keys via Threshold ECDSA From Generic MPC | ||
/// | ||
|
@@ -43,7 +56,7 @@ pub fn run_ecdsa(message: Nat) { | |
/// 2. Generate circuit | ||
/// 3. Evaluate circuit | ||
/// 3. Return signature: (r, s) | ||
fn sign_message(m: Nat, sk_shared: NatShares) -> (Nat, Nat) { | ||
fn sign_message_bedoza(m: Nat, sk_shared: NatShares) -> (Nat, Nat) { | ||
// User independent preprocessing | ||
let preprocessed_tuple = user_independent_preprocessing(&curve::nonzero_order()); | ||
|
||
|
@@ -103,7 +116,7 @@ fn verify_signature(m: Nat, signature: (Nat, Nat), pk: Point) -> bool { | |
/// Generate public key | ||
/// | ||
/// Returns a point on the curve | ||
/// pk = Open(Convert(\[sk\])) | ||
/// `pk = Open(Convert([sk]))` or `pk = sk * G` | ||
fn generate_public_key(sk_shared: NatShares) -> Point { | ||
let sk_convert = PointShares::convert(sk_shared); | ||
let pk = Shares::Point(sk_convert).open().point(); | ||
|
@@ -192,11 +205,11 @@ fn ecdsa_circuit( | |
} | ||
|
||
/// Generate ECDSA keypair where sk is secret shared | ||
fn keygen() -> (NatShares, Point) { | ||
fn keygen() -> (Nat, NatShares, Point) { | ||
let sk = curve::rand_mod_order(); | ||
let sk_shared = NatShares::new(&sk, curve::nonzero_order()); | ||
let pk = generate_public_key(sk_shared.clone()); | ||
(sk_shared, pk) | ||
(sk, sk_shared, pk) | ||
} | ||
|
||
/// Compute H(m) = sha256(m) | ||
|
@@ -211,38 +224,116 @@ fn hash_message(m: Nat) -> Nat { | |
return Nat::from_le_slice(&result[..]); | ||
} | ||
|
||
/// Sign a message with plain ECDSA (no BeDOZa) | ||
/// | ||
/// Based on https://cryptobook.nakov.com/digital-signatures/ecdsa-sign-verify-messages | ||
#[allow(dead_code)] | ||
fn sign_message_plain(message: Nat, sk: Nat) -> (Nat, Nat) { | ||
// Calculate the hash of the message | ||
let h = hash_message(message); | ||
|
||
// Generate a random number k | ||
let k = curve::rand_mod_order(); | ||
|
||
// Calculate random point R = k * G and take its x coordinate r_x = R.x | ||
let r_x = FieldBytesEncoding::decode_field_bytes( | ||
&AffinePoint::from(Point::mul_by_generator( | ||
&curve::Scalar::from_uint_unchecked(k), | ||
)) | ||
.x(), | ||
); | ||
|
||
// Calculate the modular inverse of k | ||
let (k_inv, k_inv_exists) = k.inv_mod(&curve::nonzero_order()); | ||
if !bool::from(k_inv_exists) { | ||
panic!("k inverse does not exist") | ||
} | ||
|
||
// Calculate the signature proof s = (h + r_x * sk) * k_inv | ||
let h_plus_r_x_sk = | ||
mul_mod(&r_x, &sk, &curve::nonzero_order()).add_mod(&h, &curve::nonzero_order()); | ||
let s = mul_mod(&h_plus_r_x_sk, &k_inv, &curve::nonzero_order()); | ||
|
||
(r_x, s) | ||
} | ||
|
||
/// Read message after "ecdsa" from command line arguments | ||
pub fn read_args_message(args: env::Args) -> Nat { | ||
let args: Vec<String> = args.collect(); | ||
let m = Nat::from(args.get(2).unwrap().parse::<u128>().unwrap()); | ||
m | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
mod tests_bedoza { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_run_ecdsa() { | ||
run_ecdsa_bedoza(Nat::from_u16(1337)); | ||
} | ||
|
||
#[test] | ||
fn test_threshold_ecdsa_bedoza_positive() { | ||
// Test that sign/verify of 100 random messages | ||
let mut i = 0; | ||
while i < 100 { | ||
let message = curve::rand_mod_order(); | ||
|
||
let (_, sk_shared, pk) = keygen(); | ||
let s = sign_message_bedoza(message, sk_shared); | ||
assert!(verify_signature(message, s, pk)); | ||
i = i + 1; | ||
} | ||
run_ecdsa_bedoza(Nat::from_u16(1337)); | ||
} | ||
|
||
#[test] | ||
fn test_threshold_ecdsa_bedoza_negative() { | ||
// Test that sign/verify of 100 random messages | ||
// m1 and m2 where m1 != m2 | ||
let mut i = 0; | ||
while i < 100 { | ||
let m1 = curve::rand_mod_order(); | ||
let m2 = curve::rand_mod_order(); | ||
if m1 == m2 { | ||
continue; | ||
} | ||
|
||
let (_, sk_shared, pk) = keygen(); | ||
let s = sign_message_bedoza(m1, sk_shared); | ||
assert!(!verify_signature(m2, s, pk)); | ||
i = i + 1; | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests_plain { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_run_ecdsa() { | ||
run_ecdsa(Nat::from_u16(1337)); | ||
run_ecdsa_plain(Nat::from_u16(1337)); | ||
} | ||
|
||
#[test] | ||
fn test_threshold_ecdsa_positive() { | ||
fn test_threshold_ecdsa_plain_positive() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ... Continuing the comment on the naming: I think of 'threshold signing' as signing using MPC and thus 'test_threshold_ecdsa_plain' doesn't make much sense IMO :) |
||
// Test that sign/verify of 100 random messages | ||
let mut i = 0; | ||
while i < 100 { | ||
let message = curve::rand_mod_order(); | ||
|
||
let (sk_shared, pk) = keygen(); | ||
let s = sign_message(message, sk_shared); | ||
let (sk, _, pk) = keygen(); | ||
let s = sign_message_plain(message, sk); | ||
assert!(verify_signature(message, s, pk)); | ||
i = i + 1; | ||
} | ||
run_ecdsa(Nat::from_u16(1337)); | ||
run_ecdsa_plain(Nat::from_u16(1337)); | ||
} | ||
|
||
#[test] | ||
fn test_threshold_ecdsa_negative() { | ||
fn test_threshold_ecdsa_plain_negative() { | ||
// Test that sign/verify of 100 random messages | ||
// m1 and m2 where m1 != m2 | ||
let mut i = 0; | ||
|
@@ -253,8 +344,8 @@ mod tests { | |
continue; | ||
} | ||
|
||
let (sk_shared, pk) = keygen(); | ||
let s = sign_message(m1, sk_shared); | ||
let (sk, _, pk) = keygen(); | ||
let s = sign_message_plain(m1, sk); | ||
assert!(!verify_signature(m2, s, pk)); | ||
i = i + 1; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not a big fan of this
bedoza
/plain
naming scheme -Can we use
threshold_sign
andsign
instead (I've been keeping this to myself, but I also generally think including names of parameters as part of a function name, such asfn sign_message(message: str)
is an anti-pattern :))? And perhapsrun_threshold_ecdsa
for the runner?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want to spend a lot of energy arguing naming though, so just view this as a suggestion and mark the comment as resolved :)