-
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
Add "raw digest" signature marker trait and sign/verify traits #10
Conversation
Introduces the notion of a "raw digest" signature algorithm, i.e. any algorithm where signatures are always computed as `S(H(m)))` where: - `S`: signature algorithm - `H`: hash (a.k.a. digest) function - `m`: message Notably this does not hold true for Ed25519, which hashes the input message twice in an effort to remain secure even in the event of collisions in the underlying hash function. However, it supports a separate IUF mode Ed25519ph, which is trivial for any Ed25519 implementation to implement (it hashes the message in advance, then performs a regular Ed25519 signature albeit with a domain separation tweak). For cases like this, where the IUF mode is deliberately domain separated from the normal mode, it would be nice to avoid a blanket impl which assumes all signature algorithms are composed as `S(H(m))`. To accomplish this, this commit adds a `RawDigestSignature` marker trait to be applied at the algorithm level to the corresponding signature trait. For signature types marked as `RawDigestSignature`, it's possible to `impl` a corresponding `SignRawDigest` trait for which a blanket `impl` of `Sign` exists which hashes the message with the corresponding `Digest` algorithm. Additionally, `SignDigest` is blanket impl'd for all `SignRawDigest` types.
@newpavlov I think this accomplishes what you were trying to do, which was to wrap a non-object-trait ala 80f518a with an object safe one, and use the former for the blanket impls so they could be constrained by the associated type. I think this approach solves everything elegantly with simple trait composition, has clearer concepts it uses in the trait bounds, and best of all hoists the relevant marker trait up to the signature algorithm level so it can be solved once per algorithm. |
If there aren't any objections, I'd like to merge this |
Sorry for the delay! I wanted to think more carefully about this PR on this weekend. I have some questions, which I think we can discuss in IRC. |
@newpavlov sounds good |
where | ||
D: Digest, | ||
S: Signature + RawDigestSignature, | ||
T: SignRawDigest<S, Digest = D>, |
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.
@newpavlov here is where the Digest
associated type from SignRawDigest
is used. It's needed to constrain the blanket impl.
@newpavlov I'll admit this still feels a bit weird. Let me briefly spell out the concerns I'm trying to juggle here:
Alternative 1: proc macrosOne alternative I can think of to the approach in this PR is to add
This is quite flexible and keeps the public facing API simple (just Alternative 2: move associated type to the signature traitAnother is to move the It means that |
Actually I like alternative 2 so much I'm going to close this and try again 😉 |
I would expect two different types for two different signature algorithms, even if they are jsut a slightly different.
I think it shouldn't be done on traits side. A better approach will be to use default type parameters, something like this: struct SignAlg<D: Digest<Output=U32> = Sha256> { .. } |
Presently The same also goes for supporting multiple digest algorithms per type. Then the type can be generic around the digest function.
Using a trait means the problem can be solved once at an algorithm level (e.g. in the It also means the blanket impl "Just Works" on signature algorithms that are constructed as |
Introduces the notion of a "raw digest" signature algorithm, i.e. any algorithm where signatures are always computed as
S(H(m)))
where:S
: signature algorithmH
: hash (a.k.a. digest) functionm
: messageNotably this does not hold true for Ed25519, which hashes the input message twice in an effort to remain secure even in the event of collisions in the underlying hash function.
However, it supports a separate IUF mode Ed25519ph, which is trivial for any Ed25519 implementation to implement (it hashes the message in advance, then performs a regular Ed25519 signature albeit with a domain separation tweak).
For cases like this, where the IUF mode is deliberately domain separated from the normal mode, it would be nice to avoid a blanket impl which assumes all signature algorithms are composed as
S(H(m))
, so signers can, if they so desire, impl both traits and support both modes using a single underlying type (e.g.KeyPair
for signers andPublicKey
for verifiers), as the keys used for either algorithm are identical and both modes can be used safely under the same key due to domain separation.To accomplish this, this commit adds a
RawDigestSignature
marker trait to be applied at the algorithm level to the corresponding signature trait.For signature types marked as
RawDigestSignature
, it's possible toimpl
a correspondingSignRawDigest
trait for which a blanketimpl
ofSign
exists which hashes the message with the correspondingDigest
algorithm.Additionally,
SignDigest
is blanket impl'd for allSignRawDigest
types.