Skip to content

Use ed25519 + signature interop crates#102

Closed
tarcieri wants to merge 1 commit intodalek-cryptography:masterfrom
tarcieri:ed25519-crate
Closed

Use ed25519 + signature interop crates#102
tarcieri wants to merge 1 commit intodalek-cryptography:masterfrom
tarcieri:ed25519-crate

Conversation

@tarcieri
Copy link
Copy Markdown
Contributor

The ed25519 crate and the signature crates provide trait-based interoperability between Ed25519 libraries:

https://github.com/RustCrypto/signatures

This commit integrates the ed25519::Signature type, and changes the existing sign and verify methods to use the Signer and Verifier traits from the signature crate. Additionally, it replaces the current error types with an opaque one from the signature crate.

This has the drawback of requiring the Signer and Verifier traits are in scope in order to create signatures, but the benefit of interoperable support for other Ed25519 signers and verifiers. Presently
there are a number of these provided through the Signatory framework, along with support for HSM-backed Ed25519 signatures in the yubihsm crate:

@tarcieri
Copy link
Copy Markdown
Contributor Author

tarcieri commented Nov 12, 2019

I think this still is a bit rough around the edges. This is partly due to the crates being named ed25519 and signature, and those clashing with the existing module names. This is a bit easier to deal with in the 2018 edition module system, and I think it might be good to do a 2018 edition upgrade before landing this.

I'll try to leave some line notes about the parts I don't like.

Ideally I think we could get rid of the existing (private) signature module, and re-export the signature crate.

Comment thread src/errors.rs Outdated
@@ -9,49 +9,6 @@

//! Errors which may occur when parsing keys and/or signatures to or from wire formats.
Copy link
Copy Markdown
Contributor Author

@tarcieri tarcieri Nov 12, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is perhaps the most invasive change in the current implementation: it eliminates the InternalError type entirely, and uses the opaque signature::Error type as SignatureError. Additionally, it drops failure, and uses the equivalent std::error::Error trait when available.

It's still possible to support InternalError using the current signature crate via its Error::source feature, but only when std is enabled:

https://docs.rs/signature/1.0.0-pre.1/signature/struct.Error.html#method.from_source

However, as noted in the docs above, the signature crate discourages this to avoid sidechannel leakage that might allow the attacker to either recover a private key or forge a signature.

If you'd prefer to keep the InternalError type, I can use Error::from_source to capture it, and expose it as Error::source, but only when the std feature is enabled (since it uses Box to store it internally, and exposes it through the std::error::Error trait)

Comment thread src/signature.rs
pub use ed25519_crate::{Signature, signature::Signature as SignatureTrait};

/// Extension trait for the `Signature` type providing access to the
/// `R` and `s` components of the signature.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the ed25519 crate provides the ed25519::Signature type (which internally is a 64-byte "octet string"), I opted to define an extension trait to provide the previous behavior. Let me know what you think of this approach. Alternatively I think this code could be factored to the relevant call sites.

Comment thread src/ed25519.rs
}
}

impl Signer<Signature> for Keypair {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main interop trait for signing. It's generic around the return value (ed25519::Signature, in this case)

Comment thread src/ed25519.rs

impl Signer<Signature> for Keypair {
/// Sign a message with this keypair's secret key.
fn try_sign(&self, message: &[u8]) -> Result<Signature, SignatureError> {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This trait provides an infallible sign method impl'd in terms of try_sign, which exists to support e.g. HSMs which can have communication errors while signing.

In the case of this library, since signing is always infallible, it just returns Ok

Comment thread src/ed25519.rs
}
}

impl Verifier<Signature> for Keypair {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the main interop trait for signature verification

Comment thread src/signature.rs
}
}

#[cfg(feature = "serde")]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ed25519 crate provides a serde feature, but it uses the bag-of-bytes representation of a signature, and needed some more invasive changes which I didn't complete as part of the initial PR.

@tarcieri
Copy link
Copy Markdown
Contributor Author

Looks like the tests are failing due to serde support, which I didn't finish in the initial PR. Will circle back on that.

Comment thread src/signature.rs
#[derive(Copy, Eq, PartialEq)]
pub struct Signature {
// Re-export the `ed25519` crate's Signature type
pub use ed25519_crate::{Signature, signature::Signature as SignatureTrait};
Copy link
Copy Markdown
Contributor Author

@tarcieri tarcieri Nov 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really dislike naming a trait *Trait, but it was necessary here to avoid a naming collision (partially exacerbated by the pre-2018 module system).

Fortunately importing it is rarely needed: the only methods the itself trait provides are from_bytes and as_slice, with equivalent functionality available through TryFrom<&[u8]> and AsRef<[u8]> impls. Otherwise its main function is to provide a set of other trait bounds.

Ideally I think the signature module could be eliminated and the signature crate publicly re-exported at the toplevel. That would allow this trait to be accessed as:

use ed25519_dalek::signature::Signature as _;

(I'm all ears on suggested alternative naming if that still seems confusing)

Copy link
Copy Markdown
Contributor

@nickray nickray Dec 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put the trait in a src/signature/prelude.rs maybe? Although I personally find that "prelude" naming pretentious, and have "traits" submodules usually. In code this reads as Signature: traits::Signature, in docs sometimes as just Signature: Signature.

@nickray
Copy link
Copy Markdown
Contributor

nickray commented Dec 17, 2019

I think instead of forcing the "bag of bytes" on ed25519-dalek, either ed25519 should stop specifying the internal representation, or adopt the more mathematical approach from here and specify compressed point / scalar components.

EDIT: see #80 for a lengthy discussion on the mutual merits.

The `ed25519` crate and the `signature` crates provide trait-based
interoperability between Ed25519 libraries:

https://github.com/RustCrypto/signatures

This commit integrates the `ed25519::Signature` type, and changes the
existing `sign` and `verify` methods to use the `Signer` and `Verifier`
traits from the `signature` crate. Additionally, it replaces the current
error types with an opaque one from the `signature` crate.

This has the drawback of requiring the `Signer` and `Verifier` traits
are in scope in order to create signatures, but the benefit of
interoperable support for other Ed25519 signers and verifiers. Presently
there are a number of these provided through the Signatory framework,
along with support for HSM-backed Ed25519 signatures in the `yubihsm`
crate:

- https://github.com/tendermint/signatory
- https://github.com/tendermint/yubihsm-rs
@tarcieri
Copy link
Copy Markdown
Contributor Author

tarcieri commented Mar 8, 2020

Rebased off master, and updated to ed25519 crate v1.0.0-pre.2

Looks like there's still one remaining bug with serde serialization:

failures:

---- serialisation::serialize_deserialize_signature stdout ----
thread 'serialisation::serialize_deserialize_signature' panicked at 'assertion failed: `(left == right)`
  left: `ed25519::Signature([10, 126, 151, 143, 157, 64, 47, 1, 196, 140, 179, 58, 226, 152, 18, 102, 160, 123, 80, 16, 210, 86, 196, 28, 53, 231, 12, 157, 169, 19, 158, 63, 45, 154, 238, 7, 53, 185, 227, 229, 79, 108, 213, 80, 124, 252, 84, 167, 216, 85, 134, 144, 129, 149, 41, 81, 63, 120, 126, 100, 92, 59, 50, 11])`,
 right: `ed25519::Signature([64, 0, 0, 0, 0, 0, 0, 0, 10, 126, 151, 143, 157, 64, 47, 1, 196, 140, 179, 58, 226, 152, 18, 102, 160, 123, 80, 16, 210, 86, 196, 28, 53, 231, 12, 157, 169, 19, 158, 63, 45, 154, 238, 7, 53, 185, 227, 229, 79, 108, 213, 80, 124, 252, 84, 167, 216, 85, 134, 144, 129, 149, 41, 81])`', tests/ed25519.rs:251:9

Comment thread src/signature.rs

#[cfg(not(feature = "legacy_compatibility"))]
#[inline(always)]
fn check_scalar(bytes: [u8; 32]) -> Result<Scalar, SignatureError> {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was unfortunate to lose in the rebase, and perhaps there's something we can still do yet to retain it, but note that the check_scalar check above (for the highest 3 bits being unset) was adapted into the ed25519 crate:

https://github.com/RustCrypto/signatures/blob/master/ed25519/src/lib.rs#L104

@tarcieri
Copy link
Copy Markdown
Contributor Author

tarcieri commented Mar 9, 2020

I'm not really happy with the state of this PR after rebasing, as it seems a lot of recent work was lost in the rebase.

So I think I'll redo it from scratch, and try to find a less invasive way of implementing it which preserves all of the current functionality.

@tarcieri tarcieri closed this Mar 9, 2020
@tarcieri tarcieri deleted the ed25519-crate branch March 9, 2020 16:49
@tarcieri
Copy link
Copy Markdown
Contributor Author

Opened a new, less invasive version of this PR in #124

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants