-
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
RSA signature concerns #25
Comments
In my understanding impl<P: RsaPadding> Signer for RsaPrivateKey<P> { .. } |
@roblabla a hypothetical https://github.com/tendermint/yubihsm-rs/tree/develop/src/rsa As @newpavlov mentioned, the For an Note that PSS likely needs to be generic around a |
Note: renamed the title to something a bit more generic as I think these are important concerns to capture somewhere |
I picked this up again, trying to fit Why does DigestVerifier take an instantiated Digest object as an argument? Is it expected to pass a Digest that was already given input? If not, then it should really just be a type parameter - the Digest trait has a All schemes based on RSA require some way of passing options to the signing scheme (again taking PSS as an example, we need a way to pass the salt size and an RNG for signing). I currently pass then in a constructor. This can be very clunky - since struct RsaPssSigner<RNG> { key: RsaPrivateKey, rng: RefCell<RNG>, salt_len: Option<usize> };
impl<RNG> RsaPssSigner<RNG> {
fn new(key: RsaPrivateKey, rng: RefCell::new(RNG) salt_len: Option<usize>);
}
impl<RNG: Rng, D: Digest, S: Signature> DigestSigner {
fn try_sign_digest(&self, digest: D) -> Result<S, Error> {
pss::sign(self.rng.borrow_mut(), &self.key, digest.output(), self.salt_len)
}
}
// Usage:
let signer = RsaPssSigner::new(RsaPrivateKey::new(), rng::thread_rng(), salt_len: None);
let message = b"Hello World";
let mut hash = Sha256::new();
hash.input(message);
signer.try_sign_digest(hash); This feels kinda wrong. I would have expected impl DigestSigner {
type Options = (Option<usize>, &mut RNG);
fn try_sign_digest(&self, digest: D, opts: Self::Options) -> Result<S, Error> {
pss::sign(self.rng.borrow_mut(), &self.key, digest.output(), self.salt_len)
}
} And finally, on the error side of things, why not use an associated error type? The way signature currently handles errors on no_std will turn errors into an entirely unhelpful unit struct! 😱 |
That's okay, there are a lot of "hard won knowledge" in these traits I can link you to. I'm pretty sure we've tried pretty much everything you're suggesting and then decided to do the opposite for good reasons.
We originally did exactly what I think you're suggesting: we took a raw digest as a So why change it to a The main one, in my mind, is most signature schemes proven secure in the random oracle model (ROM) rely on it critically for security, so requiring the random oracle (i.e. the An attacker who can convince you to take a malicious digest output to verify can also (often trivially) trick you into believing a signature which verifies when it does not: the security of these systems critically hinges on the output of the random oracle not being a parameter an attacker can choose, and if they can, it can be trivial for the attacker to forge one. Making the random oracle a first-class part of the API avoids this sharp edge.
RSA is not alone here. ECDSA needs to know:
Seems like what you really want is a
Associated types aren't object safe, and I personally use these traits extensively as part of object-safe APIs which provide keychains/keystores which are backed by multiple key storage methods (e.g. HSMs). We can investigate adding a set of corresponding non-object-safe traits and some blanket impls for them, much like the ones that exist in some of the other
Aside from HSM use cases where it's helpful to pass back things like authentication, I/O, or other "I can't talk to the HSM" errors, there's ample history of "rich" error types emitted from cryptographic APIs or protocols being exploited by attackers as sidechannels. For this reason many modern cryptographic libraries use opaque error types, which in as much as they are "unhelpful", are also unhelpful to attackers. Here is All that said, much of what you have brought up are debatable points we have waffled on. None of this is set in stone and can still be changed (or changed back), but especially in cases where we tried one thing and went the other way (namely |
Thanks for taking the time to answer this :).
Part of the problem, IMO, is that a lot of this design seems to be hidden in the commit history/issues/PRs and the current design decisions aren't written down anywhere with a clear rationale. This should all be documented somewhere. Of course, I'm clearly spoiled by Rust's RFC system (which allows getting very clear rationale of every decision in the rust language) but a lot of what's explained here would probably be good to have in the signature documentation somewhere.
OK, I can understand the rationale behind that. But what about DigestVerifier? Why does it take a Digest? It shouldn't take anything except the message and self.
I guess that would work. I still find such stateful APIs kinda ugly. IMO, the RNG should be passed at each RSA call site by the user. But I can make peace with this.
That would be good. I'm pretty sure an object safe layer could be made on top of a non-object-safe version, in similar spirit to erased-serde. Erased-serde also uses associated types for the error, so it should surely be doable. I'll try to do some prototyping tomorrow.
I am painfully aware of this. That said, I have at least one case that I need to bubble up that I think is valid, and that's an error when the user-provided RNG is failing to provide random data (likely due to low entropy). |
Good point! @newpavlov any thoughts on how we should do that?
In cryptographic API design terminology, the goal of The API for "the message and self" is https://github.com/tendermint/signatory/blob/develop/signatory-secp256k1/src/lib.rs#L19 (we've played around with several other strategies for this including blanket impls, but haven't found another which addresses all concerns. It's something else I think worth potentially revisiting)
Nondeterministic ECDSA is in a similar boat. I think we could add an API like this, but if we did, it would need a new trait (e.g.
@newpavlov is painfully aware of this as well with all of his work on the Personally my suggestion in the case of any RNG failure would be to panic. Curious what @newpavlov thinks. |
I think there was a proposal to add a repo for RFCs, à la https://github.com/tokio-rs/tokio-rfcs (I think it was you @tarcieri?), but I didn't got to it yet, since I am not sure how useful it will be in practice.
Note that currently Though I am not sure why your use-case can not be handled by the current error type. |
I think for documenting API rationale the easiest approach will be to create an empty
We already have everything in place to handle RNG errors gracefully (e.g. the |
@newpavlov sure, that works (on both counts) |
BTW I think we can work with the stateless traits by constraining RNGs with the |
@newpavlov embedded RNGs seeded from a hardware peripheral almost certainly can't impl My personal preference for those would be to add either a stateful trait or |
In nostd, the current error type is a ZST. I can’t return any information on the error (like a message), unlike std errors which contains a boxed cause. My primary use of RSA will be in a nostd environment. That’s what’s bugging me wrt the error type. |
@roblabla I think @newpavlov is saying you should return the opaque error type in that case. Again, the counterpoint is opaque error types are good for security because they eliminate information an attacker could use to e.g. recover the signature key or forge a signature verification as part of an adaptive online attack. Having more than one error type which an attacker is able to observe is pretty much the classical way in which every practical sidechannel attack (e.g. BB'98, particularly relevant to RSA) has been implemented. The only way of preventing these attacks is to not even give the attacker one bit. |
@tarcieri Then why does the error type have a non-opaque cause when built under std? This is inconsistent. Even under your use-case of HSMs, no_std (such as embedded) crates that want to interface with a hardware module are currently unable to communicate what the error is (beyond the fact that an error occured). |
As you mentioned, it's specifically for the use case of relaying HSM errors which occur prior to actually performing the cryptographic operation, and indeed even this was a bit contentious.
This is true, although in my experience as someone who both interfaces with existing HSMs and is working on embedded cryptographic applications, I'm either communicating with an HSM from a If you have a use case for communicating with an external HSM from a |
Well, designing extendable errors which will work on both |
Indeed. I think you either wind up with two incompatible approaches, or unnecessarily constraining the errors expressible on For external signers, be they HSM, TPM, SEP, or network KMS, I think it's very important to be able to include rich error information for debugging. A network KMS might return an encrypted error bytestring, for example, which can be decrypted to determine some sort of e.g. AuthZ error around a key usage policy. This was the motivation for using |
I've added quite a bit of documentation which hopefully covers some of the design rationale questions highlighted in this thread to the https://docs.rs/signature/1.0.0-pre.3/signature/index.html I've also opened a 2-week final comment period prior to shipping a 1.0: I'm trying to direct any remaining final comments to that issue, so I'll close this one. |
In RSA, there are multiple schemes to sign/verify, mainly PKCS1 v1.5 and PSS (although others exists). I was looking at how to integrate
signature
into the RSA crate, and was a bit confused at the lack of a Padding argument or type inSigner
/Verifier
. Is the idea to implementVerifier
on a type that is already aware of the padding scheme?The text was updated successfully, but these errors were encountered: