-
Notifications
You must be signed in to change notification settings - Fork 177
[WIP] Fixed-size no_alloc-friendly keys
#636
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,9 @@ use core::fmt; | |
| use core::hash::{Hash, Hasher}; | ||
|
|
||
| use crypto_bigint::modular::{BoxedMontyForm, BoxedMontyParams}; | ||
| use crypto_bigint::{BoxedUint, Integer, NonZero, Odd, Resize}; | ||
| use crypto_bigint::{ | ||
| BoxedUint, Integer, Monty, NonZero, Odd, Resize, Unsigned, U2048, U3072, U4096, | ||
| }; | ||
| use rand_core::CryptoRng; | ||
| use zeroize::{Zeroize, ZeroizeOnDrop}; | ||
| #[cfg(feature = "serde")] | ||
|
|
@@ -27,18 +29,28 @@ use crate::traits::{PaddingScheme, SignatureScheme}; | |
|
|
||
| /// Represents the public part of an RSA key. | ||
| #[derive(Debug, Clone)] | ||
| pub struct RsaPublicKey { | ||
| pub struct GenericRsaPublicKey<U: Unsigned> { | ||
| /// Modulus: product of prime numbers `p` and `q` | ||
| n: NonZero<BoxedUint>, | ||
| n: NonZero<U>, | ||
| /// Public exponent: power to which a plaintext message is raised in | ||
| /// order to encrypt it. | ||
| /// | ||
| /// Typically `0x10001` (`65537`) | ||
| e: BoxedUint, | ||
| e: U, | ||
|
|
||
| n_params: BoxedMontyParams, | ||
| n_params: <U::Monty as Monty>::Params, | ||
| } | ||
|
|
||
| /// RSA private key using dynamically sized heap-allocated integers for backing storage. | ||
| pub type RsaPublicKey = GenericRsaPublicKey<BoxedUint>; | ||
|
|
||
| /// RSA-2048 public key (stack-allocated). | ||
| pub type Rsa2048PublicKey = GenericRsaPublicKey<U2048>; | ||
| /// RSA-3072 public key (stack-allocated). | ||
| pub type Rsa3072PublicKey = GenericRsaPublicKey<U3072>; | ||
| /// RSA-4096 public key (stack-allocated). | ||
| pub type Rsa4096PublicKey = GenericRsaPublicKey<U4096>; | ||
|
|
||
| impl Eq for RsaPublicKey {} | ||
|
|
||
| impl PartialEq for RsaPublicKey { | ||
|
|
@@ -60,17 +72,27 @@ impl Hash for RsaPublicKey { | |
|
|
||
| /// Represents a whole RSA key, public and private parts. | ||
| #[derive(Clone)] | ||
| pub struct RsaPrivateKey { | ||
| pub struct GenericRsaPrivateKey<U: Unsigned + Zeroize> { | ||
| /// Public components of the private key. | ||
| pubkey_components: RsaPublicKey, | ||
| pubkey_components: GenericRsaPublicKey<U>, | ||
| /// Private exponent | ||
| pub(crate) d: BoxedUint, | ||
| pub(crate) d: U, | ||
| /// Prime factors of N, contains >= 2 elements. | ||
| pub(crate) primes: Vec<BoxedUint>, | ||
| pub(crate) primes: Vec<U>, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to think if we represent MP-RSA in here given
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oof yeah, that needs a solution. Maybe a helper trait? I can't think of a particularly good one here. You can probably reasonably cap the number of primes at a small number but if they're stack allocated it would still waste a lot of space.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally some type of borrowing injection mechanism that could f.ex. borrow already parsed PKCS#8 data that can be extended to other containers given this whole thing is very itsy bits? |
||
| /// Precomputed values to speed up private operations | ||
| pub(crate) precomputed: Option<PrecomputedValues>, | ||
| pub(crate) precomputed: Option<GenericPrecomputedValues<U>>, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Option is problematic given it takes it's largest layout in memory regardless of it being None leading to using memory if the precomputed is not used in case that type of variant is to be ever supported in no-alloc. Keeping up the precomputed stuff is essentially copies of stuff |
||
| } | ||
|
|
||
| /// RSA private key using dynamically sized heap-allocated integers for backing storage. | ||
| pub type RsaPrivateKey = GenericRsaPrivateKey<BoxedUint>; | ||
|
|
||
| /// RSA-2048 private key (stack-allocated). | ||
| pub type Rsa2048PrivateKey = GenericRsaPrivateKey<U2048>; | ||
| /// RSA-3072 private key (stack-allocated). | ||
| pub type Rsa3072PrivateKey = GenericRsaPrivateKey<U3072>; | ||
| /// RSA-4096 private key (stack-allocated). | ||
| pub type Rsa4096PrivateKey = GenericRsaPrivateKey<U4096>; | ||
|
|
||
| impl fmt::Debug for RsaPrivateKey { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| let precomputed = if self.precomputed.is_some() { | ||
|
|
@@ -111,35 +133,36 @@ impl Hash for RsaPrivateKey { | |
| } | ||
| } | ||
|
|
||
| impl Drop for RsaPrivateKey { | ||
| impl<U: Unsigned + Zeroize> Drop for GenericRsaPrivateKey<U> { | ||
| fn drop(&mut self) { | ||
| self.d.zeroize(); | ||
| self.primes.zeroize(); | ||
| self.precomputed.zeroize(); | ||
| } | ||
| } | ||
|
|
||
| impl ZeroizeOnDrop for RsaPrivateKey {} | ||
|
|
||
| #[derive(Clone)] | ||
| pub(crate) struct PrecomputedValues { | ||
| pub(crate) struct GenericPrecomputedValues<U: Unsigned + Zeroize> { | ||
| /// D mod (P-1) | ||
| pub(crate) dp: BoxedUint, | ||
| pub(crate) dp: U, | ||
| /// D mod (Q-1) | ||
| pub(crate) dq: BoxedUint, | ||
| pub(crate) dq: U, | ||
| /// Q^-1 mod P | ||
| pub(crate) qinv: BoxedMontyForm, | ||
| pub(crate) qinv: U::Monty, | ||
|
|
||
| /// Montgomery params for `p` | ||
| pub(crate) p_params: BoxedMontyParams, | ||
| pub(crate) p_params: <U::Monty as Monty>::Params, | ||
| /// Montgomery params for `q` | ||
| pub(crate) q_params: BoxedMontyParams, | ||
| pub(crate) q_params: <U::Monty as Monty>::Params, | ||
| } | ||
|
|
||
| pub(crate) type PrecomputedValues = GenericPrecomputedValues<BoxedUint>; | ||
|
|
||
| impl ZeroizeOnDrop for PrecomputedValues {} | ||
|
|
||
| impl Zeroize for PrecomputedValues { | ||
| fn zeroize(&mut self) { | ||
| impl<U: Unsigned + Zeroize> Drop for GenericPrecomputedValues<U> { | ||
| fn drop(&mut self) { | ||
| self.dp.zeroize(); | ||
| self.dq.zeroize(); | ||
| // TODO: once these have landed in crypto-bigint | ||
|
|
@@ -148,12 +171,6 @@ impl Zeroize for PrecomputedValues { | |
| } | ||
| } | ||
|
|
||
| impl Drop for PrecomputedValues { | ||
| fn drop(&mut self) { | ||
| self.zeroize(); | ||
| } | ||
| } | ||
|
|
||
| impl From<RsaPrivateKey> for RsaPublicKey { | ||
| fn from(private_key: RsaPrivateKey) -> Self { | ||
| (&private_key).into() | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
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.
If everything is
Uwhich could be optimized smaller size we end up taking n+e+d+p+qU * 3 + (U * primes)size in stackIn case of Rsa4096 it would be 512 * 5 (~4 kB) assuming just
pandqkept plus Precomputed copies..Having it as enum would allow different sizes and then requiring dispatch through that though like there is already the litany of U-type alias and you will have another litany of enum wrapping again all of them :)
Uh oh!
There was an error while loading. Please reload this page.
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.
An enum would monomorphize the code for all sizes up front. A generic lets you pick a specific size and it just monomorphizes for that.
An enum would also be as large in memory as the largest supported size.
Uh oh!
There was an error while loading. Please reload this page.
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.
ah yes. Associated type/s wouldn't monomorphize between them though and allows T bounds.
Another is to have two/three different generics - would get the lowest hanging fruit at least 🤷♀️
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.
Maybe
HeaplessUint<N>? RustCrypto/crypto-bigint#1148There 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.
Instead of using
U, an options i something based onLIMBS:where smaller primes can be expressed, in standard 2 prime RSA keys for example.
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.
If you have one that's generic around
U, you can easily make one that's generic aroundLIMBSwith a type alias:Uh oh!
There was an error while loading. Please reload this page.
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.
btw Why do we need a holder struct? Can't it be just provided as trait impl over hazmat over fixed size well known "safe" variants e.g. RSA2048, 3072, 4096 etc. that implements the validated component trait and which impls can be gated around what RSA sizes people need/want ? Associated types allows multiple types and even the provided methods could be used to check between them
Bonus is only one generic needs to be dragged around that needs it instead of multiple.
trait ValidatedRsaPrivateComponentsoverstruct RsaPrivateKey#640