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

generalize-curve: move CurvePoint behind a trait #557

Merged
merged 49 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
4ba9dfc
generalize-curve: move CurvePoint behind a trait
Dec 29, 2024
7d9a8f0
eduardo: generalize-scalar
Dec 29, 2024
1599528
Solve stack overflow in presign (round 3)
emmorais Jan 7, 2025
72ec5c8
Update src/curve.rs
emmorais Jan 7, 2025
0ca9991
Remove ignored tag from temporarily failing tests
emmorais Jan 7, 2025
d275d8e
Fmt
emmorais Jan 7, 2025
4865dc6
Clippy
emmorais Jan 7, 2025
2eac94f
Make CI pass with generalized Scalar
emmorais Jan 7, 2025
7b6cfe7
Propagate a bit more
emmorais Jan 7, 2025
5b8152f
Propagate to keyshare_export
emmorais Jan 7, 2025
c853d57
More propagation of generic scalar
emmorais Jan 7, 2025
80f922d
Propagate to auxinfo
emmorais Jan 7, 2025
4685d7a
Propagate to broadcast
emmorais Jan 8, 2025
114aca8
Renaming
emmorais Jan 8, 2025
19c14af
Merge pull request #558 from boltlabs-inc/generalize-scalar
naure Jan 8, 2025
0869ac1
Generalize VerifyingKey and Signature (not compiling yet)
emmorais Jan 14, 2025
8be7c75
Use verify_signature instead of verify_digest
emmorais Jan 15, 2025
5d9482e
generalize-curve: Fix signature.deref()
Jan 21, 2025
1d32d15
Generalize plusminus_challenge_from_transcript
emmorais Jan 21, 2025
0856b24
Merge branch 'main' into generalize-curve
emmorais Jan 22, 2025
35e96b7
Merge and generalize thread example
emmorais Jan 22, 2025
bfcfe26
Remove unneeded Phantoms
emmorais Jan 22, 2025
4f3f268
Generalize RecoveryID
emmorais Jan 22, 2025
a5d71ca
Move CurvePoint to the new k256 module
emmorais Jan 22, 2025
56e0454
Rename CurvePoint to K256
emmorais Jan 22, 2025
206df3b
Implement CT, ST, VKT and SignatureTrait for P256
emmorais Jan 22, 2025
ebc6186
Generalize part of the testing code
emmorais Jan 23, 2025
4714e05
P256 passing threshold test, but failing HD wallet
emmorais Jan 24, 2025
3c70ba8
Generalize CT::random
emmorais Jan 27, 2025
f5e8cae
Move k256 code from curve.rs to k256.rs
emmorais Jan 27, 2025
a9ebefc
Minimal change to test the other curve
emmorais Jan 27, 2025
2dfcacc
Generalize basic end-to-end tests
emmorais Jan 27, 2025
bf2fef2
Rename traits to be more descriptive
emmorais Jan 28, 2025
e22e35c
Rename TestCT to TestCurve
emmorais Jan 28, 2025
b0811ae
Rename TestST to TestScalar
emmorais Jan 28, 2025
f7f9160
Small adjustments
emmorais Jan 28, 2025
b6685d1
Generalize the Recovery ID
emmorais Jan 28, 2025
601f072
Adjustments, renaming and TODO
emmorais Jan 28, 2025
17ae6a6
CI passing for P256 (HW wallet restricted to K256)
emmorais Jan 29, 2025
d3f081d
Renaming
emmorais Jan 29, 2025
f21556f
Uncomment ckd test vector (only used for K256)
emmorais Jan 29, 2025
2f3e4f2
Remove unneeded TODO
emmorais Jan 29, 2025
1b208ea
Minor adjsutments
emmorais Feb 3, 2025
72f8b7c
More adjustments
emmorais Feb 3, 2025
ee44288
Remove old unnecessary TODOs
emmorais Feb 3, 2025
9b391d9
Renaming
emmorais Feb 3, 2025
c1caf86
Move static lifetime to CurveTrait
emmorais Feb 3, 2025
9d8db28
Remove unneeded static lifetime
emmorais Feb 3, 2025
baa4b52
Finish moving static to CurveTrait
emmorais Feb 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ flamer = { version = "0.3", optional = true }
generic-array = "0.14"
hex = "0.4"
k256 = { version = "0.13", features = ["arithmetic", "sha256", "ecdsa", "serde"] }
p256 = { version = "0.13.2", features = ["arithmetic", "sha256", "ecdsa", "serde"] }
lazy_static = "1"

# libpaillier depends on `unknown_order` which in turn depends on `rug`.
Expand Down
21 changes: 16 additions & 5 deletions benches/e2e_benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use rand::{prelude::IteratorRandom, rngs::OsRng, CryptoRng, Rng, RngCore};
use std::collections::HashMap;
use tss_ecdsa::{
auxinfo::AuxInfoParticipant,
curve::TestCurve,
errors::Result,
keygen::KeygenParticipant,
messages::Message,
Expand Down Expand Up @@ -106,25 +107,30 @@ fn run_benchmarks_for_given_size(c: &mut Criterion, num_players: usize) {
let keygen_sid = Identifier::random(&mut rng);
let keygen_inputs = std::iter::repeat(()).take(num_players).collect::<Vec<_>>();
c.bench_function(&format!("Keygen with {num_players} nodes"), |b| {
b.iter(|| run_subprotocol::<KeygenParticipant>(keygen_sid, keygen_inputs.clone()))
b.iter(|| {
run_subprotocol::<KeygenParticipant<TestCurve>>(keygen_sid, keygen_inputs.clone())
})
});

// Benchmark auxinfo
let auxinfo_sid = Identifier::random(&mut rng);
let auxinfo_inputs = std::iter::repeat(()).take(num_players).collect::<Vec<_>>();
c.bench_function(&format!("Auxinfo with {num_players} nodes"), |b| {
b.iter(|| run_subprotocol::<AuxInfoParticipant>(auxinfo_sid, auxinfo_inputs.clone()))
b.iter(|| {
run_subprotocol::<AuxInfoParticipant<TestCurve>>(auxinfo_sid, auxinfo_inputs.clone())
})
});

// Prepare to benchmark presign:
// 1. Run keygen and get outputs
let keygen_inputs = std::iter::repeat(()).take(num_players).collect();
let keygen_outputs = run_subprotocol::<KeygenParticipant>(keygen_sid, keygen_inputs).unwrap();
let keygen_outputs =
run_subprotocol::<KeygenParticipant<TestCurve>>(keygen_sid, keygen_inputs).unwrap();

// 2. Run auxinfo and get outputs
let auxinfo_inputs = std::iter::repeat(()).take(num_players).collect();
let auxinfo_outputs =
run_subprotocol::<AuxInfoParticipant>(auxinfo_sid, auxinfo_inputs).unwrap();
run_subprotocol::<AuxInfoParticipant<TestCurve>>(auxinfo_sid, auxinfo_inputs).unwrap();

// 3. Assemble presign input from keygen and auxinfo.
let presign_inputs = auxinfo_outputs
Expand All @@ -138,7 +144,12 @@ fn run_benchmarks_for_given_size(c: &mut Criterion, num_players: usize) {
// Benchmark presign
let presign_identifier = Identifier::random(&mut rng);
c.bench_function(&format!("Presign with {num_players} nodes"), |b| {
b.iter(|| run_subprotocol::<PresignParticipant>(presign_identifier, presign_inputs.clone()))
b.iter(|| {
run_subprotocol::<PresignParticipant<TestCurve>>(
presign_identifier,
presign_inputs.clone(),
)
})
});
}

Expand Down
49 changes: 25 additions & 24 deletions examples/threaded_example/threaded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use tracing::{debug, info, instrument, span, trace, Level};
use tracing_subscriber::{self, EnvFilter};
use tss_ecdsa::{
auxinfo::{self, AuxInfoParticipant},
curve::{CurveTrait, TestCurve},
keygen::{self, KeygenParticipant, Output},
messages::Message,
presign::{self, PresignParticipant},
Expand Down Expand Up @@ -183,7 +184,7 @@ fn main() -> anyhow::Result<()> {
worker_messages.insert(config.id(), from_coordinator_tx);

let outgoing = outgoing_tx.clone();
thread::spawn(|| participant_worker(config, from_coordinator_rx, outgoing));
thread::spawn(|| participant_worker::<TestCurve>(config, from_coordinator_rx, outgoing));
}

// Coordinator initiates entire protocol.
Expand Down Expand Up @@ -330,26 +331,26 @@ fn threshold_participants(mut pids: Vec<ParticipantIdentifier>) -> Vec<Participa
}

/// Worker participating in tss-ecdsa protocols.
struct Worker {
struct Worker<C: CurveTrait + 'static> {
/// Configuration for this participant.
config: ParticipantConfig,
/// Stored participants executed by this worker.
participants: ParticipantStorage,
/// Outputs of successful key generation.
key_gen_material: StoredOutput<KeygenParticipant>,
key_gen_material: StoredOutput<KeygenParticipant<C>>,
/// Outputs of successful aux info.
aux_info: StoredOutput<AuxInfoParticipant>,
aux_info: StoredOutput<AuxInfoParticipant<C>>,
/// Outputs of successful tshare.
tshares: StoredOutput<TshareParticipant>,
tshares: StoredOutput<TshareParticipant<C>>,
/// Outputs of successful presign.
presign_records: StoredOutput<PresignParticipant>,
presign_records: StoredOutput<PresignParticipant<C>>,
/// Signatures generated from successful signing runs.
signatures: StoredOutput<SignParticipant>,
signatures: StoredOutput<SignParticipant<C>>,
/// Channel for sending messages to the coordinator.
outgoing: Sender<MessageFromWorker>,
}

impl Worker {
impl<C: CurveTrait> Worker<C> {
/// Create new worker.
fn new(config: ParticipantConfig, outgoing: Sender<MessageFromWorker>) -> Self {
Worker {
Expand Down Expand Up @@ -422,16 +423,16 @@ impl Worker {

/// Sub-protocol wrappers around `new_sub_protocol`.
/// These functions fetch the required inputs from storage.
impl Worker {
impl<C: CurveTrait> Worker<C> {
fn new_keygen(&mut self, sid: SessionId, key_id: KeyId) -> anyhow::Result<()> {
self.new_sub_protocol::<KeygenParticipant>(self.config.clone(), sid, (), key_id)
self.new_sub_protocol::<KeygenParticipant<C>>(self.config.clone(), sid, (), key_id)
}

fn new_auxinfo(&mut self, sid: SessionId, key_id: KeyId) -> anyhow::Result<()> {
// Note: Missing inputs to aux-info see issues
// #242 and #243.
let _output: &Output = self.key_gen_material.retrieve(&key_id);
self.new_sub_protocol::<AuxInfoParticipant>(self.config.clone(), sid, (), key_id)
let _output: &Output<C> = self.key_gen_material.retrieve(&key_id);
self.new_sub_protocol::<AuxInfoParticipant<C>>(self.config.clone(), sid, (), key_id)
}

fn new_tshare(&mut self, sid: SessionId, key_id: KeyId) -> anyhow::Result<()> {
Expand All @@ -443,14 +444,14 @@ impl Worker {
let auxinfo_output = self.aux_info.retrieve(&key_id);

let inputs = tshare::Input::new(auxinfo_output.clone(), Some(private_share), THRESHOLD)?;
self.new_sub_protocol::<TshareParticipant>(self.config.clone(), sid, inputs, key_id)
self.new_sub_protocol::<TshareParticipant<C>>(self.config.clone(), sid, inputs, key_id)
}

/// Pick a quorum of participants. Return the corresponding additive shares.
fn make_quorum(
&self,
key_id: KeyId,
) -> anyhow::Result<(ParticipantConfig, keygen::Output, auxinfo::Output)> {
) -> anyhow::Result<(ParticipantConfig, keygen::Output<C>, auxinfo::Output)> {
let participants = threshold_participants(self.config.all_participants());

let keygen_output = self.key_gen_material.retrieve(&key_id);
Expand All @@ -477,7 +478,7 @@ impl Worker {
let (config, keygen_quorum, auxinfo_quorum) = self.make_quorum(key_id)?;

let inputs = presign::Input::new(auxinfo_quorum, keygen_quorum)?;
self.new_sub_protocol::<PresignParticipant>(config, sid, inputs, key_id)
self.new_sub_protocol::<PresignParticipant<C>>(config, sid, inputs, key_id)
}

fn new_sign(&mut self, sid: SessionId, key_id: KeyId) -> anyhow::Result<()> {
Expand All @@ -486,14 +487,14 @@ impl Worker {
let record = self.presign_records.take(&key_id);

let inputs = sign::Input::new(b"hello world", record, key_shares.to_vec(), THRESHOLD, None);
self.new_sub_protocol::<SignParticipant>(config, sid, inputs, key_id)
self.new_sub_protocol::<SignParticipant<C>>(config, sid, inputs, key_id)
}
}

/// Sub-protocol wrappers around `process_message` method.
impl Worker {
impl<C: CurveTrait> Worker<C> {
fn process_keygen(&mut self, sid: SessionId, incoming: Message) -> anyhow::Result<()> {
let (p, key_id) = self.participants.get_mut::<KeygenParticipant>(&sid);
let (p, key_id) = self.participants.get_mut::<KeygenParticipant<C>>(&sid);
Self::process_message(
p,
key_id,
Expand All @@ -504,17 +505,17 @@ impl Worker {
}

fn process_auxinfo(&mut self, sid: SessionId, incoming: Message) -> anyhow::Result<()> {
let (p, key_id) = self.participants.get_mut::<AuxInfoParticipant>(&sid);
let (p, key_id) = self.participants.get_mut::<AuxInfoParticipant<C>>(&sid);
Self::process_message(p, key_id, incoming, &mut self.aux_info, &self.outgoing)
}

fn process_tshare(&mut self, sid: SessionId, incoming: Message) -> anyhow::Result<()> {
let (p, key_id) = self.participants.get_mut::<TshareParticipant>(&sid);
let (p, key_id) = self.participants.get_mut::<TshareParticipant<C>>(&sid);
Self::process_message(p, key_id, incoming, &mut self.tshares, &self.outgoing)
}

fn process_presign(&mut self, sid: SessionId, incoming: Message) -> anyhow::Result<()> {
let (p, key_id) = self.participants.get_mut::<PresignParticipant>(&sid);
let (p, key_id) = self.participants.get_mut::<PresignParticipant<C>>(&sid);
Self::process_message(
p,
key_id,
Expand All @@ -525,21 +526,21 @@ impl Worker {
}

fn process_sign(&mut self, sid: SessionId, incoming: Message) -> anyhow::Result<()> {
let (p, key_id) = self.participants.get_mut::<SignParticipant>(&sid);
let (p, key_id) = self.participants.get_mut::<SignParticipant<C>>(&sid);
Self::process_message(p, key_id, incoming, &mut self.signatures, &self.outgoing)
}
}

/// Function to drive work for the workers. These workers execute in their
/// own thread.
#[instrument(skip_all)]
fn participant_worker(
fn participant_worker<C: CurveTrait + 'static>(
config: ParticipantConfig,
from_coordinator: Receiver<MessageFromCoordinator>,
outgoing: Sender<MessageFromWorker>,
) -> anyhow::Result<()> {
info!("Worker thread started.");
let mut worker = Worker::new(config, outgoing);
let mut worker: Worker<C> = Worker::new(config, outgoing);
let mut current_subprotocol: HashMap<SessionId, SubProtocol> = Default::default();

for incoming in from_coordinator {
Expand Down
18 changes: 11 additions & 7 deletions src/auxinfo/auxinfo_commit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

use crate::{
auxinfo::{info::AuxInfoPublic, participant::AuxInfoParticipant},
curve::CurveTrait,
errors::{InternalError, Result},
messages::{AuxinfoMessageType, Message, MessageType},
parameters::PRIME_BITS,
Expand All @@ -18,7 +19,7 @@ use libpaillier::unknown_order::BigNumber;
use merlin::Transcript;
use rand::{CryptoRng, RngCore};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::{fmt::Debug, marker::PhantomData};
use tracing::{error, instrument};

/// The commitment produced by [`CommitmentScheme`].
Expand All @@ -42,7 +43,7 @@ impl Commitment {
/// to send decommit to a validly produced [`Commitment`] one needs to send
/// [`CommitmentScheme`] itself.
#[derive(Serialize, Deserialize, Clone)]
pub(crate) struct CommitmentScheme {
pub(crate) struct CommitmentScheme<C: CurveTrait> {
/// A unique session identifier (`ssid` in the paper).
sid: Identifier,
/// This participant's [`ParticipantIdentifier`] (`i` in the paper).
Expand All @@ -60,9 +61,11 @@ pub(crate) struct CommitmentScheme {
/// This randomness is to ensure that the hash-based commitment is properly
/// randomized.
commit_randomness: [u8; 32],
/// Phantom data to ensure that the commitment scheme is curve-agnostic.
_phantom: PhantomData<C>,
}

impl Debug for CommitmentScheme {
impl<C: CurveTrait> Debug for CommitmentScheme<C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// Redacting randomness and commit_randomness because I'm not sure how
// sensitive they are. If later analysis suggests they're fine to print,
Expand All @@ -77,7 +80,7 @@ impl Debug for CommitmentScheme {
}
}

impl CommitmentScheme {
impl<C: CurveTrait + 'static> CommitmentScheme<C> {
/// Construct a new [`CommitmentScheme`] using the provided unique session
/// [`Identifier`], [`AuxInfoParticipant`], and [`AuxInfoPublic`].
///
Expand All @@ -86,7 +89,7 @@ impl CommitmentScheme {
/// [`AuxInfoParticipant`].
pub(crate) fn new<R: RngCore + CryptoRng>(
sid: Identifier,
auxinfo_participant: &AuxInfoParticipant,
auxinfo_participant: &AuxInfoParticipant<C>,
public_key: AuxInfoPublic,
rng: &mut R,
) -> Result<Self> {
Expand All @@ -109,6 +112,7 @@ impl CommitmentScheme {
randomness: rid,
commit_randomness: u_i,
public_key,
_phantom: PhantomData,
})
}

Expand All @@ -117,10 +121,10 @@ impl CommitmentScheme {
/// This method verifies all the internal [`CommitmentScheme`] values.
pub(crate) fn from_message(
message: &Message,
context: &<AuxInfoParticipant as InnerProtocolParticipant>::Context,
context: &<AuxInfoParticipant<C> as InnerProtocolParticipant>::Context,
) -> Result<Self> {
message.check_type(MessageType::Auxinfo(AuxinfoMessageType::R2Decommit))?;
let scheme: CommitmentScheme = deserialize!(&message.unverified_bytes)?;
let scheme: CommitmentScheme<C> = deserialize!(&message.unverified_bytes)?;

// Public parameters in this decommit must be consistent with each other...
scheme.clone().public_key.verify(context)?;
Expand Down
Loading