-
Notifications
You must be signed in to change notification settings - Fork 12
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
Generic EC #186
base: master
Are you sure you want to change the base?
Generic EC #186
Conversation
…e::Scalar. So this goes back to the old `Scalar(BackendScalar)` type and makes it generic over the curve.
Patch sec1 crate v0.7.3 to impl ModulusSize for U8
Make Point generic over scheme params
@@ -5,13 +5,15 @@ use manul::{ | |||
dev::{run_sync, BinaryFormat, TestSessionParams, TestSigner, TestVerifier}, | |||
signature::Keypair, | |||
}; | |||
use rand_core::OsRng; | |||
use synedrion::{AuxGen, AuxInfo, InteractiveSigning, KeyInit, KeyShare, TestParams}; | |||
use primeorder::FieldBytes; |
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.
We only need primeorder
for this type, and it's just a type alias, perhaps we can just define it ourselves.
pub(crate) const ORDER: U256 = Secp256k1::ORDER; | ||
|
||
impl HashableType for Curve { | ||
impl HashableType for k256::Secp256k1 { |
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.
Can it be defined generically for any Curve
implementor?
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 problem here was that there's a blanket impl that conflicts? But maybe I'm wrong. I'll try!
let bytes_lo = GenericArray::<_, FieldBytesSize<P::Curve>>::from_slice(&bytes_lo); | ||
let bytes_hi = reader.read_boxed(Self::repr_len()); | ||
let bytes_hi = GenericArray::<_, FieldBytesSize<P::Curve>>::from_slice(&bytes_hi); | ||
let uint_lo = <P::Curve as Curve>::Uint::decode_field_bytes(bytes_lo); |
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.
Does that work with non-reduced data? Feels like this should be crypto_bigint::Encoding:from_be_bytes()
and not something specific to ECC
let uint_lo = <P::Curve as Curve>::Uint::decode_field_bytes(bytes_lo); | ||
let uint_hi = <P::Curve as Curve>::Uint::decode_field_bytes(bytes_hi); | ||
let wide_uint = uint_lo.concat(&uint_hi); | ||
// TODO: When the elliptic curve stack upgrades to crypto-bigint v0.6 we can use RemMixed and |
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.
You can use crypto-bigint 0.6
(the one we use for Paillier, for example) all the way and in the last line convert it to the old Uint
, like in params::convert_uint()
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.
Hm, but then you'll probably need to convert ORDER
too...
pub fn from_reduced_bytes(bytes: &[u8; 32]) -> Self { | ||
let arr = GenericArray::<u8, FieldBytesSize<Secp256k1>>::from(*bytes); | ||
Self(<BackendScalar as Reduce<U256>>::reduce_bytes(&arr)) | ||
pub fn from_reduced_bytes(bytes: impl AsRef<[u8]>) -> Self { |
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 wondering if this should reduce from a double-size uint as well.
@@ -25,7 +26,10 @@ use crate::{ | |||
/// - Safe serialization/deserialization (down to `serde` API; what happens there we cannot control) | |||
pub(crate) struct Secret<T: Zeroize>(SecretBox<T>); | |||
|
|||
impl<T: Zeroize> Secret<T> { | |||
impl<T> Secret<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.
Btw, do you use some tool to move the type bounds into the where
clause? I would like to make it consistent throughout the codebase, but I couldn't find a lint in clippy
or rustfmt
setting that would do that.
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 it only works as a one-off thing, mapped to a key combo I have to press for each spot in the code. Having a way to do it across the code base would be real nice. Like, when I write code it's often convenient to type the bounds directly, but when reading code it's much easier (for me) to scan for the where
clause; so automation would work well there for me.
@@ -83,31 +99,42 @@ impl Polynomial { | |||
} | |||
|
|||
#[derive(Debug, Clone, Serialize, Deserialize)] | |||
pub(crate) struct PublicPolynomial(Vec<Point>); | |||
#[serde(bound(deserialize = "for<'x> P: Deserialize<'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.
Strange that you need an explicit bound for Deserialize
but not for Serialize
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.
Agreed. See other comment.
@@ -175,7 +175,6 @@ impl HasWide for U4096 { | |||
type Wide = U8192; | |||
} | |||
|
|||
// TODO(dp): Suggest crypto-bigint update nlimbs! macro. |
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.
We can probably bring back nlimbs!
usage, it doesn't produce clippy warnings anymore.
synedrion/src/www02/bip32.rs
Outdated
for tweak in tweaks { | ||
public_key = public_key.derive_child(*tweak)?; | ||
} | ||
let offset = bip32::KEY_SIZE - <C as Curve>::FieldBytesSize::USIZE; |
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 sure I understand how this works. bip32::KEY_SIZE = 32
, <C as Curve>::FieldBytesSize::USIZE
is 32 for k256
or 24 for tiny-curve
. Then the length of public_key.to_bytes()
is 33, with all of these bytes being meaningful for k256
. Then we just drop the first byte (the sign of y
) and it works as a SEC1 representation?
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.
Also the question above about unwrapping applies here as well - I don't think you need to de/serialize at all.
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 me spell out my reasoning and you can shoot it down!
- For
k256
the offset is0
, sobytes.get(offset..)
will be the whole slice of 33 bytes yes? Should behave the same as the old code. - For
TinyCurve64
the offset is8
, so in this case we truncate the high 8 bytes. This is likely not ok for a proper SEC1 repr, but the premise here is that BIP32 isn't really defined for anything other than secp256k1 so it's unclear what "valid" would even mean.
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.
Hm, perhaps it does work, but in any case it's not necessary - we can avoid serialization entirely, see the other comment.
synedrion/src/www02/bip32.rs
Outdated
tweakable_sk = tweakable_sk.derive_child(*tweak)?; | ||
} | ||
let bytes = tweakable_sk.to_bytes(); | ||
let bytes = bytes.get(32 - Scalar::<P>::repr_len()..).ok_or(bip32::Error::Decode)?; |
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.
Wouldn't it be easier to just unwrap SecretTweakable
into SecretKey
, via another method in the SecretTweakable
trait?
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.
Can you elaborate a bit on this (and "I don't think you need to de/serialize at all" above), not sure how you mean!
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.
See fjarri@fa15322 (naming is rough, bounds may be adjusted, and perhaps some arguments can be taken by value). Basically, you have a trait to convert VerifyingKey<C>
to something bip32
accepts, so I just add another method to convert that something back to VerifyingKey<C>
, which we can do either by just returning the same object (for k256
) or unwrapping the newtype (for tiny-curve
). Same for SigningKey
.
pub trait SchemeParams: | ||
'static + Debug + Clone + Send + PartialEq + Eq + Send + Sync + Ord + Copy + Serialize + for<'x> Deserialize<'x> | ||
where | ||
<Self::Curve as CurveArithmetic>::ProjectivePoint: FromEncodedPoint<Self::Curve>, |
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.
Some of the bounds look like they could be removed. The Copy
ones, for example.
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 assume you mean Copy
on SchemeParams
? Yeah I was on the fence on that, and tried both with and without; in the end I think that the reduced noise all over the code base from having it be Copy
is worth it.
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.
No, the Copy
ones in the where
clause. The Copy
on SchemeParams
is reasonable, I think clippy will complain if it's not present.
} | ||
|
||
/// ZK proof: Paillier Affine Operation with Group Commitment in Range. | ||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
#[serde(bound(deserialize = "for<'x> P: Deserialize<'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.
It's kind of weird to see these bounds everywhere. I thought about dealing with #10 by bounding SchemeParams
on Serialize
/Deserialize
, which you did, but I would expect that to eliminate the need for the explicit bounds?
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 stuff was driving me nuts. I cannot tell you why this works or if there are other/better ways of doing it. It was by far the most frustrating thing about this work. You add a bound in one place and it typechecks, but then breaks somewhere else, you fix it over there in a way that makes sense and now it breaks somewhere else...
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.
Filed serde-rs/serde#2899. These bounds can be avoided if we don't bound SchemeParams
on Deserialize
, but then other explicit bounds will be needed - but we already had those, so maybe that's the best course of action.
synedrion/src/www02/bip32.rs
Outdated
fn derive_verifying_key_bip32(&self, derivation_path: &DerivationPath) -> Result<VerifyingKey<C>, bip32::Error>; | ||
} | ||
|
||
mod sealed { |
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.
What's up with sealing? Do these traits show up in the docs otherwise?
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 idea was to lock down the types for which it's ok to impl them, not really about hiding from docs. You think it's overkill? They have to be pub because the types they are impl'd for are pub.
Yes, that looks correct, I added a comment in Also, there are three remaining comments mentioning |
Make
synderion
generic over the elliptic curve type.Very noisy PR, but the gist of it is that we make the
SchemeParams
trait agnostic over the elliptic curve used and thread that change across the code base while removing all explicit dependencies onk256
.Introduces a new generic parameter
P: SchemeParams
in many places, hence most of the noise.The main areas to consider during review are:
#[serde(bound(deserialize(STUFF))]
are necessary or even why they work beyond "serde magic". Not great.HashOutput
associated type onSchemeParams
should be considered temporary. The goal is to remove the need for it and then we can remove it entirely.WideCurveUint
associated type onSchemeParams
is there to allow reduced bias when generating scalars from extendable hashes. It's not great to have to do this, but I have found no better way. :/One outstanding question not addressed here is how to tweak the
TestParams
for optimal speed. ForTinyCurve64
these settings seem to work:…along with a
PRIME_BITS
setting of256
.Closes #27.