-
Notifications
You must be signed in to change notification settings - Fork 7
Ed25519 support #120
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
base: main
Are you sure you want to change the base?
Ed25519 support #120
Conversation
Ex: `#define foo() bl edwards25519_decode_alt_mul_p25519`
Before this change, the generated rust macro didn't wire up the local label
reference properly:
```rust
macro_rules! foo { () => { Q!(
"bl " Label!("edwards25519_decode_alt_mul_p25519", 0, Before)
)} }
```
After this change:
```rust
macro_rules! foo { () => { Q!(
"bl " Label!("edwards25519_decode_alt_mul_p25519", 4, After)
)} }
```
To support this, we now track which macros reference labels. We then track
which blocks call these macros. As long as all macro callsites are uniformly
before or after the labels they reference, we can safely replace the local label
id and search direction in the original macro definition.
It was challenging to track macro callsites in the main `RustFormatter` and also
difficult to "pre-locate" callsites while dealing with hoisting, so the actual
macro fixing happens on the generated Rust code from the `RustFormatter` pass.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #120 +/- ##
==========================================
+ Coverage 99.14% 99.31% +0.17%
==========================================
Files 171 185 +14
Lines 39165 50337 +11172
==========================================
+ Hits 38830 49994 +11164
- Misses 335 343 +8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
CodSpeed Performance ReportMerging #120 will not alter performanceComparing Summary
Benchmarks breakdown
|
phlip9
left a 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.
Thanks a ton for bringing this over the finish line! And I apologize for the mess of WIP commits 😅
Figured I would leave some comments as a potential user of both rustls-graviola and graviola::signing::eddsa
|
|
||
| pkcs8::Key::decode(bytes, &asn1::oid::id_ed25519, None) | ||
| .and_then(|k| asn1::OctetString::from_bytes(k.private_key()).map_err(Error::Asn1Error)) | ||
| .and_then(|pk| Self::from_bytes(pk.as_octets())) |
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.
would be nice to sanity check that the serialized pubkey in the pkcs8 document matches the derived compressed pubkey
https://github.com/briansmith/ring/blob/main/src/ec/curve25519/ed25519/signing.rs#L136
| pub fn public_key(&self) -> Ed25519VerifyingKey { | ||
| let _entry = Entry::new_secret(); | ||
| Ed25519VerifyingKey(self.0.verifying_key()) | ||
| } |
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 is obviously committing to the signing key containing the decompressed verifying key, but it would be huge if this could just return a &Ed25519VerifyingKey and not have to clear registers:
impl Ed25519SigningKey {
pub fn public_key(&self) -> &Ed25519VerifyingKey {
Ed25519VerifyingKey::from_ref(self.0.verifying_key())
}
}
/// An Ed25519 verification public key.
#[derive(Debug)]
#[repr(transparent)]
pub struct Ed25519VerifyingKey(ed25519::VerifyingKey);
impl Ed25519VerifyingKey {
#[inline]
const fn from_ref(vk: &ed25519::VerifyingKey) -> &Self {
// Assert that both types have the same layout
const _: [(); std::mem::size_of::<Self>()] =
[(); std::mem::size_of::<ed25519::VerifyingKey>()];
const _: [(); std::mem::align_of::<Self>()] =
[(); std::mem::align_of::<ed25519::VerifyingKey>()];
// SAFETY: Both types have the same layout and the newtype uses
// #[repr(transparent)], so their references are equivalent.
// See <https://docs.rs/ref_cast>.
unsafe { &*(vk as *const ed25519::VerifyingKey as *const Self) }
}
}| .try_into() | ||
| .map(|seed| Self(ed25519::SigningKey::from_seed(&seed))) | ||
| .map_err(|_| Error::WrongLength) | ||
| } |
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.
somewhat unfortunate that this fn has to become fallible when in principle it's infallible
pub fn from_seed(bytes: &[u8; 32]) -> Self { /* .. */ }the struggles of API stability 😢
| let _entry = Entry::new_secret(); | ||
| Ed25519VerifyingKey(self.0.verifying_key()) | ||
| } | ||
| } |
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.
likewise, if I can get access to the seed after, that would be amazing:
impl Ed25519SigningKey {
#[inline]
pub fn as_seed(&self) -> &[u8; 32] { /* .. */ }
}this is a problem I have with ring's interface; it doesn't expose the seed, which means I have to duplicate it outside: https://github.com/lexe-app/lexe-public/blob/master/common/src/ed25519.rs#L77
/// An ed25519 secret key and public key.
///
/// Applications should always sign with a *key pair* rather than passing in
/// the secret key and public key separately, to avoid attacks like
/// [attacker controlled pubkey signing](https://github.com/MystenLabs/ed25519-unsafe-libs).
pub struct KeyPair {
/// The ring key pair for actually signing things.
key_pair: ring::signature::Ed25519KeyPair,
/// Unfortunately, [`ring`] doesn't expose the `seed` after construction,
/// so we need to hold on to the seed if we ever need to serialize the key
/// pair later.
seed: [u8; 32],
}|
|
||
| impl SigningKey { | ||
| pub(crate) fn from_seed(seed: &[u8; 32]) -> Self { | ||
| let _entry = low::Entry::new_secret(); |
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 this still need let _entry = low::Entry::new_secret();?
| low::ct::into_public(sig) | ||
| } | ||
|
|
||
| pub(crate) fn verifying_key(&self) -> VerifyingKey { |
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 can probably return a &VerifyingKey now
|
|
||
| let mut sig = [0u8; 64]; | ||
| sig[0..32].clone_from_slice(&sig_r.0); | ||
| sig[32..64].clone_from_slice(&s.to_le_bytes()); |
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.
minor: the compiler specializes this correctly, but .copy_from_slice(..) is less surprising as a reader
| low::zeroise(&mut self.prefix); | ||
| // clearly not necessary, but makes zeroisation easier to test | ||
| low::zeroise(&mut self.verifying_key.point.0); | ||
| low::zeroise(&mut self.verifying_key.bytes); |
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.
minor: could gate zeroising public data on #[cfg(debug_assertions)]
|
|
||
| /// `PureEd25519` signature verification | ||
| pub(crate) fn verify(&self, sig: &[u8; 64], msg: &[u8]) -> Result<(), Error> { | ||
| let _entry = low::Entry::new_public(); |
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.
likewise, does this need entry if it's not a public API?
This is work-in-progress Ed25519 support. Much of the work is thanks to @phlip9 in #74
Draft while I do these items:
fixes #74