Giter Site home page Giter Site logo

rsa's People

Contributors

aloucks avatar baloo avatar cbenoit avatar dependabot[bot] avatar dfabregat avatar dignifiedquire avatar est31 avatar littledivy avatar lucdew avatar lumag avatar mkeeter avatar newpavlov avatar npmccallum avatar phayes avatar poliorcetics avatar psychon avatar ricardicus avatar roblabla avatar sandhose avatar str4d avatar striezel avatar tarcieri avatar tchebb avatar tisonkun avatar trevor-crypto avatar ubamrein avatar wdhg avatar wsygog avatar zheylmun avatar zicklag avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rsa's Issues

How to output key to string?

Sorry, this is such a basic question, but I'm relatively new, and I don't really see how you can output the actual public and private keys to a string so I can save them. Is this possible?

Release v0.3.0?

There are a number of features and other changes that have landed since the previous release, such as support for parsing various key formats as well as @str4d's key trait refactoring (e.g. #35, #44) which I think it would be nice to have in a release version.

What do you think about doing another release soon?

pem

how can i export the keypair to file.pem

Implement SIMD where applicable

What got me looking into this was the somewhat slow key generation and the related issue #29. A solution or rather, improvement would be to implement SIMD, Single Instruction Multiple Data. I found a paper by freescale semiconductor on this topic here: http://application-notes.digchip.com/314/314-66328.pdf.

Since all processors do not support the AVX/SSE/SIMD family of instructions, this would have to be implemented under a feature flag or as in the case of aes_gcm, the feature is enabled when the compiler is passed these flags:

RUSTFLAGS="-Ctarget-cpu=sandybridge -Ctarget-feature=+aes,+sse2,+sse4.1,+ssse3"

Update: What takes time during key generation is finding big primes, and that is done here: https://github.com/dignifiedquire/num-bigint/blob/master/src/bigrand.rs#L324-L371

I might take a deeper look at this when my exams are over.

Documentation overhaul

The documentation in this crate is currently a bit barebones. I'd be interested in working on enhancing the documentation. Here are some things I can think of that would be nice to document:

  • RSAPrivateKey.sign Why is the Hash an Option? (Same question for verify). I assume passing None there means the "hashed" value is actually the raw message rather than a hash? That won't work for PSS verification.
  • RSAPrivateKey.validate When is it necessary to call this function? What happens if a key is used without being valid? Why do the constructors not ensure validity?
  • RSAPrivateKey.precompute Again, why not do this in new()? When is it necessary to call this function?
  • What does "blinding" the decryption process mean? When is it useful? (Probably want to put a link to wikipedia or something here, but having some documentation about it directly in the crate would be most welcome).
  • For all the enc/dec/sig/verif routines that are likely to be used, it would be nice to have a doctest showing how it should be used.

New API design

Prompted by RustCrypto/signatures#25 (comment) and my desire to get #18 and #26 merged 😄

The main design difference compared to the current traits is that instead of making the "padding scheme" a parameter of the pubkey sign/verify functions, we make each scheme a first-class primitive that wraps the common public or private key. It needs to be an explicit choice by the user anyway.

Current draft proposal (as of 2020-03-07) (without changes to the signature crate, and without handling the encryption cases):

// This module has been merged into master
mod raw {
    pub trait EncryptionPrimitive {
        /// Do NOT use directly! Only for implementors.
        fn raw_encryption_primitive(&self, plaintext: &[u8]) -> Result<Vec<u8>>;
    }

    pub trait DecryptionPrimitive {
        /// Do NOT use directly! Only for implementors.
        fn raw_decryption_primitive<R: Rng>(
            &self,
            rng: Option<&mut R>,
            ciphertext: &[u8],
        ) -> Result<Vec<u8>>;
    }
}

mod key {
    pub trait PublicKeyParts {
        /// Returns the modulus of the key.
        fn n(&self) -> &BigUint;
        /// Returns the public exponent of the key.
        fn e(&self) -> &BigUint;
        /// Returns the modulus size in bytes. Raw signatures and ciphertexts for
        /// or by this public key will have the same size.
        fn size(&self) -> usize {
            (self.n().bits() + 7) / 8
        }
    }

    pub trait PrivateKey: crate::raw::DecryptionPrimitive + PublicKeyParts {
        /// Could have functions like this for usability?
        pub fn sign_pkcs1v15(&self) -> crate::pkcs1v15::Signer;
        pub fn sign_pkcs1v15_blinded<R: Rng>(&self, rng: R) -> crate::pkcs1v15::Signer;
        pub fn sign_pss(&self) -> crate::pss::Signer;
        pub fn sign_pss_blinded(&self) -> crate::pss::Signer;
    }

    pub trait PublicKey: crate::raw::EncryptionPrimitive + PublicKeyParts {
        /// Could have functions like this for usability?
        pub fn verify_pkcs1v15(&self) -> crate::pkcs1v15::Verifier;
        pub fn verify_pss(&self) -> crate::pss::Verifier;
    }

    pub struct RSAPrivateKey { ... }

    impl crate::raw::DecryptionPrimitive for RSAPrivateKey { ... }

    impl PublicKeyParts for RSAPrivateKey { ... }

    impl PrivateKey for RSAPrivateKey {
        pub fn sign_pkcs1v15(&self) -> crate::pkcs1v15::Signer {
            crate::pkcs1v15::Signer::unblinded(self)
        }

        pub fn sign_pkcs1v15_blinded<R: Rng>(&self, rng: R) -> crate::pkcs1v15::Signer {
            crate::pkcs1v15::Signer::blinded(rng, self)
        }

        pub fn sign_pss(&self) -> crate::pss::Signer {
            crate::pss::Signer::unblinded(self)
        }

        pub fn sign_pss_blinded(&self) -> crate::pss::Signer {
            crate::pss::Signer::blinded(self)
        }
    }

    pub struct RSAPublicKey { ... }

    impl crate::raw::EncryptionPrimitive for RSAPublicKey { ... }

    impl PublicKeyParts for RSAPublicKey { ... }

    impl PublicKey for RSAPublicKey {
        pub fn verify_pkcs1v15(&self) -> crate::pkcs1v15::Verifier {
            crate::pkcs1v15::Verifier::new(self)
        }

        pub fn verify_pss(&self) -> crate::pss::Verifier {
            crate::pss::Verifier::new(self)
        }
    }
}

/// PKCS#1 v1.5 signing (and encryption?)
mod pkcs1v15 {
    use signature::Error;

    use crate::{
        key::{PrivateKey, PublicKey},
        raw::DecryptionPrimitive,
    };

    pub struct Signature {
        bytes: Vec<u8>,
    }

    impl signature::Signature for Signature {
        fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, Error> {
            // Parse a PKCS#1 v1.5 signature here non-contextually
            // (i.e. length can't be verified as we don't know n)
        }
    }

    // Technically all we need is K: DecryptionPrimitive, but PrivateKey
    // is the correct user-level encapsulation, and we should keep
    // DecryptionPrimitive internal as much as possible.
    pub struct Signer<'a, R: Rng, K: PrivateKey> {
        // RefCell in lieu of a stateful or randomizable signature trait
        rng: Option<RefCell<R>>,
        priv_key: &'a K,
    }

    impl<'a, R: Rng, K: PrivateKey> Signer<'a, R, K> {
        pub fn unblinded(priv_key: &'a K) -> Self {
            Signer { rng: None, priv_key }
        }

        pub fn blinded(rng: R, priv_key: &'a K) -> Self {
            Signer { rng: Some(RefCell::new(rng)), priv_key }
        }
    }

    impl<'a, R: Rng, K: PrivateKey> signature::Signer<Signature> for Signer<'a, R, K> {
        fn try_sign(&self, msg: &[u8]) -> Result<Signature, Error> {
            // Sign the message directly (equivalent to current None case)
        }
    }

    impl<'a, R: Rng, K: PrivateKey> signature::DigestSigner<D: Digest, Signature> for Signer<'a, R, K> {
        fn try_sign_digest(&self, digest: D) -> Result<Signature, Error> {
            // Sign the digest (equivalent to current Some(Hash) case)
        }
    }

    pub struct Verifier<'a, PK: PublicKey> {
        pub_key: &'a PK,
    }

    impl<'a, PK: PublicKey> Verifier<'a, PK> {
        pub fn new(pub_key: &'a PK) -> Self {
            Verifier { pub_key }
        }
    }

    impl<'a, PK: PublicKey> signature::Verifier<Signature> for Verifier<'a, PK> {
        fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {
            // Verify the message directly (equivalent to current None case)
        }
    }

    impl<'a, PK: PublicKey> signature::DigestVerifier<D: Digest, Signature> for Verifier<'a, PK> {
        fn verify_digest(&self, digest: D, signature: &Signature) -> Result<(), Error> {
            // Verify the digest (equivalent to current Some(Hash) case)
        }
    }
}

/// PSS signing
mod pss {
    use signature::Error;

    use crate::{
        key::{PrivateKey, PublicKey},
        raw::DecryptionPrimitive,
    };

    pub struct Signature {
        bytes: Vec<u8>,
    }

    impl signature::Signature for Signature {
        fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, Error> {
            // Parse a PSS signature here non-contextually
            // (i.e. length can't be verified as we don't know n)
        }
    }

    pub struct Signer<'a, R: Rng, K: PrivateKey> {
        // RefCell in lieu of a stateful or randomizable signature trait
        rng: RefCell<R>,
        priv_key: &'a K,
        salt_len: Option<usize>,
        blind: bool
    }

    impl<'a, R: Rng, K: PrivateKey> Signer<'a, R, K> {
        pub fn unblinded(rng: R, priv_key: &'a K, salt_len: Option<usize>) -> Self {
            Signer { rng: RefCell::new(rng), priv_key, salt_len, blind: false }
        }

        pub fn blinded(rng: R, priv_key: &'a K, salt_len: Option<usize>) -> Self {
            Signer { rng: RefCell::new(rng), priv_key, salt_len, blind: true }
        }
    }

    impl<'a, R: Rng, K: PrivateKey> signature::DigestSigner<D: Digest, Signature> for Signer<'a, R, K> {
        fn try_sign_digest(&self, digest: D) -> Result<Signature, Error> { ... }
    }

    pub struct Verifier<'a, PK: PublicKey> {
        pub_key: &'a PK,
    }

    impl<'a, PK: PublicKey> Verifier<'a, PK> {
        pub fn new(pub_key: &'a PK) -> Self {
            Verifier { pub_key }
        }
    }

    impl<'a, PK: PublicKey> signature::DigestVerifier<D: Digest, Signature> for Verifier<'a, PK> {
        fn verify_digest(&self, digest: D, signature: &Signature) -> Result<(), Error> { ... }
    }
}

// Do these improve usability?
pub use pkcs1v15::{
    Signature as Pkcs1v15Signature,
    Signer as Pkcs1v15Signer,
    Verifier as Pkcs1v15Verifier,
};
pub use pss::{
    Signature as PssSignature,
    Signer as PssSigner,
    Verifier as PssVerifier,
};

I'll update the proposal in this post as we discuss it.

Slow key generation

Hi all,

I have the following code snippet.

use rsa::RSAPrivateKey;
use rand::thread_rng;

let mut rng = thread_rng();
let private = RSAPrivateKey::new(&mut rng, 2048).unwrap();    

It sometimes takes about a second to execute, but most of the time it takes tens of seconds. Am I doing something wrong? Is there a different RNG that is faster?

Thanks!

`no_std`-targeted build fails, `getrandom` dependency uses `std`

Hello everyone,
Looking at #22, I'd expect to be able to build the library for no_std target, but this currently failson getrandom like this:

$> cargo build --target thumbv7em-none-eabihf
   Compiling getrandom v0.2.3
   Compiling typenum v1.13.0
error[E0463]: can't find crate for `std`
 --> /home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/getrandom-0.2.3/src/error_impls.rs:9:1
  |
9 | extern crate std;
  | ^^^^^^^^^^^^^^^^^ can't find crate
  |
  = note: the `thumbv7em-none-eabihf` target may not support the standard library

Is this expected to build at all, or things (e.g. your approach to supporting no_std) have changed since #22?

rsa is not compatible with rand 0.8.0

If I update my dependency of rand from 0.7.3 to 0.8.0 I get the following error:

the trait bound rand::rngs::OsRng: rand_core::RngCore is not satisfied the trait rand_core::RngCore is not implemented for rand::rngs::OsRng note: required because of the requirements on the impl of rand::Rng for rand::rngs::OsRng

I'm using rsa 0.3.0.

RSA PKCS#1 v1.5 signature scheme verification incompatibility issue

I was testing PKCS#1 v1.5 signature verification as implemented in RustCrypto-RSA and noticed it rejects valid signature whose encoded message uses an implicit NULL parameter for hash algorithm (where digestAlgorithm ANS.1 der encoded does not have NULL parameter TLV; that is, 0x0500 is absent).
According to RFC4055, pg.5 and RFC8017, pg. 64, for SHA-1, and the SHA-2 family, the algorithm parameter has to be NULL and both explicit NULL parameter and implicit NULL parameter (ie, absent NULL parameter) are considered to be legal and equivalent. However, this implementation does not accept a valid PKCS input with implicit NULL parameter.

Reference notation and concrete values

  • N: public modulus
  • |N|: length of public modulus
  • d: private exponent
  • e: public exponent
  • H: hash function
  • m: message
  • I: to-be-singed RSA PKCS#1 v1.5 signature scheme input structure
  • S: signature value obtained by I^d mod N
N = 0xE932AC92252F585B3A80A4DD76A897C8B7652952FE788F6EC8DD640587A1EE5647670A8AD4C2BE0F9FA6E49C605ADF77B5174230AF7BD50E5D6D6D6D28CCF0A886A514CC72E51D209CC772A52EF419F6A953F3135929588EBE9B351FCA61CED78F346FE00DBB6306E5C2A4C6DFC3779AF85AB417371CF34D8387B9B30AE46D7A5FF5A655B8D8455F1B94AE736989D60A6F2FD5CADBFFBD504C5A756A2E6BB5CECC13BCA7503F6DF8B52ACE5C410997E98809DB4DC30D943DE4E812A47553DCE54844A78E36401D13F77DC650619FED88D8B3926E3D8E319C80C744779AC5D6ABE252896950917476ECE5E8FC27D5F053D6018D91B502C4787558A002B9283DA7

|N| = 256 bytes

d = 0x009b771db6c374e59227006de8f9c5ba85cf98c63754505f9f30939803afc1498eda44b1b1e32c7eb51519edbd9591ea4fce0f8175ca528e09939e48f37088a07059c36332f74368c06884f718c9f8114f1b8d4cb790c63b09d46778bfdc41348fb4cd9feab3d24204992c6dd9ea824fbca591cd64cf68a233ad0526775c9848fafa31528177e1f8df9181a8b945081106fd58bd3d73799b229575c4f3b29101a03ee1f05472b3615784d9244ce0ed639c77e8e212ab52abddf4a928224b6b6f74b7114786dd6071bd9113d7870c6b52c0bc8b9c102cfe321dac357e030ed6c580040ca41c13d6b4967811807ef2a225983ea9f88d67faa42620f42a4f5bdbe03b

e = 3

H = SHA-256 (OID = 0x608648016503040201)

m = "hello world!"

I = 0x0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00302f300b060960864801650304020104207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9

S = 0xa0073057133ff3758e7e111b4d7441f1d8cbe4b2dd5ee4316a14264290dee5ed7f175716639bd9bb43a14e4f9fcb9e84dedd35e2205caac04828b2c053f68176d971ea88534dd2eeec903043c3469fc69c206b2a8694fd262488441ed8852280c3d4994e9d42bd1d575c7024095f1a20665925c2175e089c0d731471f6cc145404edf5559fd2276e45e448086f71c78d0cc6628fad394a34e51e8c10bc39bfe09ed2f5f742cc68bee899d0a41e4c75b7b80afd1c321d89ccd9fe8197c44624d91cc935dfa48de3c201099b5b417be748aef29248527e8bbb173cab76b48478d4177b338fe1f1244e64d7d23f07add560d5ad50b68d6649a49d7bc3db686daaa7

core-only "heapless" support

Right now, there is a PR open (#22) that will allow the RSA crate to be used in core+alloc environments (e.g. environments without libstd, but where a dynamic allocator is available).

The next step is for the RSA crate to support core-only environments. This would require multiple changes: num-bigint-dig would need to be represented either as a stack-allocated array (GenericArray? ArrayVec?) or a slice reference (wooo lifetimes) instead of a heap-allocated vector. Furthermore, we'll need to make extra sure that all the operations are bounded within a certain numeric range.

How can I encrypt my password with RSA public key?

In my case I need to use rsa to encrypt password before send it to server. the public key is distributed by server, and the implement java code is below:

byte[] decoded_pubkey = Base64.decodeBase64("{THE PUBLIC KEY DISTRIBUTED BY SERVER}");

RSAPublicKey publicKey = (RSAPublicKey) KeyFactory
        .getInstance(EncodingConstants.RSA)
        .generatePublic(new X509EncodedKeySpec(decoded_pubkey)); //use X509EncodedKeySpec

Cipher cipher = Cipher.getInstance(EncodingConstants.RSA);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);  

String encodedPassword = Base64.encodeBase64String(cipher.doFinal("my_password".getBytes(StandardCharsets.UTF_8)));

I tried rewrite this with rust:

let pukey = "{pubkey distributed by server}";

let decoded_pub_key = base64::decode(pukey).unwrap();

//question 1
let rsa_pub_key = rsa::RSAPublicKey::from_pkcs8(&decoded_pub_key).unwrap();

let password = "my password";

//question 2 and 3
let encrypt_pass = rsa_pub_key.encrypt(RNG, PaddingScheme::new_pkcs1v15_encrypt(), password.as_bytes());

and I got this question:

  1. Can I use rsa::RSAPublicKey::from_pkcs8 to replace the java code X509EncodedKeySpec? It seems different but I can't find any other suitable replace code.
  2. I don't have the param "RNG", where can I get it? or need I to generate it?
  3. I can't find the parameter PaddingScheme in java code. How can I use it correctly?

Cannot export public key to PEM

Hello, I'm trying to use the method to_pem_pkcs1 to export my public key and send it over the network. However I have an error telling me that it does not exist. Is this functionality available over crates.io? I'm using the version 0.3.0.

Here is my code:

use rsa::{PublicKey, RSAPrivateKey, RSAPublicKey, PaddingScheme, PublicKeyPemEncoding};
use rand::rngs::OsRng;

pub struct PloufClient {
    pub public_key: RSAPublicKey,
    pub pem: String,
    private_key: RSAPrivateKey,
}

pub fn init_client_session() -> PloufClient {
    let bits = 2048;
    let private_key = RSAPrivateKey::new(&mut OsRng, bits).expect("Failed to generate a key");
    let public_key = RSAPublicKey::from(&private_key);
    let pem = public_key.to_pem_pkcs1();

    PloufClient {
        private_key: private_key,
        pem: pem,
        public_key: public_key,
    }
}

And here is the error when compiling:

error[E0432]: unresolved import `rsa::PublicKeyPemEncoding`
 --> client/src/plouf_client.rs:1:66
  |
1 | use rsa::{PublicKey, RSAPrivateKey, RSAPublicKey, PaddingScheme, PublicKeyPemEncoding};
  |                                                                  ^^^^^^^^^^^^^^^^^^^^ no `PublicKeyPemEncoding` in the root

error[E0599]: no method named `to_pem_pkcs1` found for struct `RSAPublicKey` in the current scope
  --> client/src/main.rs:16:91
   |
16 |     let connection_message = format!("connect:{}:{:?}", username, plouf_client.public_key.to_pem_pkcs1());
   |                                                                                           ^^^^^^^^^^^^ method not found in `RSAPublicKey`

Thanks!

`pem` feature is not optional

Features std and pem are selected by default. But if you turn off pem feature, crate won't compile. Tried with the last version of rsa from crates.io.

Cargo.toml:

[dependencies.rsa]
version = "0.4"
default-features = false
features = ["std", "serde"]

Error:

`cargo check` output
$ cargo check

   Compiling num-bigint-dig v0.7.0
    Checking rsa v0.4.0
error[E0432]: unresolved imports `self::encode::PrivateKeyPemEncoding`, `self::encode::PublicKeyPemEncoding`
   --> /Users/surv/.cargo/registry/src/github.com-1ecc6299db9ec823/rsa-0.4.0/src/lib.rs:120:25
    |
120 |     PrivateKeyEncoding, PrivateKeyPemEncoding, PublicKeyEncoding, PublicKeyPemEncoding,
    |                         ^^^^^^^^^^^^^^^^^^^^^                     ^^^^^^^^^^^^^^^^^^^^ no `PublicKeyPemEncoding` in `encode`
    |                         |
    |                         no `PrivateKeyPemEncoding` in `encode`
    |
help: a similar name exists in the module
    |
120 |     PrivateKeyEncoding, PrivateKeyEncoding, PublicKeyEncoding, PublicKeyPemEncoding,
    |                         ^^^^^^^^^^^^^^^^^^
help: a similar name exists in the module
    |
120 |     PrivateKeyEncoding, PrivateKeyPemEncoding, PublicKeyEncoding, PublicKeyEncoding,
    |                                                                   ^^^^^^^^^^^^^^^^^

error[E0432]: unresolved import `pem`
 --> /Users/surv/.cargo/registry/src/github.com-1ecc6299db9ec823/rsa-0.4.0/src/encode.rs:9:5
  |
9 | use pem::{EncodeConfig, LineEnding};
  |     ^^^ use of undeclared crate or module `pem`

error[E0405]: cannot find trait `PublicKeyPemEncoding` in this scope
   --> /Users/surv/.cargo/registry/src/github.com-1ecc6299db9ec823/rsa-0.4.0/src/encode.rs:219:6
    |
219 | impl PublicKeyPemEncoding for RSAPublicKey {
    |      ^^^^^^^^^^^^^^^^^^^^ help: a trait with a similar name exists: `PublicKeyEncoding`
...
308 | pub trait PublicKeyEncoding: PublicKey {
    | -------------------------------------- similarly named trait `PublicKeyEncoding` defined here

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0405, E0432.
For more information about an error, try `rustc --explain E0405`.
error: could not compile `rsa`

To learn more, run the command again with --verbose.

Add a tutorial on intro page

We could add how to use this crate :

dependency to add in cargo.toml file
how to import the package in our own crate ?
how to generate private and public keys ?
how to access them and convert them into std::string ?
how to encrypt with the public key ?
how to decrypt with the private key ?

Easy to Misuse API For PEM Encoding ( On Master )

Hey there, I just noticed that it is extremely easy when using the PEM encoding traits to accidentally generate a public key when you mean to generate a private key PEM. For instance ( Key Pair is just a simple struct of mine ):

This works as expected:

let mut rng = rand::rngs::OsRng;
let private = rsa::RSAPrivateKey::new(&mut rng, 4096)?;
let public = rsa::RSAPublicKey::from(&private);

use rsa::PublicKeyPemEncoding;
use rsa::PrivateKeyPemEncoding;
Ok(KeyPair {
    private: private.to_pem_pkcs8()?,
    public: public.to_pem_pkcs8()?,
})

This actually generates two public keys:

let mut rng = rand::rngs::OsRng;
let private = rsa::RSAPrivateKey::new(&mut rng, 4096)?;
let public = rsa::RSAPublicKey::from(&private);

use rsa::PublicKeyPemEncoding;
// Say we accidentally forget to include the private key trait
// use rsa::PrivateKeyPemEncoding;
Ok(KeyPair {
    private: private.to_pem_pkcs8()?,
    public: public.to_pem_pkcs8()?,
})

Apparently the PublicKeyPemEncoding trait is implemented for private keys with the same function name: to_pem_pks8. I think an easy solution to this would be to name the to_pem_pkcs8 functions specific to the type of key you want to generate such as to_public_pem_pkcs8 or something like that.

Feature Request: RSA Blind signing

Hi there,

I was wondering if you would be interested in implementing RSA blind-signing in this crate?

Blind-signing is rare enough that it won't be picked up by any of the mainstream cryptography crates (like ring, openssl etc) so it might make sense to include it here.

Suggestions on using rsa and elliptic-curve dependencies (failing on subtle)

When trying to include both rsa and elliptic-curve dependencies you get a version issues with caused by subtle being pinned to =2.1.1 caused by #71. What is the best path forward here to better for me to also fork and downgrade elliptic-curve or upgrade it for rsa?

error: failed to select a version for `subtle`.
    ... required by package `elliptic-curve v0.8.4`
    ... which is depended on by `ecdsa v0.10.2`
    ... which is depended on by `p256 v0.7.1`
...
versions that meet the requirements `=2.4.0` are: 2.4.0

all possible versions conflict with previously selected packages.

  previously selected package `subtle v2.1.1 (https://github.com/dalek-cryptography/subtle.git?tag=2.1.1#d12fc92b)`
    ... which is depended on by `rsa v0.3.0 (https://github.com/RustCrypto/RSA.git?branch=master#1d8ff05d)`
...

modpow implementation is not constant-time

Hi there,

I'm the author of sidefuzz (https://github.com/phayes/sidefuzz) and I have found what appears to be variable-time behavior in the rsa::internals::encrypt() function. Specifically, rsa::internals::encrypt() appears to be variable-time in relation to the message. Note that I haven't worked this up into an actual exploit, but merely demonstrated that this function isn't constant-time in relation to the message inputed.

Specifically, the message 20d90c8af42aac9b1ee53dc9a0187201 takes 549894 instructions to encrypt, while the message 5a28cec68d47f6fe3b1df54c9f320f6d takes 552427 instruction to encrypt. This is a difference of 2533 instructions, or about 0.5%. So it's a slight difference, but probably exploitable with sufficient sampling.

I have crated a fuzzing targets here: https://github.com/phayes/sidefuzz-targets

You can confirm this difference with the sidefuzz tool like so:

sidefuzz check ./target/wasm32-unknown-unknown/release/rsa_encrypt_message.wasm 5a28cec68d47f6fe3b1df54c9f320f6d 20d90c8af42aac9b1ee53dc9a0187201
samples: 20000, t-value: 219771.0790572351, confidence: 100%
Found timing difference of 2533 instructions between these two inputs with 100% confidence:
input 1: 5a28cec68d47f6fe3b1df54c9f320f6d (552427 instructions)
input 2: 20d90c8af42aac9b1ee53dc9a0187201 (549894 instructions)

My first suspicion was that this was due to num_bigint_dig::BigUint::from_bytes_be() being variable-time, but fuzzing that function specifically results in what appears to be constant-time behavior. So I'm not actually sure where the problem is.

Type names

Right now this crate uses capitalized RSA in type names, e.g. RSAPrivateKey. But it looks like according to Rust naming conventions it should be named as RsaPrivateKey, see for example UdpSocket.

Checks performed in check_public seem insufficient

The check_public function nominally checks "that the public key is well formed and has an exponent within acceptable bounds."
https://github.com/RustCrypto/RSA/blob/master/src/key.rs#L642

But it also accepts even exponents, as long as they are within the defined range.

And it does not perform any checks on the modulus, accepting values including zero, even numbers, or modulus smaller than the public exponent.

Is accepting such keys intentional?

serde1 vs serde feature (crates.io package vs GitHub source code)

Hi,
I notice that if using the crates dot io package, the following type of error occurs

the trait serde::ser::Serialize is not implemented for rsa::RSAPrivateKey

In this instance the Cargo.toml file is using the serde feature. For example

[dependencies]
rsa = { version = "^0.2", features = ["serde"] }

The GitHub source code [1] and config [2] choose serde over serde1. I believe that serde1 is deprecated.

If the following dependency in the Cargo.toml file is used the issue is resolved.

rsa = { version = "^0.2", features = ["serde1"] }

For example the private key can be easily serialised like this

use serde_json;
extern crate rand;
extern crate rsa;
use rand::rngs::OsRng;
use rsa::{RSAPrivateKey};
fn main() {
    let mut rng = OsRng;
    let bits = 2048;
    let key = RSAPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
    let as_json_string = serde_json::to_string(&key).unwrap();
    println!("{:?}", as_json_string);
}

My main observation is that the crate is out of alignment/sync with the GitHub code, config and documentation. If the crate was updated then users would be able to use the following to enable the optional serde feature.

[dependencies]
rsa = { version = "^0.2", features = ["serde"] }

Hope this helps.

Kind regards
Tim

[1] https://github.com/RustCrypto/RSA/blob/master/src/key.rs#L51
[2] https://github.com/RustCrypto/RSA/blob/master/Cargo.toml#L59

The oaep padding is not compatiable to other lib.

I try to use this crate to encrypt data and decrypt use node's crypto and this online tool.

this is my example code:

use rsa::{PublicKey, RSAPublicKey, RSAPrivateKey, PaddingScheme};
use rand::rngs::OsRng;
use sha2::Sha256;

pub fn encrypt_(key: &[u8], plaintext: &[u8]) -> Vec<u8> {
  let mut rng = OsRng;
  let data = plaintext;
  let public_key = RSAPublicKey::from_pkcs8(key).expect("get private key error");
  let padding = PaddingScheme::new_oaep::<Sha256>();
  let enc_data = public_key.encrypt(&mut rng, padding, &data[..]).expect("failed to decrypt");
  enc_data
}

then try to decrypt by crypto

const crypto = require('crypto');
const fs = require('fs');
const privateKey = fs.readFileSync('./private.pem');
const publicKey = fs.readFileSync('./public.pem');
const cipher = fs.readFileSync('./cipher') // I persisted the encrypt data into this file without base64 encoded

function decryptOthers() {
  const plainText = crypto.privateDecrypt(privateKey, cipher);
  console.log('plainText: ', plainText.toString());
}

then I got this error error:04099079:rsa routines:RSA_padding_check_PKCS1_OAEP_mgf1:oaep decoding error

here is my private.pem

-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC3tiVTqKHkGZAl
bW9NsOjT87HkoIjlQM8el9qhsSBZJRzZr3cbZuV57qbhC2Nls7RurYGjd6+sNL4d
mcbjzpaSSUK9WxRi/t0Uc5cAeUiPWJ4tsf4C+zPg3cA/PE4UhbOcJxsXMQ6QMCSg
9Kvqbx6IGtejvMGLsnBc9lQrY5mKzmJBEtQ8FGyZDIDWhYCvhD8PJf/VCWLh9eMM
6sQrZlO84BB5rB1iu4K23zr4CUT5hT7855FCfAhCguT+S4hy+LZTTzoObwDtT8Qo
SarCh9Y8Tu463orkYjz3h/soHci8Vryue3gvcu8l9pC/cTYZp61vEKTPLHGHbNea
Tx5WrffjAgMBAAECggEATLsyWf/sJmaa81cDDzMd/DEkEzcOFpr8J24Lrk9aEIHU
zANivzo6DLSmVQa5rGfjq/qiVBGQeV/KLHjnPo0yDMW2r3pUSIjk9D7XSiZeLSk5
YCKg0sPzRi+4tXDNt/qkK0uWIBioWYcSNXwcNh3VZcm4WbZkLKIKFnn4a+r83aFi
8j2qsMe1u0qbTUiEJjXDI6wJNJkUj/MVaBbkM0O8GPIrTbGLSSElHa843kRrnkCL
/w8xALywKJcLoYD4gruapDZIZ7kQ7dmccnghZOt+yfNsa9NX5Eiv0sYBtSgaSKfI
tbiitP5gUtTtHszDiliDjkMWlufVht5vVPjSQDl/gQKBgQDbi3WqcMXOX4+qBzy9
WT8kP8BB0tKgHpjTuyfFCUyXnfBQSngEH4LOpm73bSbl4dfvk8P+6NTLgfZDpNJt
qyFD8zW6nl6cmXpFBnW0LRRd1DY9JgqxEyMHTAWgaUWkwTz06MwU1eScj/7INV5l
5nQ7+ebW7qZaoiXy1aSmcA+KwQKBgQDWN3bw7Zkh25//hW5TvSCNgKGBI1irsNcH
akMLm5GNikUpq4i/En35MqfGGIWZD50g4J83tWSHSYfBc2RXqEuW2duPrFqRby8x
CjLfFyPimPz6OSUIAwOsCZhAxc3eHLsakpS9S0/H6ucqMGenbEvOiwN4eNmLjwo2
0kM+wgtfowKBgAtHe/EYwCDdKf1bR9SWjDbpi5AWK7oMOF/QYretxaCYfasyWD4l
/Etvo43UrsXqt3RYjB5BigU2lRdelW0K+LeL/DutPUEgZXa8YTmzgzNa3rp/hNca
G5eZYUSfg/N19hLDDxEW+BrPDxDsJQBpOZmHOkPamPNZF0gFj+L5KONBAoGALz6w
VtawLU147iwJdyKBKXQzKn4XMlagNhxziP3YjjTuqBAS6sEzU2CerAwAew98giHI
O8sfzs6iyEGg4cxWT4Dg8cD7gJPa/p/XNGiwG2vOb4lm52tyxeCf2blGfDQt9+BY
OZ+AiwD91soyMjSqS3VRYKKWqDb1871P42l8hK0CgYAv1ncq/QPZMM6yg97hHCNL
dXIlRASa1G8tHJGxdz0O0z1+tMwHR6GgN9cgKTT8I6VvhhQfQs8Lj0+itMcVnVbt
lVVqu1DNdnAT+VLFECOyxbn28GKeKBWLVih7DXCAz4UhwiYlAmdMnqCFcDy2XBs9
ZINP8KKsJFd50ijb8hRo5w==
-----END PRIVATE KEY-----

and public.pem

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt7YlU6ih5BmQJW1vTbDo
0/Ox5KCI5UDPHpfaobEgWSUc2a93G2blee6m4QtjZbO0bq2Bo3evrDS+HZnG486W
kklCvVsUYv7dFHOXAHlIj1ieLbH+Avsz4N3APzxOFIWznCcbFzEOkDAkoPSr6m8e
iBrXo7zBi7JwXPZUK2OZis5iQRLUPBRsmQyA1oWAr4Q/DyX/1Qli4fXjDOrEK2ZT
vOAQeawdYruCtt86+AlE+YU+/OeRQnwIQoLk/kuIcvi2U086Dm8A7U/EKEmqwofW
PE7uOt6K5GI894f7KB3IvFa8rnt4L3LvJfaQv3E2GaetbxCkzyxxh2zXmk8eVq33
4wIDAQAB
-----END PUBLIC KEY-----

and I also tried on this online tool. still no lucky.

but if:

  1. I encrypt with node's crypto then decrypt with this online tool. it works.
  2. I encrypt with this rust lib,but use pkcs1 padding, and select pkcs1 in online tool. it works.

So I can infer that it's the oaep padding lead to incompatible.

Can't load generated keys into ring

I've tried to load the keys generated by this crate into the ring crate. However, sadly it didn't work. Instead, ring returned an error KeyRejected("WrongAlgorithm")'. I think that the bug lies on the side of the rsa crate.

use rsa::{RSAPrivateKey, PrivateKeyEncoding, PrivateKeyPemEncoding};
use rand::rngs::OsRng;

fn main() -> Result<(), Box<dyn std::error::Error>> {
	let mut rng = OsRng;
	let bits = 2048;
	let private_key = RSAPrivateKey::new(&mut rng, bits)?;
	let private_key_der = private_key.to_pkcs8()?;
	println!("{}", private_key.to_pem_pkcs8()?);
	let key_pair = ring::signature::RsaKeyPair::from_pkcs8(&private_key_der).unwrap();
	Ok(())
}

This is an example key generated by the rsa crate:

``` -----BEGIN PRIVATE KEY----- MIIEuwIBADALBgkqhkiG9w0BAQEEggSnMIIEowIBAAKCAQEAvSxNp8eh+L+yONrg HvEB9sQqebrcWoGJyFhuwph/IYLL3qakAFVEss/E4dBBgewhW7z0Lg+rI4/peu8g 6gXKTgpHthqJsD4XLM//zkNQFDR78W2M9u1yJpoqcqkAkRNhVed1cmvTgBuxRQFV znlgob/cEENokgVUZGWvujx+nZtdi+jm+d2iMYq97j2cY+CyOiGzzGsE1Bn0ictd VIjbBiRGGR0MSXu2mRwoKTfcQfMGO9MFT5q+fBVCiqI1xAqkSBuuuSdmNHs6BC2M vVPt4/KBhQz4jh60Lf3Kcnp2uselY3fwIALIuVb2UMFQO+LbSrPCTrIIlKYFk86q SDpmuwIDAQABAoIBAQCwZnejVIy9IERYsAD92U8zhIj3KkAARXrtz0i5POqPnR/u ZwXXKav98bBtwXzUYhRZL0GjEglMCA54DDRtBSk4zG1Cakyqg7uoley7shkiPi61 WqpwN8qSJIAgm2dj4OaK7GKgZ9u5Qhlzd2NEPfrV2cqjf7grMb0YQZoz1+VZoMRb B7Yzrv2wwgVsMVl35ragxcNbI7ioU2sOUyRMi2Ud0YhjsR27F1Sekx68XH4CX/W3 wb+PRKpXDwiCAhfdf60pYJwL4arAgYvkRkVXzdQWpzJhDoXTxvVx7qKiSTM+KBRB eYjvgKzcSLbs6Bo8xlqdytwjJeYjnmw1V4dR3+fBAoGBANS39tLzCHy7Jy4CPAQp 8KlJ1DCJhomPGBJEgXjwBlWyo7ZfM8YkOjD2o2q5fs5kxu6ocwFOM/HJ8f+i+yy7 OMbDQL/ArSYb9tiAHTRCnaCxXTR1YEW76VBgewZfxzeuzKEXUQcKKWZM2Mhz/vvL +Nsxmf9majely0awRG7hx6xnAoGBAOOp53uzB756+zZzoqoMsgKKPVexDRUbd/eC lw9St1RCWX5g5XstgyPjYsz8/zgG797GDDznrhLJsOtd1tly41Bk/pFaJEwVhDp7 tdKBZropHnZY5SNHUFQdhVxZS15iKPxIEqAf9o4rhdcKxoW/KwRFC7YuWJmy0KAt BEOa1L6NAoGAJahTp9CihgJT009N9KbAFhgd2mHeUWflqQY7rX9Af9daKCT+c2ti adyf3XrtOfiZRxLHGUoQ5BMOJmVh9e9HA9j5E3k2rfJxZq53fRL1+Mata2YEHxQ/ H52U6fH2wKLPUw1fqNFhttyP3By83TKk4ZkMXCxzTVqqe98knkSWJq0CgYAewBA4 MRJWtB3CWRQbi+NvXGS0t4Fg3HjzwXvN4gpJIz0bSNM2NAuGiWwK7BE87BLyMCXv BAwLFZsD74TZkh8J+No5n8dPT4iwrYz9heTuOCeZLKCmhzdeZS5iLt1zPibzDHkX j7mpsk89oIL4Bb41sW3HibMF75UmTW5KA1vA8QKBgDwoIQ0pZUlGmptUfSdk9QLU SU5djs3mO5odof2OHXN51dUvT39T4BH/+MWWJcFnys1QJWiqmVqSm8newQhmyfnG 6KQ2rBmU2AWqyFHEaaMK2YjAjlPj+TYfE9Dxi8KY90gSesr7H0P7GIGblDzPooa/ cgObFZ9QIVgA8NmHGJQR -----END PRIVATE KEY----- ```

This is an example key generated by the openssl CLI tool.

The openssl generated key can be loaded successfully by ring. The rsa generated key can't.

If I load the rsa generated key into the lapo.it tool, I find the following:

Screenshot_20210529_045821

If I load the openssl generated key into the lapo.it tool, I find the following:

Screenshot_20210529_050210

Notice how the rsa crate key does not have a NULL value in the id's location where the openssl crate key does. I think the issue lies in the missing NULL value.

I've checked ring's source code and it seems to only return a KeyRejected("WrongAlgorithm") if the algorithm id mismatches the expected algorithm id, which in this instance is the algorithm id of RSA. This ID is provided via a der file under src/data/alg-rsa-encryption.der. If you inspect the file, it contains both the known oid and the NULL value.

As there is a difference in behaviour, i've studied the spec to find out which implementation is in the wrong. PKCS#5 (from which PKCS#8 imports the AlgorithmIdentifier type), specifies AlgorithmIdentifier to be made up as a SEQUENCE containing two or three fields. The third field is optional. So I think the rsa crate should add this second field as NULL instead omitting it.

Maybe there are further issues preventing ring from loading rsa crate generated keys, idk. This is as far as I've got.

Encoded PEM Files Not Compatible With OpenSSL or SSH

I am getting the following error when trying to decode a private key file encoded with the master version of this crate:

$ openssl rsa -in id_rsa -text
unable to load Private Key
140237402744128:error:0D078079:asn1 encoding routines:asn1_item_embed_d2i:field missing:../crypto/asn1/tasn_dec.c:406:Field=dmp1, Type=RSAPrivateKey
140237402744128:error:04096004:rsa routines:rsa_priv_decode:RSA lib:../crypto/rsa/rsa_ameth.c:183:
140237402744128:error:0606F091:digital envelope routines:EVP_PKCS82PKEY:private key decode error:../crypto/evp/evp_pkey.c:44:
140237402744128:error:0907B00D:PEM routines:PEM_read_bio_PrivateKey:ASN1 lib:../crypto/pem/pem_pkey.c:88:

My program looks like this:

    let mut rng = rand::thread_rng();
    let private = rsa::RSAPrivateKey::new(&mut rng, 4096).unwrap();
    let public = rsa::RSAPublicKey::from(&private);

    use rsa::PrivateKeyPemEncoding;
    std::fs::write("id_rsa", private.to_pem_pkcs8().unwrap()).unwrap();
    use rsa::PublicKeyPemEncoding;
    std::fs::write("id_rsa.pub", public.to_pem_pkcs8().unwrap()).unwrap();

    let key_data = std::fs::read_to_string("./id_rsa").unwrap();
    let key_data = rsa::pem::parse(&key_data).unwrap();
    let key = rsa::RSAPrivateKey::try_from(key_data).unwrap();
    assert_eq!(key, private);
    dbg!(key);

I create a key, encode it, and then read it just to make sure it survives the round trip. The round trip survives fine and all that, but for some reason OpenSSL doesn't like it. It doesn't like the public key either:

$ openssl rsa -in id_rsa.pub -text -pubin
unable to load Public Key
140275199583552:error:0D0BD0DC:asn1 encoding routines:c2i_ASN1_BIT_STRING:invalid bit string bits left:../crypto/asn1/a_bitstr.c:137:
140275199583552:error:0D08303A:asn1 encoding routines:asn1_template_noexp_d2i:nested asn1 error:../crypto/asn1/tasn_dec.c:627:Field=public_key, Type=X509_PUBKEY
140275199583552:error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib:../crypto/pem/pem_oth.c:33:

If I change the bits to 512 the public key will read fine but the private key still doesn't:

$ openssl rsa -in id_rsa.pub -text -pubin
RSA Public-Key: (512 bit)
Modulus:
    00:c1:31:44:3d:2e:d6:3e:43:35:f4:ae:bc:29:24:
    c3:2b:66:d1:bc:e5:e0:9b:30:d6:fa:91:66:25:9a:
    a0:9e:0a:df:eb:f4:62:c3:f8:28:af:a0:ed:f8:48:
    e3:1b:e8:5f:24:45:b8:8c:a3:5e:d9:6e:e7:54:7d:
    92:11:12:74:07
Exponent: 65536 (0x10000)
writing RSA key
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMExRD0u1j5DNfSuvCkkwytm0bzl4Jsw
1vqRZiWaoJ4K3+v0YsP4KK+g7fhI4xvoXyRFuIyjXtlu51R9khESdAcCAwEAAA==
-----END PUBLIC KEY-----
$ unable to load Private Key
140639017108800:error:0D078079:asn1 encoding routines:asn1_item_embed_d2i:field missing:../crypto/asn1/tasn_dec.c:406:Field=dmp1, Type=RSAPrivateKey
140639017108800:error:04096004:rsa routines:rsa_priv_decode:RSA lib:../crypto/rsa/rsa_ameth.c:183:
140639017108800:error:0606F091:digital envelope routines:EVP_PKCS82PKEY:private key decode error:../crypto/evp/evp_pkey.c:44:
140639017108800:error:0907B00D:PEM routines:PEM_read_bio_PrivateKey:ASN1 lib:../crypto/pem/pem_pkey.c:88:

Any idea what's wrong?

How do I actually import a public key from a string ?

In go you simply pem.Decode and then x509.ParsePKIXPublicKey the given byte slice and you get a rsa.PublicKey. However, in the rust world none of those libraries are actually compatible with each other so how do I import an existing public key?

Remove std feature requirement for encoding

I would like to use PrivateKeyPemEncoding in my Wasm project but RSA currently cannot target Wasm with feature std enabled. I am able to compile and generate keys successfully with feature std disabled, however I am unable to encode it to a usable format for my use case (pem, pkcs1, jwk).

RSA/SHA-384/PKCSv1.5 signature verification disagrees with (presumed) Java equivalent

Trying to port something that boils down to the following Java 10/11 code to Rust:

package com.example;

// convenient hex decoder in the project's classpath
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.RSAPublicKeySpec;

public final class VerifyRsaSignature {
    private VerifyRsaSignature() { }

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException, InvalidKeyException, SignatureException {
        final var hash = "1b2591f59a097c064dc523b463b9382bca7beb8aaf3017d5ce00ad79b2526900f4d32a112c90100b0371d31ca5915f85";
        final var sig = "5b4d9df396c6e612e76ec19c90e4c8487dc972374309c388a561a775e9aac50df684caa141b787b422b840ecd4f9266dea36cc4a635ee45e8172ccbe2bcaa1483b4be18999655a90ff971ba8ce66549ff57fffdecf7574080f3957b159840a68882f80358ad896ea49cfd933dff72344cd930d643e3fd31dec4d7c65183c03cbeaf61521a4a0b51987906b65d7efa4cc84e0af2e58dd540e9def08d8f845659e47f6bd932d2bee70351fbea24d17f1d0c1904262c9ac7ca940f731513431a07d565a8ff602bb74248a591de58d6855f65ddd7af5401e07162cd3822ec8457c54b7ac26564083c014ff8b0a84b9172d0e5549cbe4fe3d25ac43d578c868d1980cf758132f22db185848d06409829d4635b459dc7e3f46484680a552c4b4918e457459814f08c88439e44f02cebbe5bbfb30fa970cf520addc66b70345a2af71699b5d0f695726189ff787dbf9792819aaaef423688a8ae03743882959ad0c5677e6caeeecec1c38095bee4ef3cd3890b869ea451822836b096aa47de2b4df4d92";

        final var otherPubKeySpec = new RSAPublicKeySpec(
            new BigInteger("4815353633898737192848467593367479656218099547053668152312532447850642727820088857916758887139149255016433747196944716163948011240330834655975271455398595576805218637134748817769890176151097847194370880509935527062478215773688525966290912363244346086836749488396907118087367668292595162658160435767367539879328798314903185634557745948134125007150538337992219966215843942253830490101640601187569602464387723392658408739275456883457028273497894255911578642292096621474695979417740477306988814325596158429448021004131893376997192061503077838129481608056956263873489869509947467332984314094537003274510244027855449655341677101205418688544184928739525406202351565555525168438173253543605598706596301395282261458791359035333554877158961391715712192703126452301368119990091871827806149386795649553706086123204793824838772530087465870735788671062500440390873934724266075703478741258343061310611954575668801257331192260237252091166799"),
            new BigInteger("65537"));

        final var keyFactory = KeyFactory.getInstance("RSA");
        final var pubKey = (RSAPublicKey) keyFactory.generatePublic(otherPubKeySpec);

        final var signature = Signature.getInstance("SHA384withRSA", "SunRsaSign");
        signature.initVerify(pubKey);
        signature.update(Hex.decode(hash));

        if (signature.verify(Hex.decode(sig))) {
            System.out.println("signature verified");
        }
    }
}

This runs and prints "signature verified".

Java specifies that the SHA384withRSA signing algorithm uses the PCKS v1.5 padding scheme, so I assumed the following Rust code would be equivalent:

// hex = "0.3.2"
// rsa = "0.1.3"
// num-bigint-dig = "0.4.0"
// failure = "0.1.5"

use num_bigint_dig::BigUint;
use rsa::{RSAPublicKey, PublicKey, PaddingScheme};
use rsa::hash::Hashes;

const MODULUS: &str = "4815353633898737192848467593367479656218099547053668152312532447850642727820088857916758887139149255016433747196944716163948011240330834655975271455398595576805218637134748817769890176151097847194370880509935527062478215773688525966290912363244346086836749488396907118087367668292595162658160435767367539879328798314903185634557745948134125007150538337992219966215843942253830490101640601187569602464387723392658408739275456883457028273497894255911578642292096621474695979417740477306988814325596158429448021004131893376997192061503077838129481608056956263873489869509947467332984314094537003274510244027855449655341677101205418688544184928739525406202351565555525168438173253543605598706596301395282261458791359035333554877158961391715712192703126452301368119990091871827806149386795649553706086123204793824838772530087465870735788671062500440390873934724266075703478741258343061310611954575668801257331192260237252091166799";
const EXPONENT: &str = "65537";

const HASH: &str = "1b2591f59a097c064dc523b463b9382bca7beb8aaf3017d5ce00ad79b2526900f4d32a112c90100b0371d31ca5915f85";
const SIG: &str = "5b4d9df396c6e612e76ec19c90e4c8487dc972374309c388a561a775e9aac50df684caa141b787b422b840ecd4f9266dea36cc4a635ee45e8172ccbe2bcaa1483b4be18999655a90ff971ba8ce66549ff57fffdecf7574080f3957b159840a68882f80358ad896ea49cfd933dff72344cd930d643e3fd31dec4d7c65183c03cbeaf61521a4a0b51987906b65d7efa4cc84e0af2e58dd540e9def08d8f845659e47f6bd932d2bee70351fbea24d17f1d0c1904262c9ac7ca940f731513431a07d565a8ff602bb74248a591de58d6855f65ddd7af5401e07162cd3822ec8457c54b7ac26564083c014ff8b0a84b9172d0e5549cbe4fe3d25ac43d578c868d1980cf758132f22db185848d06409829d4635b459dc7e3f46484680a552c4b4918e457459814f08c88439e44f02cebbe5bbfb30fa970cf520addc66b70345a2af71699b5d0f695726189ff787dbf9792819aaaef423688a8ae03743882959ad0c5677e6caeeecec1c38095bee4ef3cd3890b869ea451822836b096aa47de2b4df4d92";

fn main() -> failure::Fallible<()> {
    let modulus = MODULUS.parse::<BigUint>()?;
    let exponent = EXPONENT.parse::<BigUint>()?;

    let pub_key = RSAPublicKey::new(modulus, exponent)?;

    let hash_bytes = hex::decode(HASH)?;
    let sig_bytes = hex::decode(SIG)?;

    pub_key.verify(PaddingScheme::PKCS1v15, Some(&Hashes::SHA2_384), &hash_bytes, &sig_bytes)?;

    Ok(())
}

This returns Error: Verification.

Am I doing something wrong here?

Multi-prime RSA

This is a tracking issue for discussion of multi-prime RSA.

Multi-prime RSA provides a performance-vs-security tradeoff, allowing for improved performance but at the cost of reduced security:

https://www.imperialviolet.org/2011/04/09/multiprime.html

Screen Shot 2020-09-22 at 2 10 39 PM

In semi-recent history, attacks against multi-prime RSA have been discovered which further weaken its security:

https://eprint.iacr.org/2015/1137.pdf

Because of that, in some cases we have elected to forego support for multi-prime RSA. This is a tracking ticket for those cases, and also gauging whether or not anyone actually cares about multi-prime RSA support or not.

  • In #64 it was decided to forego serialization support for multi-prime RSA keys.

Panic at 'invalid coeff' using a private key generated by openssl

Hi,

I tried playing around with RSA and everything is nice as long as I generate my private keys with the API provided by the crate itself (great!).
I also tried to sign a message using a private key generated by openssl. It worked for most of the generated keys but not this one.

The public exponent is 65537 and the other components in base64 are :

  • n: wC8GyQvTCZOK+iiBR5fGQCmzRCTWX9TQ3aRG5gGFk0wB6EFoLMAyEEqeG3gS8xhAm2rSWYx9kKufvNat3iWlbSRVqkcbpVAYlj2vTrpqDpJl+6u+zxFYoUEBevlJJkAhl8EuCccOA30fVpcfRvXPTtvRd3yFT9E9EwZljtgSI02w7gZwg7VIxaGeajh5Euz6ZVQZ+qNRKgXrRC7gPRqVyI6Dt0Jc+Su5KBGNn0QcPDzOahWha1ieaeMkFisZ9mdpsJoZ4tw5eicLaUomKzALHXQVt+/rcZSrCd6/7uUo11B/CYBM4UfSpwXaL88J9AE6A5++no9hmJzaF2LLp+Qwx4yY3j9TDutxSAjsraxxJOGZ3XyA9nG++Ybt3cxZ5fP7ROjxCfROBmVv5dYn0O9OBIqYeCH6QraNpZMadlLNIhyMv8Y+P3r5l/PaK4VJaEi5pPosnEPawp0W0yZDzmjk2z1LthaRx0aZVrAjlH0Rb/6goLUQ9qu1xsDtQVVpN4A89ZUmtTWORnnJr0+595eHHxssd2gpzqf4bPjNITdAEuOCCtpvyi4ls23zwuzryUYjcUOEnsXNQ+DrZpLKxdtsD/qNV/j1hfeyBoPllC3cV+6bcGOFcVGbjYqb+Kw1b0+jL69RSKQqgmS+qYqr8c48nDRxyq3QXhR8qtzUwBFSLVk=
  • d: qQazSQ+FRN7nVK1bRsROMRB8AmsDwLVEHivlz1V3Td2Dr+oW3YUMgxedhztML1IdQJPq/ad6qErJ6yRFNySVIjDaxzBTOEoB1eHa1btOnBJWb8rVvvjaorixvJ6Tn3i4EuhsvVy9DoR1k4rGj3qSIiFjUVvLRDAbLyhpGgEfsr0Z577yJmTC5E8JLRMOKX8Tmxsk3jPVpsgd65Hu1s8S/ZmabwuHCf9SkdMeY/1bd/9i7BqqJeeDLE4B5x1xcC3z3scqDUTzqGO+vZPhjgprPDRlBamVwgenhr7KwCn8iaLamFinRVwOAag8BeBqOJj7lURiOsKQa9FIX1kdFUS1QMQxgtPycLjkbvCJjriqT7zWKsmJ7l8YLs6Wmm9/+QJRwNCEVdMTXKfCP1cJjudaiskEQThfUldtgu8gUDNYbQ/Filb2eKfiX4h1TiMxZqUZHVZyb9nShbQoXJ3vj/MGVF0QM8TxhXM8r2Lv9gDYU5t9nQlUMLhs0jVjai48jHABbFNyH3sEcOmJOIwJrCXw1dzG7AotwyaEVUHOmL04TffmwCFfnyrLjbFgnyOeoyIIBYjcY7QFRm/9nupXMTH5hZ2qrHfCJIp0KK4tNBdQqmnHapFl5l6Le1s4qBS5bEIzjitobLvAFm9abPlDGfxmY6mlrMK4+nytwF9Ct7wc1AE=
  • prime 1: 9kQWEAzsbzOcdPa+s5wFfw4XDd7bB1q9foZ31b1+TNjGNxbSBCFlDF1q98vwpV6nM8bWDh/wtbNoETSQDgpEnYOQ26LWEw6YY1+q1Q2GGEFceYUf+Myk8/vTc8TN6Zw0bKZBWy10Qo8h7xk4JpzuI7NcxvjJYTkS9aErFxi3vVH0aiZC0tmfaCqr8a2rJxyVwqreRpOjwAWrotMsf2wGsF4ofx5ScoFy5GB5fJkkdOrW1LyTvZAUCX3cstPr19+TNC5zZOk7WzZatnCkN5H5WzalWtZuu0oVL205KPOa3R8V2yv5e6fm0v5fTmqSuvjmaMJLXCN4QJkmIzojO99ckQ==
  • prime 2: x8exdMjVA2CiI+Thx7loHtVcevoeE2sZ7btRVAvmBqo+lkHwxb7FHRnWvuj6eJSlD2f0T50EewIhhiW3R9BmktCk7hXjbSCnC1u9Oxc1IAUm/7azRqyfCMx43XhLxpD+xkBCpWkKDLxGczsRwTuaP3lKS3bSdBrNlGmdblubvVBIq4YZ2vXVlnYtza0cS+dgCK7BGTqUsrCUd/ZbIvwcwZkZtpkhj1KQfto9X/0OMurBzAqbkeq1cyRHXHkOfN/qbUIIRqr9Ii7Eswf9Vk8xp2O1Nt8nzcYS9PFD12M5eyaeFEkEYfpNMNGuTzp/31oqVjbpoCxS6vuWAZyADxhISQ==
  • prime 3: is7d0LY4HoXszlC2NO7gejkq7XqL4p1W6hZJPYTNx+r37t1CC2n3Vvzg6kNdpRixDhIpXVTLjN9O7UO/XuqSumYKJIKoP52eb4Tg+a3hw5Iz2Zsb5lUTNSLgkQSBPAf71LHxbL82JL4g1nBUog8ae60BwnVArThKY4EwlJguGNw09BAU4lwf6csDl/nX2vfVwiAloYpeZkHL+L8m+bueGZM5KE2jEz+7ztZCI+T+E5i69rZEYDjx0lfLKlEhQlCW3HbCPELqXgNJJkRfi6MP9kXa9lSfnZmoT081RMvqonB/FUa4HOcKyCrw9XZEtnbNCIdbitfDVEX+pSSD7596wQ==
  • prime 4: GPs0injugfycacaeIP5jMa/WX55VEnKLDHom4k6WlfDF4L4gIGoJdekcPEUfxOI5faKvHyFwRP1wObkPoRBDM0qZxRfBl4zEtpvjHrd5MibSyJkM8+J0BIKk/nSjbRIGeb3hV5O56PvGB3S0dKhCUnuVObiC+ne7izplsD4OTG70l1Yud33UFntyoMxrxGYLUSqhBMmZfHquJg4NOWOzKNY/K+EcHDLj1Kjvkcgv9Vf7ocsVxvpFdD9uGPceQ6kwRDdEl6mb+6FDgWuXVyqR9+904oanEIkbJ7vfkthagLbEf57dyG6nJlqh5FBZWxGIR72YGypPuAh7qnnqXXjY2Q==
  • prime 5: CUWC+hRWOT421kwRllgVjy6FYv6jQUcgDNHeAiYZnf5HjS9iK2ki7v8G5dL/0f+Yf+NhE/4q8w4m8go51hACrVpP1p8GJDjiT09+RsOzITsHwl+ceEKoe56ZW6iDHBLlrNw5/MtcYhKpjNU9KJ2udm5J/c9iislcjgckrZG2IB8ADgXHMEByZ5DgaMl4AKZ1Gx8/q6KftTvmOT5rNTMLi76VN5KWQcDWK/DqXiOiZHM7Nr4dX4me3XeRgABJyNR8Fqxj3N1+HrYLe/zs7LOaK0++F9Ul3tLelhrhsvLxei3oCZkF9A/foD3on3luYA+1cRcxWpSY3h2J4/22+yo4+Q==

Here is a minimal code:

let n = base64::decode("wC8GyQvTCZOK+iiBR5fGQCmzRCTWX9TQ3aRG5gGFk0wB6EFoLMAyEEqeG3gS8xhAm2rSWYx9kKufvNat3iWlbSRVqkcbpVAYlj2vTrpqDpJl+6u+zxFYoUEBevlJJkAhl8EuCccOA30fVpcfRvXPTtvRd3yFT9E9EwZljtgSI02w7gZwg7VIxaGeajh5Euz6ZVQZ+qNRKgXrRC7gPRqVyI6Dt0Jc+Su5KBGNn0QcPDzOahWha1ieaeMkFisZ9mdpsJoZ4tw5eicLaUomKzALHXQVt+/rcZSrCd6/7uUo11B/CYBM4UfSpwXaL88J9AE6A5++no9hmJzaF2LLp+Qwx4yY3j9TDutxSAjsraxxJOGZ3XyA9nG++Ybt3cxZ5fP7ROjxCfROBmVv5dYn0O9OBIqYeCH6QraNpZMadlLNIhyMv8Y+P3r5l/PaK4VJaEi5pPosnEPawp0W0yZDzmjk2z1LthaRx0aZVrAjlH0Rb/6goLUQ9qu1xsDtQVVpN4A89ZUmtTWORnnJr0+595eHHxssd2gpzqf4bPjNITdAEuOCCtpvyi4ls23zwuzryUYjcUOEnsXNQ+DrZpLKxdtsD/qNV/j1hfeyBoPllC3cV+6bcGOFcVGbjYqb+Kw1b0+jL69RSKQqgmS+qYqr8c48nDRxyq3QXhR8qtzUwBFSLVk=").unwrap();
let e = base64::decode("AQAB").unwrap();
let d = base64::decode("qQazSQ+FRN7nVK1bRsROMRB8AmsDwLVEHivlz1V3Td2Dr+oW3YUMgxedhztML1IdQJPq/ad6qErJ6yRFNySVIjDaxzBTOEoB1eHa1btOnBJWb8rVvvjaorixvJ6Tn3i4EuhsvVy9DoR1k4rGj3qSIiFjUVvLRDAbLyhpGgEfsr0Z577yJmTC5E8JLRMOKX8Tmxsk3jPVpsgd65Hu1s8S/ZmabwuHCf9SkdMeY/1bd/9i7BqqJeeDLE4B5x1xcC3z3scqDUTzqGO+vZPhjgprPDRlBamVwgenhr7KwCn8iaLamFinRVwOAag8BeBqOJj7lURiOsKQa9FIX1kdFUS1QMQxgtPycLjkbvCJjriqT7zWKsmJ7l8YLs6Wmm9/+QJRwNCEVdMTXKfCP1cJjudaiskEQThfUldtgu8gUDNYbQ/Filb2eKfiX4h1TiMxZqUZHVZyb9nShbQoXJ3vj/MGVF0QM8TxhXM8r2Lv9gDYU5t9nQlUMLhs0jVjai48jHABbFNyH3sEcOmJOIwJrCXw1dzG7AotwyaEVUHOmL04TffmwCFfnyrLjbFgnyOeoyIIBYjcY7QFRm/9nupXMTH5hZ2qrHfCJIp0KK4tNBdQqmnHapFl5l6Le1s4qBS5bEIzjitobLvAFm9abPlDGfxmY6mlrMK4+nytwF9Ct7wc1AE=").unwrap();
let primes = vec![
    base64::decode("9kQWEAzsbzOcdPa+s5wFfw4XDd7bB1q9foZ31b1+TNjGNxbSBCFlDF1q98vwpV6nM8bWDh/wtbNoETSQDgpEnYOQ26LWEw6YY1+q1Q2GGEFceYUf+Myk8/vTc8TN6Zw0bKZBWy10Qo8h7xk4JpzuI7NcxvjJYTkS9aErFxi3vVH0aiZC0tmfaCqr8a2rJxyVwqreRpOjwAWrotMsf2wGsF4ofx5ScoFy5GB5fJkkdOrW1LyTvZAUCX3cstPr19+TNC5zZOk7WzZatnCkN5H5WzalWtZuu0oVL205KPOa3R8V2yv5e6fm0v5fTmqSuvjmaMJLXCN4QJkmIzojO99ckQ==").unwrap(),
    base64::decode("x8exdMjVA2CiI+Thx7loHtVcevoeE2sZ7btRVAvmBqo+lkHwxb7FHRnWvuj6eJSlD2f0T50EewIhhiW3R9BmktCk7hXjbSCnC1u9Oxc1IAUm/7azRqyfCMx43XhLxpD+xkBCpWkKDLxGczsRwTuaP3lKS3bSdBrNlGmdblubvVBIq4YZ2vXVlnYtza0cS+dgCK7BGTqUsrCUd/ZbIvwcwZkZtpkhj1KQfto9X/0OMurBzAqbkeq1cyRHXHkOfN/qbUIIRqr9Ii7Eswf9Vk8xp2O1Nt8nzcYS9PFD12M5eyaeFEkEYfpNMNGuTzp/31oqVjbpoCxS6vuWAZyADxhISQ==").unwrap(),
    base64::decode("is7d0LY4HoXszlC2NO7gejkq7XqL4p1W6hZJPYTNx+r37t1CC2n3Vvzg6kNdpRixDhIpXVTLjN9O7UO/XuqSumYKJIKoP52eb4Tg+a3hw5Iz2Zsb5lUTNSLgkQSBPAf71LHxbL82JL4g1nBUog8ae60BwnVArThKY4EwlJguGNw09BAU4lwf6csDl/nX2vfVwiAloYpeZkHL+L8m+bueGZM5KE2jEz+7ztZCI+T+E5i69rZEYDjx0lfLKlEhQlCW3HbCPELqXgNJJkRfi6MP9kXa9lSfnZmoT081RMvqonB/FUa4HOcKyCrw9XZEtnbNCIdbitfDVEX+pSSD7596wQ==").unwrap(),
    base64::decode("GPs0injugfycacaeIP5jMa/WX55VEnKLDHom4k6WlfDF4L4gIGoJdekcPEUfxOI5faKvHyFwRP1wObkPoRBDM0qZxRfBl4zEtpvjHrd5MibSyJkM8+J0BIKk/nSjbRIGeb3hV5O56PvGB3S0dKhCUnuVObiC+ne7izplsD4OTG70l1Yud33UFntyoMxrxGYLUSqhBMmZfHquJg4NOWOzKNY/K+EcHDLj1Kjvkcgv9Vf7ocsVxvpFdD9uGPceQ6kwRDdEl6mb+6FDgWuXVyqR9+904oanEIkbJ7vfkthagLbEf57dyG6nJlqh5FBZWxGIR72YGypPuAh7qnnqXXjY2Q==").unwrap(),
    base64::decode("CUWC+hRWOT421kwRllgVjy6FYv6jQUcgDNHeAiYZnf5HjS9iK2ki7v8G5dL/0f+Yf+NhE/4q8w4m8go51hACrVpP1p8GJDjiT09+RsOzITsHwl+ceEKoe56ZW6iDHBLlrNw5/MtcYhKpjNU9KJ2udm5J/c9iislcjgckrZG2IB8ADgXHMEByZ5DgaMl4AKZ1Gx8/q6KftTvmOT5rNTMLi76VN5KWQcDWK/DqXiOiZHM7Nr4dX4me3XeRgABJyNR8Fqxj3N1+HrYLe/zs7LOaK0++F9Ul3tLelhrhsvLxei3oCZkF9A/foD3on3luYA+1cRcxWpSY3h2J4/22+yo4+Q==").unwrap(),
];

RSAPrivateKey::from_components(
    BigUint::from_bytes_be(&n),
    BigUint::from_bytes_be(&e),
    BigUint::from_bytes_be(&d),
    primes
        .iter()
        .map(|p| BigUint::from_bytes_be(p))
        .collect(),
);

You should get a backtrace similar to:

thread '<unnamed>' panicked at 'invalid coeff', src/libcore/option.rs:1185:5
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/libunwind.rs:88
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at src/libstd/sys_common/backtrace.rs:77
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
             at src/libstd/sys_common/backtrace.rs:61
   4: core::fmt::write
             at src/libcore/fmt/mod.rs:1028
   5: std::io::Write::write_fmt
             at src/libstd/io/mod.rs:1412
   6: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:65
   7: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:50
   8: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:188
   9: std::panicking::default_hook
             at src/libstd/panicking.rs:205
  10: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:464
  11: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:373
  12: rust_begin_unwind
             at src/libstd/panicking.rs:302
  13: core::panicking::panic_fmt
             at src/libcore/panicking.rs:139
  14: core::option::expect_failed
             at src/libcore/option.rs:1185
  15: core::option::Option<T>::expect
             at /rustc/1423bec54cf2db283b614e527cfd602b481485d1/src/libcore/option.rs:345
  16: rsa::key::RSAPrivateKey::precompute::{{closure}}
             at /home/bcortier/.cargo/registry/src/github.com-1ecc6299db9ec823/rsa-0.1.4/src/key.rs:336
  17: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &mut F>::call_once
             at /rustc/1423bec54cf2db283b614e527cfd602b481485d1/src/libcore/ops/function.rs:275
  18: core::option::Option<T>::map
             at /rustc/1423bec54cf2db283b614e527cfd602b481485d1/src/libcore/option.rs:447
  19: <core::iter::adapters::Map<I,F> as core::iter::traits::iterator::Iterator>::next
             at /rustc/1423bec54cf2db283b614e527cfd602b481485d1/src/libcore/iter/adapters/mod.rs:710
  20: alloc::vec::Vec<T>::extend_desugared
             at /rustc/1423bec54cf2db283b614e527cfd602b481485d1/src/liballoc/vec.rs:2108
  21: <alloc::vec::Vec<T> as alloc::vec::SpecExtend<T,I>>::spec_extend
             at /rustc/1423bec54cf2db283b614e527cfd602b481485d1/src/liballoc/vec.rs:2005
  22: <alloc::vec::Vec<T> as alloc::vec::SpecExtend<T,I>>::from_iter
             at /rustc/1423bec54cf2db283b614e527cfd602b481485d1/src/liballoc/vec.rs:2000
  23: <alloc::vec::Vec<T> as core::iter::traits::collect::FromIterator<T>>::from_iter
             at /rustc/1423bec54cf2db283b614e527cfd602b481485d1/src/liballoc/vec.rs:1901
  24: core::iter::traits::iterator::Iterator::collect
             at /rustc/1423bec54cf2db283b614e527cfd602b481485d1/src/libcore/iter/traits/iterator.rs:1493
  25: rsa::key::RSAPrivateKey::precompute
             at /home/bcortier/.cargo/registry/src/github.com-1ecc6299db9ec823/rsa-0.1.4/src/key.rs:326
  26: rsa::key::RSAPrivateKey::from_components
             at /home/bcortier/.cargo/registry/src/github.com-1ecc6299db9ec823/rsa-0.1.4/src/key.rs:298
  27: picky::models::signature::SignatureHashType::sign
             at picky/src/models/signature.rs:58

It looks like the mod_inverse operation from num_bigint_dig returns None here: https://github.com/RustCrypto/RSA/blob/master/src/key.rs#L337

Hook up code-coverage

Hi there,

I was wondering if you could hook up code-coverage reporting (I personally like codecov, but coveralls and others would be fine too).

Once this is done, I will work on getting code coverage up to 100% (or as close as possible).

Check out the travis.yml file over at https://github.com/phayes/rsa-fdh for an example of how to do it.

Audit Status

Since this is one of the more widespread used rsa crates, it would be great if this could be audited by a third party. Are there currently any plans to do so?

Swappable RSADP for YubiKey support

While working on yubikey-piv I was testing the RSA logic by implementing OAEP around it. What I ended up doing was copying in all the logic from #18, and replacing:

    let mut em = {
        let mut c = BigUint::from_bytes_be(ciphertext);
        let mut m = internals::decrypt(rng, priv_key, &c)?;
        let em = internals::left_pad(&m.to_bytes_be(), k);

        c.zeroize();
        m.zeroize();

        em
    };

with

    let mut em = {
        let m = yubikey.decrypt_data(&ciphertext, algorithm, slot).unwrap();
        // I forgot to check if the padding was actually necessary
        left_pad(&m, k)
    };

It seems like the way to achieve this more generally (across all decryption schemes) would be with a trait of the form:

trait PrivateKey {
    /// Do NOT use directly! Only for implementors.
    fn raw_decryption_primitive<R: Rng>(
        &self,
        rng: Option<&mut R>,
        ciphertext: &[u8],
    ) -> Result<Vec<u8>>;

    /// Decrypt the given message.
    fn decrypt(&self, padding: PaddingScheme, ciphertext: &[u8]) -> Result<Vec<u8>> {
        ...
    }

    /// Decrypt the given message.
    /// Uses `rng` to blind the decryption process.
    pub fn decrypt_blinded<R: Rng>(
        &self,
        rng: &mut R,
        padding: PaddingScheme,
        ciphertext: &[u8],
    ) -> Result<Vec<u8>> {
        ...
    }

    fn decrypt_oaep(...) -> Result<...> {
        oaep::decrypt(...)
    }
}

impl PrivateKey for RSAPrivateKey {
    fn raw_decryption_primitive<R: Rng>(
        &self,
        rng: Option<&mut R>,
        ciphertext: &[u8],
    ) -> Result<Vec<u8>> {
        let mut c = BigUint::from_bytes_be(ciphertext);
        let mut m = internals::decrypt(rng, priv_key, &c)?;
        let em = internals::left_pad(&m.to_bytes_be(), k);

        c.zeroize();
        m.zeroize();

        em
    }
}

Then in yubikey-piv we could do something like:

struct YubiKeyRsaPrivateKey {
    yubikey: &mut YubiKey,
    algorithm: AlgorithmId,
    slot: SlotId,
}

impl PrivateKey for YubiKeyRsaPrivateKey {
    fn raw_decryption_primitive<R: Rng>(
        &self,
        _rng: Option<&mut R>,
        ciphertext: &[u8],
    ) -> Result<Vec<u8>> {
        self.yubikey.decrypt_data(&ciphertext, self.algorithm, self.slot).map_err(|e| e.into())
    }
}

Thoughts? The part I dislike about the above sketch is that the raw RSA decryption primitive is exposed in the API, but this needs to happen somewhere if this kind of interoperability and code de-duplication were to happen at all.

More than two primes

Hi,

I have this private key with 5 primes.

But when I load it using RSAPrivateKey::from_pkcs8, only two primes are kept because at
https://github.com/RustCrypto/RSA/blob/master/src/parse.rs#L169
only the two first primes are read.

    ...
    let prime1 = big_uint(try_asn1!(Integer(blocks.next()), "prime1"))?;
    let prime2 = big_uint(try_asn1!(Integer(blocks.next()), "prime2"))?;
    let primes = vec![prime1, prime2];
    Ok(RSAPrivateKey::from_components(n, e, d, primes))

So I tried to manually load my key and create a RSAPrivateKey using from_components. No issue to load. Signature and encryption operations are working (at least no error is returned).
However there is a panic on decryption:

panicked at 'assertion failed: `(left == right)`
  left: `256`,
 right: `640`: destination and source slices have different lengths'

This is failing inside the left_pad call here.

Behavior is the same regardless of the padding method used (currently PKCS1v15 and OAEP).

So, here is a question: are keys containing three primes and more intended to be supported in the current state? Current API allow to create RSAPrivateKey with an arbitrary amount of primes. Also no check is performed to fail pkcs8 load if more than two primes are presents.
I suggest to either:

  • fix pkcs8 loading to include other primes and fix decryption with them,
  • or to explicitly state in doc that only two primes are supported, change from_components function to not take a Vec but two arguments for prime1 and prime2 instead.

I'm ready to help and send PRs.

Fix undeterminism

key::tests::key_generation_128 has failed with "unwrap() on None". I've restarted the test and it got "fixed". IIUC you have non-determinism in your tests and implementation has a bug.

Ref: #1 (comment)

`pkcs8` crate

FYI, I've just published a pkcs8 crate which the https://github.com/RustCrypto/elliptic-curves projects are now using:

It's no_std-friendly, mostly self-contained including its own PEM parser (which depends only on alloc at present), and also provides pkcs8::FromPrivateKey and pkcs8::FromPublicKey traits which wrap up PKCS#8/SPKI (respectively) decoding from BER/DER and PEM formats, including loading keys directly from the filesystem.

I think it'd be great if the rsa crate adopted these and will work on a PR if I have time, unless someone else is interested.

One nice benefit would be making it easier for people to write libraries that work with e.g. either RSA(SSA) or ECDSA signatures.

Encoded keys missing required NULL parameter in AlgorithmIdentifier

As per the RFC 8017 [1], the NULL parameter SHALL be present if the OID in AlgorithmIdentifier is rsaEncryption. This crate currently omits the required NULL parameter.

From the RFC:

The object identifier rsaEncryption identifies RSA public and private
keys as defined in Appendices A.1.1 and A.1.2.  The parameters field
has associated with this OID in a value of type AlgorithmIdentifier
SHALL have a value of type NULL.

and:

When rsaEncryption is used in an AlgorithmIdentifier,
the parameters MUST be present and MUST be NULL.

[1] https://tools.ietf.org/html/rfc8017#appendix-A

3047 bit RSA with exp 3 extremely slow

I am trying to generate a key the following way and it is taking around 667 seconds:

let exp = BigUint::from_u64(3).unwrap();
let rsa = RSAPrivateKey::new_with_exp(&mut rng, 3072, &exp)?;

Generating the same length key with the default new() is much faster (~17s)

let rsa = RSAPrivateKey::new(&mut rng, 3072)?;

Is there any reason why this would be the case? Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.