-
Notifications
You must be signed in to change notification settings - Fork 113
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
Signing and verification traits #7
Conversation
Trait for producing digital signatures
Support for signing and verifying prehashed message `Digest`s, for use with signature algorithms that support Initialize-Update-Finalize usage.
So after IRC discussion the only change for now is to remove SHA traits, right? @dignifiedquire |
@newpavlov yes, we can try completely removing the For posterity, the reason for having separate So for now, I agree, let's try removing them and see if we can get by without them. Worst case, if someone does show up clamoring for the mix-and-match combinations, we can (potentially) add them back, but let's cross that bridge when we get there. |
Hmm I am not entirely sure how this would fit into the requirements for RSA. It needs to express the following things
|
(3) could be solved by extending |
@dignifiedquire for RSA signatures I would suggest instantiating a signer type in your preferred way, then This is more or less where we netted out around the complexities of selecting which hash function to use for ECDSA, which was previously solved by providing a trait for each with a differently named method. Instead of that, we are requiring the signer to decide that a priori. Note that in such a scheme, it is still possible to allow the signer to select from different hash functions to perform on the input. You could either select things a priori at runtime via initializers (e.g. Either way, the point is the incidental complexity around each signature algorithm can be kept out-of-band from the core signature trait. As it were, this is how ring's signing APIs work.
Yep! And really I think "to prehash or not to prehash" is the only decision that actually needs to be handled by the signing traits, as many ECDSA libraries (as well as RSA) only accept a prehashed digest at input, and leave how to calculate that as an exercise to the user. So it's convenient for users of these libraries to be able to leverage I imagine we can do some blanket trait SignUsingDigest {
type Algorithm: Digest;
}
impl<D, S, T> Sign<S> for T
where
D: Digest
S: Signature,
T: SignDigest<D, S> + SignUsingDigest
{
fn sign(&self, msg: &[u8]) -> Result<S, Error> {
self.sign_digest(T::Algorithm::new().chain(msg))
}
} |
I like that, it makes the key pair a bit more complicated, but other than that this should work out. In all use cases I have seen so far a key pair is only used for a single combination of params anyway. |
The only thing I am unsure about then is blinding, as I would like to preserve the ability to pass in an rng every time the method is called, instead of per key pair. |
ECDSA has similar concerns around the secure RNG for nonces, however I guess I have the opposite preference and like only having to configure the RNG once. I'm not sure how it's possible to design a least-common-denominator API which supports passing in an RNG on a per-signature basis, as many signature algorithms are deterministic and don't require one (e.g. Ed25519 or RFC 6979 deterministic ECDSA). What's the use case for doing so? |
I honestly don't have a good use case, this was mostly an intuition I had, and the way I have seen this being handled in other places. If I really want I can always have |
f5e2db8
to
c438c2c
Compare
I removed commit f5e2db8 (SignSha* and VerifySha*) from the PR. Will merge after the test pass. Will submit a followup PR for the blanket impl of |
I wonder if we can do something like this (replace const generics with typenum for now): trait CoreSigner {
const DIGEST_SIZE: usize;
type Sig: Signature;
fn core_sign(&self, msg_digest: &[u8; DIGEST_SIZE]) -> Result<Self::Sig, Error>;
fn digest_sign<D>(&self, msg: &[u8]) -> Result<Self::Sig, Error>
where D: Digest<Output=Self::DIGEST_SIZE>
{
self.core_sign(&D::digest(msg))
}
}
trait Signer<S: Signature>: Send + Sync {
/// Sign the given message and return a digital signature
fn sign(&self, msg: &[u8]) -> Result<S, Error>;
}
struct CoreWrapper<C: CoreSigner, D: Digest> { .. }
impl<C: CoreSigner, D: Digest> CoreSigner for CoreWrapper<C, D> { .. }
impl<C, S, D> Signer<S> for CoreWrapper<C, D>
where C: CoreSigner,
D: Digest,
S: Signature + From<C::Sig>
{
fn sign(&self, msg: &[u8]) -> Result<S, Error> {
let sig = self.digest_sign::<D>(msg)?;
Ok(sig.into())
}
} And same for verification. BTW are you sure about |
@newpavlov I don't think that makes sense. It presupposes all signers will have the ability to sign both unhashed messages and digests, which is not the case (or at least, I feel very strongly about these traits being usable with all signers imaginable, be they HSMs/hardware tokens, cloud KMS services, or any existing Rust crate which may already do hashing internally) It also adds a superfluous method in order to achieve object safety which is redundant with the method in the non-object-safe versions. All in all it seems more complicated and if it has any advantages, I'm failing to see them. I think to reach equivalence with what I have in #9 (as of 2bff7eb), you'd need to add an additional What are you trying to accomplish with this change?
Haha, as it were I used |
No, HSMs which do hashing themselves will implement only Which method is superfluous here in your opinion? I want a clear distinction between algorithms and their levels. Your approach with |
fn core_sign(&self, msg_digest: &[u8; DIGEST_SIZE]) -> Result<Self::Sig, Error>; fn sign(&self, msg: &[u8]) -> Result<S, Error>; I feel like what you're doing is accomplishing less than #9 with a whole lot of added complexity. Just looking at your code I have no idea what a
These are trying to communicate an important property of the underlying signature algorithm, which is that the message-based form of the algorithm is equivalent to computing the digest of afforementioned message with the IUF API, which is a property that does not hold for Ed25519 vs Ed25519ph. I'd suggest reviewing the notes I left about this on #9. They could perhaps use a better name, but I sure had trouble coming up with a proper one to convey that particular idea. |
This PR contains a number of commits adding a set of traits for creating and verifying digital signatures.
I would suggest reviewing them commit-by-commit:
Sign
traitVerify
traitSignDigest
andVerifyDigest
SignSha256
,SignSha384
,SignSha512
,VerifySha256
,VerifySha384
,VerifySha512
All traits are bounded by
Send + Sync
to ensure signers and verifiers are thread safe. Libraries which provide access to HSMs will need to e.g.Mutex
guard access to the underlying device.