-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Conversation
@@ -48,6 +48,11 @@ impl Secret { | |||
Secret { inner: h } | |||
} | |||
|
|||
/// Creates zero key, which is invalid for crypto operations, but valid for math operation. | |||
pub fn zero() -> Self { | |||
Secret { inner: Default::default() } |
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.
Didn't wanted to implement Default
for Secret
, since it actually produces invalid Secret
.
let mut key_secret = self.to_secp256k1_secret()?; | ||
let other_secret = other.to_secp256k1_secret()?; | ||
key_secret.add_assign(&SECP256K1, &other_secret)?; | ||
match (self.is_zero(), other.is_zero()) { |
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.
The reason behind these changes is that starting with this PR, I need to handle 0 EC scalars.
0 is the invalid key in secp256k1 && library will return an error when trying to work with it.
Long story short: it wasn't a best idea to use Secret
for EC arithmetic (the initial assumption was that it will minimize U256
<-> Secret
conversions).
And the general solution is either to find&use/create separate EC-arithmetic library.
If you're strong against these changes - please comment && I'll close this PR && will switch to separate type for EC calculations first.
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.
not strongly against these changes, but down the road it could be nice to have a separate type for secp256k1 scalars which has std::ops
traits implemented for it.
fn run_reciprocal_protocol(t: usize, artifacts: &KeyGenerationArtifacts) -> Vec<Secret> { | ||
// === Given a secret x mod r which is shared among n players, it is | ||
// === required to generate shares of 1/x mod r with out revealing | ||
// === any information about x or 1/x. |
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.
nit: can we frame that as a multiplicative inverse instead of 1/x
?
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.
Sure :) It is just easier to write 1/x
instead of multiplicative inverse of x
. Did inv(x)
sounds good, or it is better to write its 'full name'?
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.
inv(x)
is fine for me
if let Some(mut secret_required) = secret_required { | ||
for polynom1 in polynoms1.iter_mut().take(n - 1) { | ||
let secret_coeff1 = generate_random_scalar().unwrap(); | ||
secret_required.sub(&secret_coeff1).unwrap(); |
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.
is that tested to wrap around?
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.
Thanks for reviewing! Yes - it will wrap around in case of underflow. Please see run_zero_key_generation
- 0 is passed here as secret_required
=> any non-zero value (which is generated by generate_random_scalar
) will cause underflow => yes it is tested.
Did an initial review of the code and it looks fine. I'll dive into the math tomorrow. |
@rphmeier Thanks for the link!!! I'll read this paper on the weekend, but iirc last time I was searching for |
So the key generation scheme in this paper differs from what we currently use (at first glance). Since the most of SS functionality is based on current KG scheme (like Enc/Dec + migration), I'll opt for using proposed (in this PR) signature generation prototype now (I was asked several times for this feature) + detailed review (and possibly including it to the SS [TODO for myself: research if shares migration is possible in this scheme]) of your paper later. |
Found some issue with |
Turned out to be issue with
|
@rphmeier could you review this pr again? I don't feel qualified to review it without diving into math |
let scalar: U256 = hash.into(); | ||
let scalar: H256 = (scalar % math::curve_order()).into(); | ||
let scalar = Secret::from_slice(&*scalar); | ||
scalar.check_validity()?; |
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.
any secret in 0..curve_order
is valid though, right?
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.
yes. except for 0 itself
|
||
/// Compute R part of ECDSA signature. | ||
#[cfg(test)] | ||
pub fn compute_ecdsa_r(nonce_public: &Public) -> Result<Secret, Error> { |
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 think this is better explained as x_coordinate(point)
than as "r", which requires diving a bit more into implementations of ecdsa
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've added public_x
and public_y
(and used public_x
in this function), but left compute_ecdsa_r
- it is easier to think of R
and S
parts in ECDSA session (next PR). I.e. leave the fact that R is actually a point.x
behind 'math-wall'.
let mut signature_v = { | ||
let nonce_public_x = &nonce_public[0..32]; | ||
let nonce_public_x: U256 = nonce_public_x.into(); | ||
let nonce_public_x: H256 = nonce_public_x.into(); |
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.
let nonce_public_x = H256::from_slice(&nonce_public[0..32])
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.
haven't dived too deep into the math, but the logic around interpolation of inv(nonce)
and the s
value seems fine
debug_assert!(id_numbers.len() >= double_t + 1); | ||
debug_assert_eq!(signature_s_shares.len(), id_numbers.len()); | ||
|
||
compute_joint_secret_from_shares(double_t, |
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.
This interpolation doesn't seem to be possible with only t
participants (as it's polluted by the shares of the z
blinding factor).
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.
Sorry - don't get it - whether this is an issue, or a question :) Interpolation is not possible with t+1
participants - yes. iirc, the reason is that two t-degree polynoms are multiplied when inv(nonce)
is calculated => it requires 2*t+1
points to interpolate.
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.
yes, but if running compute_ecdsa_s
is part of a t-of-n scheme then I don't understand how it could ever complete (relevant to my other comment)
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.
or have I got it backwards somehow, and it's actually a 2t+1-of-n scheme because we have to interpolate such polynomials?
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.
signature_s_shares
are the shares of S in (2*t+1)-of-N
scheme. So what compute_ecdsa_s
does is exactly interpolating 2*t-degree polynomial.
let mul_shares = run_multiplication_protocol(t, &artifacts.secret_shares, &nonce_inv_shares); | ||
|
||
// compute shares for s portion of signature: nonce_inv * (message_hash + secret * signature_r) | ||
// every node broadcasts this share |
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.
if each node's participation (or at least 2t + 1) is required to interpolate the polynomial here, then can it really be considered a t-of-n scheme?
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.
Replaced with 2*t+1
(it is actually compute_ecdsa_s
who take care of this, but it might be confusing to see n in this test)
It is a test of crypto-math behind possible implementation of ECDSA-compatible SS session.
The only known limitation is that it requires 2 * t <= N. I.e. it isn't possible to make ECDSA signature when key was generated, for example 4-of-6 session.
Workaround is to temporarily (for the duration of this session) decrease key threshold (see #7562 for PoC). But we need to agree on this (it is a part of #7271 discussion, actually).
So if this PR will be merged before #7271 is reviewed, I'll start adding ECDSA session without automatic threshold decreasing && wait until this solution will be reviewed.