Crate sad_rsa

Crate sad_rsa 

Source
Expand description

§sad-rsa

crates.io Documentation Build Status Apache2/MIT licensed MSRV

A hardened pure Rust RSA implementation with protection against timing side-channel attacks.

This is a security-focused fork of the RustCrypto RSA crate that implements implicit rejection for PKCS#1 v1.5 decryption to mitigate the Marvin Attack (RUSTSEC-2023-0071).

§Security Improvements

Featuresad-rsaupstream rsa
Marvin Attack mitigationYesNo
Implicit rejection (PKCS#1 v1.5)DefaultNot implemented
RFC 8017 length validationYesPartial
Key material zeroizationEnhancedBasic

§Implicit Rejection

Instead of returning distinguishable errors for invalid PKCS#1 v1.5 padding, this crate returns a deterministic pseudo-random message derived from the ciphertext. This makes valid and invalid ciphertexts indistinguishable to attackers, preventing padding oracle attacks.

Implementation follows draft-irtf-cfrg-rsa-guidance-04.

§Usage

Replace rsa with sad-rsa in your Cargo.toml:

[dependencies]
sad-rsa = "0.1"

The API is fully compatible with the upstream rsa crate:

use sad_rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey};

let mut rng = rand::thread_rng();
let bits = 2048;
let priv_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
let pub_key = RsaPublicKey::from(&priv_key);

// Encrypt
let data = b"hello world";
let enc_data = pub_key.encrypt(&mut rng, Pkcs1v15Encrypt, &data[..]).expect("failed to encrypt");
assert_ne!(&data[..], &enc_data[..]);

// Decrypt - now protected against Marvin attack
let dec_data = priv_key.decrypt(Pkcs1v15Encrypt, &enc_data).expect("failed to decrypt");
assert_eq!(&data[..], &dec_data[..]);

§Migration from rsa

  1. Replace rsa with sad-rsa in Cargo.toml
  2. Replace use rsa:: with use sad_rsa:: in your code
  3. That’s it - the API is identical

Note: Invalid ciphertexts will now return synthetic messages instead of errors. If your code explicitly checks for decryption errors to detect tampering, you should use authenticated encryption (e.g., RSA-OAEP or hybrid encryption with AES-GCM) instead.

§Performance

Note: Key generation is much faster when building with higher optimization levels:

[profile.dev]
opt-level = 2

§Minimum Supported Rust Version (MSRV)

This crate supports Rust 1.85 or higher.

§Attribution

This crate is a fork of the excellent RustCrypto RSA crate. We are grateful to the RustCrypto developers for their foundational work.

See the NOTICE file for full attribution details.

§License

Licensed under either of

at your option.

§Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

§Supported algorithms

This crate supports several schemes described in RFC8017:

These schemes are described below.

§Usage

§OAEP encryption

Note: requires sha2 feature of sad-rsa crate is enabled.

use sad_rsa::{RsaPrivateKey, RsaPublicKey, Oaep, sha2::Sha256};

let mut rng = rand::thread_rng();

let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
let public_key = RsaPublicKey::from(&private_key);

// Encrypt
let data = b"hello world";
let padding = Oaep::<Sha256>::new();
let enc_data = public_key.encrypt(&mut rng, padding, &data[..]).expect("failed to encrypt");
assert_ne!(&data[..], &enc_data[..]);

// Decrypt
let padding = Oaep::<Sha256>::new();
let dec_data = private_key.decrypt(padding, &enc_data).expect("failed to decrypt");
assert_eq!(&data[..], &dec_data[..]);

§PKCS#1 v1.5 encryption

use sad_rsa::{RsaPrivateKey, RsaPublicKey, Pkcs1v15Encrypt};

let mut rng = rand::thread_rng();

let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
let public_key = RsaPublicKey::from(&private_key);

// Encrypt
let data = b"hello world";
let enc_data = public_key.encrypt(&mut rng, Pkcs1v15Encrypt, &data[..]).expect("failed to encrypt");
assert_ne!(&data[..], &enc_data[..]);

// Decrypt
let dec_data = private_key.decrypt(Pkcs1v15Encrypt, &enc_data).expect("failed to decrypt");
assert_eq!(&data[..], &dec_data[..]);

§PKCS#1 v1.5 signatures

Note: requires sha2 feature of sad-rsa crate is enabled.

use sad_rsa::RsaPrivateKey;
use sad_rsa::pkcs1v15::{SigningKey, VerifyingKey};
use sad_rsa::signature::{Keypair, RandomizedSigner, SignatureEncoding, Verifier};
use sad_rsa::sha2::{Digest, Sha256};

let mut rng = rand::thread_rng();

let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
let signing_key = SigningKey::<Sha256>::new(private_key);
let verifying_key = signing_key.verifying_key();

// Sign
let data = b"hello world";
let signature = signing_key.sign_with_rng(&mut rng, data);
assert_ne!(signature.to_bytes().as_ref(), data.as_slice());

// Verify
verifying_key.verify(data, &signature).expect("failed to verify");

§PSS signatures

Note: requires sha2 feature of sad-rsa crate is enabled.

use sad_rsa::RsaPrivateKey;
use sad_rsa::pss::{BlindedSigningKey, VerifyingKey};
use sad_rsa::signature::{Keypair,RandomizedSigner, SignatureEncoding, Verifier};
use sad_rsa::sha2::{Digest, Sha256};

let mut rng = rand::thread_rng();

let bits = 2048;
let private_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
let signing_key = BlindedSigningKey::<Sha256>::new(private_key);
let verifying_key = signing_key.verifying_key();

// Sign
let data = b"hello world";
let signature = signing_key.sign_with_rng(&mut rng, data);
assert_ne!(signature.to_bytes().as_ref(), data);

// Verify
verifying_key.verify(data, &signature).expect("failed to verify");

§PKCS#1 RSA Key Encoding

PKCS#1 supports a legacy format for encoding RSA keys as binary (DER) or text (PEM) data.

You can recognize PEM encoded PKCS#1 keys because they have “RSA * KEY” in the type label, e.g.:

-----BEGIN RSA PRIVATE KEY-----

Most modern applications use the newer PKCS#8 format instead (see below).

The following traits can be used to decode/encode RsaPrivateKey and RsaPublicKey as PKCS#1. Note that pkcs1 is re-exported from the toplevel of the sad-rsa crate:

§Example

use sad_rsa::{RsaPublicKey, pkcs1::DecodeRsaPublicKey};

let pem = "-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEAtsQsUV8QpqrygsY+2+JCQ6Fw8/omM71IM2N/R8pPbzbgOl0p78MZ
GsgPOQ2HSznjD0FPzsH8oO2B5Uftws04LHb2HJAYlz25+lN5cqfHAfa3fgmC38Ff
wBkn7l582UtPWZ/wcBOnyCgb3yLcvJrXyrt8QxHJgvWO23ITrUVYszImbXQ67YGS
0YhMrbixRzmo2tpm3JcIBtnHrEUMsT0NfFdfsZhTT8YbxBvA8FdODgEwx7u/vf3J
9qbi4+Kv8cvqyJuleIRSjVXPsIMnoejIn04APPKIjpMyQdnWlby7rNyQtE4+CV+j
cFjqJbE/Xilcvqxt6DirjFCvYeKYl1uHLwIDAQAB
-----END RSA PUBLIC KEY-----";

let public_key = RsaPublicKey::from_pkcs1_pem(pem)?;

§PKCS#8 RSA Key Encoding

PKCS#8 is a private key format with support for multiple algorithms. Like PKCS#1, it can be encoded as binary (DER) or text (PEM).

You can recognize PEM encoded PKCS#8 keys because they don’t have an algorithm name in the type label, e.g.:

-----BEGIN PRIVATE KEY-----

The following traits can be used to decode/encode RsaPrivateKey and RsaPublicKey as PKCS#8. Note that pkcs8 is re-exported from the toplevel of the sad-rsa crate:

§Example

use sad_rsa::{RsaPublicKey, pkcs8::DecodePublicKey};

let pem = "-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtsQsUV8QpqrygsY+2+JC
Q6Fw8/omM71IM2N/R8pPbzbgOl0p78MZGsgPOQ2HSznjD0FPzsH8oO2B5Uftws04
LHb2HJAYlz25+lN5cqfHAfa3fgmC38FfwBkn7l582UtPWZ/wcBOnyCgb3yLcvJrX
yrt8QxHJgvWO23ITrUVYszImbXQ67YGS0YhMrbixRzmo2tpm3JcIBtnHrEUMsT0N
fFdfsZhTT8YbxBvA8FdODgEwx7u/vf3J9qbi4+Kv8cvqyJuleIRSjVXPsIMnoejI
n04APPKIjpMyQdnWlby7rNyQtE4+CV+jcFjqJbE/Xilcvqxt6DirjFCvYeKYl1uH
LwIDAQAB
-----END PUBLIC KEY-----";

let public_key = RsaPublicKey::from_public_key_pem(pem)?;

Re-exports§

pub use crate::errors::Error;
pub use crate::errors::Result;
pub use crate::oaep::Oaep;
pub use crate::pkcs1v15::Pkcs1v15Encrypt;
pub use crate::pkcs1v15::Pkcs1v15Sign;
pub use crate::pss::Pss;
pub use rand_core;
pub use signature;
pub use pkcs1;encoding
pub use pkcs8;encoding
pub use sha2;sha2

Modules§

errors
Error types.
hazmathazmat
⚠️ Low-level “hazmat” RSA functions.
oaep
Encryption and Decryption using OAEP padding.
pkcs1v15
PKCS#1 v1.5 support as described in RFC8017 § 8.2.
pss
Support for the Probabilistic Signature Scheme (PSS) a.k.a. RSASSA-PSS.
traits
RSA-related trait definitions.

Structs§

BoxedUint
Fixed-precision heap-allocated big unsigned integer.
CrtValue
Contains the precomputed Chinese remainder theorem values.
RsaPrivateKey
Represents a whole RSA key, public and private parts.
RsaPublicKey
Represents the public part of an RSA key.