rustcrypto / rsa Goto Github PK
View Code? Open in Web Editor NEWRSA implementation in pure Rust
License: Apache License 2.0
RSA implementation in pure Rust
License: Apache License 2.0
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?
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?
how can i export the keypair to file.pem
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.
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:
new()
? When is it necessary to call this function?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.
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!
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?
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.
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 modulusd
: private exponente
: public exponentH
: hash functionm
: messageI
: to-be-singed RSA PKCS#1 v1.5 signature scheme input structureS
: 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
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.
there is no decrypt api for publickey.
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:
rsa::RSAPublicKey::from_pkcs8
to replace the java code X509EncodedKeySpec
? It seems different but I can't find any other suitable replace code.PaddingScheme
in java code. How can I use it correctly?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!
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
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.
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 ?
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.
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.
subtle 2.3's MSRV is 1.41+. This is causing the build to break, since CI's MSRV is 1.36:
https://github.com/RustCrypto/RSA/pull/64/checks?check_run_id=1146119487
@dignifiedquire any preferences on how to address this? Seems we either need to lock to earlier releases of subtle
or bump MSRV (personally I'd prefer the latter)
Extracted from #104 (comment)
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)`
...
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.
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
.
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?
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
Probably using: https://github.com/rust-fuzz/cargo-fuzz
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:
crypto
then decrypt with this online tool. it works.So I can infer that it's the oaep padding lead to incompatible.
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:
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:
If I load the openssl generated key into the lapo.it tool, I find the following:
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.
Error types created by failure
do not implement std::error::Error
which prevents usage of the ?
operator. The thiserror
crate provides similar functionality but is fully compatible with the standard library and RFC2504.
I'm happy to submit a PR with the changes but I want collect feedback first.
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?
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?
i want to encrypt long text with RSAPublicKey , but it exception that message too long . then encrypt in batches with RSAPublicKey , but an error is reported description when decrypting . is there any good way please?
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).
Interesting paper which contains techniques for improving both key generation and inversion-free RSA-CRT:
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?
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
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.
Is there a feature that can save/export private key easily?
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 :
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
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
I hope that I can encrypt words with public key, but I find there isnt the scheme of no_padding.
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.
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?
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.
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:
from_components
function to not take a Vec
but two arguments for prime1
and prime2
instead.I'm ready to help and send PRs.
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)
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.
Line 33 in 380c7e3
I traced encryption and discovered that this copy over-writes the header previously created!
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.
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.