Add missing rustdoc comments; enable missing_docs lint (#216)

Several types and methods were missing documentation.

This commit adds document and enables warnings for `missing_docs`.

Additionally it updates all references to PKCS#1 RFCs to use RFC8017,
which documents the latest version of PKCS#1.
This commit is contained in:
Tony Arcieri 2022-10-31 14:15:52 -06:00 committed by GitHub
parent a857c8f785
commit eeb18ee88d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 201 additions and 66 deletions

View File

@ -1,4 +1,5 @@
use alloc::vec;
//! Useful algorithms related to RSA.
use digest::{Digest, DynDigest, FixedOutputReset};
use num_bigint::traits::ModInverse;
use num_bigint::{BigUint, RandPrime};

View File

@ -1,26 +1,64 @@
//! Error types.
/// Alias for [`core::result::Result`] with the `rsa` crate's [`Error`] type.
pub type Result<T> = core::result::Result<T, Error>;
/// Error types
#[derive(Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Error {
/// Invalid padding scheme.
InvalidPaddingScheme,
/// Decryption error.
Decryption,
/// Verification error.
Verification,
/// Message too long.
MessageTooLong,
/// Input must be hashed.
InputNotHashed,
/// Number of primes must be 2 or greater.
NprimesTooSmall,
/// Too few primes of a given length to generate an RSA key.
TooFewPrimes,
/// Invalid prime value.
InvalidPrime,
/// Invalid modulus.
InvalidModulus,
/// Invalid exponent.
InvalidExponent,
/// Invalid coefficient.
InvalidCoefficient,
/// Modulus too large.
ModulusTooLarge,
/// Public exponent too small.
PublicExponentTooSmall,
/// Public exponent too large.
PublicExponentTooLarge,
/// PKCS#1 error.
Pkcs1(pkcs1::Error),
/// PKCS#8 error.
Pkcs8(pkcs8::Error),
/// Internal error.
Internal,
/// Label too long.
LabelTooLong,
}

View File

@ -17,6 +17,7 @@ use crate::padding::PaddingScheme;
use crate::raw::{DecryptionPrimitive, EncryptionPrimitive};
use crate::{oaep, pkcs1v15, pss};
/// Components of an RSA public key.
pub trait PublicKeyParts {
/// Returns the modulus of the key.
fn n(&self) -> &BigUint;

View File

@ -1,9 +1,17 @@
//! RSA Implementation in pure Rust.
//!
//! It supports several schemes described in [RFC8017]:
//!
//! - OAEP encryption scheme
//! - PKCS#1 v1.5 encryption scheme
//! - PKCS#1 v1.5 signature scheme
//! - PSS signature scheme
//!
//! These schemes are described below.
//!
//! # Usage
//!
//! Using PKCS1v15.
//! Using PKCS#1 v1.5.
//! ```
//! use rsa::{PublicKey, RsaPrivateKey, RsaPublicKey, PaddingScheme};
//!
@ -47,7 +55,7 @@
//! assert_eq!(&data[..], &dec_data[..]);
//! ```
//!
//! Using PKCS1v15 signatures
//! Using PKCS#1 v1.5 signatures
//! ```
//! use rsa::RsaPrivateKey;
//! use rsa::pkcs1v15::{SigningKey, VerifyingKey};
@ -95,8 +103,8 @@
//!
//! ## PKCS#1 RSA Key Encoding
//!
//! PKCS#1 is a legacy format for encoding RSA keys as binary (DER) or text
//! (PEM) data.
//! 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.:
@ -112,8 +120,8 @@
//! toplevel of the `rsa` crate:
//!
//! - [`pkcs1::DecodeRsaPrivateKey`]: decode RSA private keys from PKCS#1
//! - [`pkcs1::DecodeRsaPublicKey`]: decode RSA public keys from PKCS#1
//! - [`pkcs1::EncodeRsaPrivateKey`]: encode RSA private keys to PKCS#1
//! - [`pkcs1::DecodeRsaPublicKey`]: decode RSA public keys from PKCS#1
//! - [`pkcs1::EncodeRsaPublicKey`]: encode RSA public keys to PKCS#1
//!
//! ### Example
@ -156,8 +164,8 @@
//! toplevel of the `rsa` crate:
//!
//! - [`pkcs8::DecodePrivateKey`]: decode private keys from PKCS#8
//! - [`pkcs8::DecodePublicKey`]: decode public keys from PKCS#8
//! - [`pkcs8::EncodePrivateKey`]: encode private keys to PKCS#8
//! - [`pkcs8::DecodePublicKey`]: decode public keys from PKCS#8
//! - [`pkcs8::EncodePublicKey`]: encode public keys to PKCS#8
//!
//! ### Example
@ -183,10 +191,17 @@
//! # Ok(())
//! # }
//! ```
//!
//! [RFC8017]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
//!
// TODO(tarcieri): figure out why rustdoc isn't rendering these links correctly
//! [`pkcs8::DecodePublicKey`]: https://docs.rs/pkcs8/latest/pkcs8/trait.DecodePublicKey.html
//! [`pkcs8::EncodePublicKey`]: https://docs.rs/pkcs8/latest/pkcs8/trait.EncodePublicKey.html
#![cfg_attr(not(test), no_std)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc(html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo_small.png")]
#![warn(missing_docs)]
#[macro_use]
extern crate alloc;
@ -196,15 +211,10 @@ extern crate std;
pub use num_bigint::BigUint;
pub use rand_core;
/// Useful algorithms.
pub mod algorithms;
/// Error types.
pub mod errors;
/// Supported padding schemes.
pub mod padding;
/// RSASSA-PKCS1-v1_5 Signature support
pub mod pkcs1v15;
/// RSASSA-PSS Signature support
pub mod pss;
mod dummy_rng;

View File

@ -15,9 +15,13 @@ use crate::key::{self, PrivateKey, PublicKey};
// TODO: This is the maximum for SHA-1, unclear from the RFC what the values are for other hashing functions.
const MAX_LABEL_LEN: u64 = 2_305_843_009_213_693_951;
/// Encrypts the given message with RSA and the padding
/// scheme from [PKCS#1 OAEP](https://datatracker.ietf.org/doc/html/rfc3447#section-7.1.1). The message must be no longer than the
/// length of the public modulus minus (2+ 2*hash.size()).
/// Encrypts the given message with RSA and the padding scheme from
/// [PKCS#1 OAEP].
///
/// The message must be no longer than the length of the public modulus minus
/// `2 + (2 * hash.size())`.
///
/// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[inline]
pub fn encrypt<R: RngCore + CryptoRng, K: PublicKey>(
rng: &mut R,
@ -63,14 +67,18 @@ pub fn encrypt<R: RngCore + CryptoRng, K: PublicKey>(
pub_key.raw_encryption_primitive(&em, pub_key.size())
}
/// Decrypts a plaintext using RSA and the padding scheme from [pkcs1# OAEP](https://datatracker.ietf.org/doc/html/rfc3447#section-7.1.2)
/// Decrypts a plaintext using RSA and the padding scheme from [PKCS#1 OAEP].
///
/// If an `rng` is passed, it uses RSA blinding to avoid timing side-channel attacks.
///
/// Note that whether this function returns an error or not discloses secret
/// information. If an attacker can cause this function to run repeatedly and
/// learn whether each instance returned an error then they can decrypt and
/// forge signatures as if they had the private key. See
/// `decrypt_session_key` for a way of solving this problem.
/// forge signatures as if they had the private key.
///
/// See `decrypt_session_key` for a way of solving this problem.
///
/// [PKCS#1 OAEP]: https://datatracker.ietf.org/doc/html/rfc8017#section-7.1
#[inline]
pub fn decrypt<R: RngCore + CryptoRng, SK: PrivateKey>(
rng: Option<&mut R>,

View File

@ -1,3 +1,5 @@
//! Supported padding schemes.
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use core::fmt;
@ -11,29 +13,44 @@ use crate::pkcs1v15;
pub enum PaddingScheme {
/// Encryption and Decryption using PKCS1v15 padding.
PKCS1v15Encrypt,
/// Sign and Verify using PKCS1v15 padding.
PKCS1v15Sign {
/// Length of hash to use.
hash_len: Option<usize>,
/// Prefix.
prefix: Box<[u8]>,
},
/// Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc3447#section-7.1.1).
/// Encryption and Decryption using [OAEP padding](https://datatracker.ietf.org/doc/html/rfc8017#section-7.1).
///
/// - `digest` is used to hash the label. The maximum possible plaintext length is `m = k - 2 * h_len - 2`,
/// where `k` is the size of the RSA modulus.
/// - `mgf_digest` specifies the hash function that is used in the [MGF1](https://datatracker.ietf.org/doc/html/rfc2437#section-10.2.1).
/// where `k` is the size of the RSA modulus.
/// - `mgf_digest` specifies the hash function that is used in the [MGF1](https://datatracker.ietf.org/doc/html/rfc8017#appendix-B.2).
/// - `label` is optional data that can be associated with the message.
///
/// The two hash functions can, but don't need to be the same.
///
/// A prominent example is the [`AndroidKeyStore`](https://developer.android.com/guide/topics/security/cryptography#oaep-mgf1-digest).
/// It uses SHA-1 for `mgf_digest` and a user-chosen SHA flavour for `digest`.
OAEP {
/// Digest type to use.
digest: Box<dyn DynDigest + Send + Sync>,
/// Digest to use for Mask Generation Function (MGF).
mgf_digest: Box<dyn DynDigest + Send + Sync>,
/// Optional label.
label: Option<String>,
},
/// Sign and Verify using PSS padding.
PSS {
/// Digest type to use.
digest: Box<dyn DynDigest + Send + Sync>,
/// Salt length.
salt_len: Option<usize>,
},
}
@ -58,10 +75,14 @@ impl fmt::Debug for PaddingScheme {
}
impl PaddingScheme {
/// Create new PKCS#1 v1.5 encryption padding.
pub fn new_pkcs1v15_encrypt() -> Self {
PaddingScheme::PKCS1v15Encrypt
}
/// Create new PKCS#1 v1.5 padding for computing a raw signature.
///
/// This sets `hash_len` to `None` and uses an empty `prefix`.
pub fn new_pkcs1v15_sign_raw() -> Self {
PaddingScheme::PKCS1v15Sign {
hash_len: None,
@ -69,6 +90,10 @@ impl PaddingScheme {
}
}
/// Create new PKCS#1 v1.5 padding for the given digest.
///
/// The digest must have an [`AssociatedOid`]. Make sure to enable the `oid`
/// feature of the relevant digest crate.
pub fn new_pkcs1v15_sign<D>() -> Self
where
D: Digest + AssociatedOid,
@ -159,6 +184,7 @@ impl PaddingScheme {
}
}
/// New PSS padding for the given digest.
pub fn new_pss<T: 'static + Digest + DynDigest + Send + Sync>() -> Self {
PaddingScheme::PSS {
digest: Box::new(T::new()),
@ -166,6 +192,7 @@ impl PaddingScheme {
}
}
/// New PSS padding for the given digest with a salt value of the given length.
pub fn new_pss_with_salt<T: 'static + Digest + DynDigest + Send + Sync>(len: usize) -> Self {
PaddingScheme::PSS {
digest: Box::new(T::new()),

View File

@ -1,4 +1,7 @@
use alloc::vec;
//! PKCS#1 v1.5 support as described in [RFC8017 § 8.2].
//!
//! [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
use alloc::vec::Vec;
use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
use core::marker::PhantomData;
@ -20,6 +23,9 @@ use crate::errors::{Error, Result};
use crate::key::{self, PrivateKey, PublicKey};
use crate::{RsaPrivateKey, RsaPublicKey};
/// PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Clone)]
pub struct Signature {
bytes: Vec<u8>,
@ -95,9 +101,9 @@ impl Display for Signature {
}
}
// Encrypts the given message with RSA and the padding
// scheme from PKCS#1 v1.5. The message must be no longer than the
// length of the public modulus minus 11 bytes.
/// Encrypts the given message with RSA and the padding
/// scheme from PKCS#1 v1.5. The message must be no longer than the
/// length of the public modulus minus 11 bytes.
#[inline]
pub(crate) fn encrypt<R: RngCore + CryptoRng, PK: PublicKey>(
rng: &mut R,
@ -122,13 +128,14 @@ pub(crate) fn encrypt<R: RngCore + CryptoRng, PK: PublicKey>(
}
/// Decrypts a plaintext using RSA and the padding scheme from PKCS#1 v1.5.
// If an `rng` is passed, it uses RSA blinding to avoid timing side-channel attacks.
//
// Note that whether this function returns an error or not discloses secret
// information. If an attacker can cause this function to run repeatedly and
// learn whether each instance returned an error then they can decrypt and
// forge signatures as if they had the private key. See
// `decrypt_session_key` for a way of solving this problem.
///
/// If an `rng` is passed, it uses RSA blinding to avoid timing side-channel attacks.
///
/// Note that whether this function returns an error or not discloses secret
/// information. If an attacker can cause this function to run repeatedly and
/// learn whether each instance returned an error then they can decrypt and
/// forge signatures as if they had the private key. See
/// `decrypt_session_key` for a way of solving this problem.
#[inline]
pub(crate) fn decrypt<R: RngCore + CryptoRng, SK: PrivateKey>(
rng: Option<&mut R>,
@ -145,19 +152,19 @@ pub(crate) fn decrypt<R: RngCore + CryptoRng, SK: PrivateKey>(
Ok(out[index as usize..].to_vec())
}
// Calculates the signature of hashed using
// RSASSA-PKCS1-V1_5-SIGN from RSA PKCS#1 v1.5. Note that `hashed` must
// be the result of hashing the input message using the given hash
// function. If hash is `None`, hashed is signed directly. This isn't
// advisable except for interoperability.
//
// If `rng` is not `None` then RSA blinding will be used to avoid timing
// side-channel attacks.
//
// This function is deterministic. Thus, if the set of possible
// messages is small, an attacker may be able to build a map from
// messages to signatures and identify the signed messages. As ever,
// signatures provide authenticity, not confidentiality.
/// Calculates the signature of hashed using
/// RSASSA-PKCS1-V1_5-SIGN from RSA PKCS#1 v1.5. Note that `hashed` must
/// be the result of hashing the input message using the given hash
/// function. If hash is `None`, hashed is signed directly. This isn't
/// advisable except for interoperability.
///
/// If `rng` is not `None` then RSA blinding will be used to avoid timing
/// side-channel attacks.
///
/// This function is deterministic. Thus, if the set of possible
/// messages is small, an attacker may be able to build a map from
/// messages to signatures and identify the signed messages. As ever,
/// signatures provide authenticity, not confidentiality.
#[inline]
pub(crate) fn sign<R: RngCore + CryptoRng, SK: PrivateKey>(
rng: Option<&mut R>,
@ -218,7 +225,7 @@ pub(crate) fn verify<PK: PublicKey>(
Ok(())
}
// prefix = 0x30 <oid_len + 8 + digest_len> 0x30 <oid_len + 4> 0x06 <oid_len> oid 0x05 0x00 0x04 <digest_len>
/// prefix = 0x30 <oid_len + 8 + digest_len> 0x30 <oid_len + 4> 0x06 <oid_len> oid 0x05 0x00 0x04 <digest_len>
#[inline]
pub(crate) fn generate_prefix<D>() -> Vec<u8>
where
@ -305,6 +312,9 @@ fn non_zero_random_bytes<R: RngCore + CryptoRng>(rng: &mut R, data: &mut [u8]) {
}
}
/// Signing key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Debug, Clone)]
pub struct SigningKey<D>
where
@ -319,14 +329,7 @@ impl<D> SigningKey<D>
where
D: Digest,
{
pub(crate) fn key(&self) -> &RsaPrivateKey {
&self.inner
}
pub(crate) fn prefix(&self) -> Vec<u8> {
self.prefix.clone()
}
/// Create a new signing key from the give RSA private key.
pub fn new(key: RsaPrivateKey) -> Self {
Self {
inner: key,
@ -334,6 +337,14 @@ where
phantom: Default::default(),
}
}
pub(crate) fn key(&self) -> &RsaPrivateKey {
&self.inner
}
pub(crate) fn prefix(&self) -> Vec<u8> {
self.prefix.clone()
}
}
impl<D> From<RsaPrivateKey> for SigningKey<D>
@ -358,6 +369,7 @@ impl<D> SigningKey<D>
where
D: Digest + AssociatedOid,
{
/// Create a new verifying key with a prefix for the digest `D`.
pub fn new_with_prefix(key: RsaPrivateKey) -> Self {
Self {
inner: key,
@ -454,6 +466,9 @@ where
}
}
/// Verifying key for PKCS#1 v1.5 signatures as described in [RFC8017 § 8.2].
///
/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2
#[derive(Debug, Clone)]
pub struct VerifyingKey<D>
where
@ -468,6 +483,7 @@ impl<D> VerifyingKey<D>
where
D: Digest,
{
/// Create a new verifying key from an RSA public key.
pub fn new(key: RsaPublicKey) -> Self {
Self {
inner: key,
@ -499,6 +515,7 @@ impl<D> VerifyingKey<D>
where
D: Digest + AssociatedOid,
{
/// Create a new verifying key with a prefix for the digest `D`.
pub fn new_with_prefix(key: RsaPublicKey) -> Self {
Self {
inner: key,

View File

@ -1,4 +1,10 @@
use alloc::vec;
//! Support for the [Probabilistic Signature Scheme] (PSS) a.k.a. RSASSA-PSS.
//!
//! Designed by Mihir Bellare and Phillip Rogaway. Specified in [RFC8017 § 8.1].
//!
//! [Probabilistic Signature Scheme]: https://en.wikipedia.org/wiki/Probabilistic_signature_scheme
//! [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
use alloc::vec::Vec;
use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex};
@ -19,6 +25,9 @@ use crate::errors::{Error, Result};
use crate::key::{PrivateKey, PublicKey};
use crate::{RsaPrivateKey, RsaPublicKey};
/// RSASSA-PSS signatures as described in [RFC8017 § 8.1].
///
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
#[derive(Clone)]
pub struct Signature {
bytes: Vec<u8>,
@ -128,6 +137,7 @@ where
}
/// SignPSS calculates the signature of hashed using RSASSA-PSS.
///
/// Note that hashed must be the result of hashing the input message using the
/// given hash function. The opts argument may be nil, in which case sensible
/// defaults are used.
@ -172,6 +182,7 @@ fn generate_salt<T: RngCore + ?Sized, SK: PrivateKey>(
}
/// signPSSWithSalt calculates the signature of hashed using PSS with specified salt.
///
/// Note that hashed must be the result of hashing the input message using the
/// given hash function. salt is a random sequence of bytes whose length will be
/// later used to verify the signature.
@ -521,6 +532,10 @@ where
}
}
/// Signing key for producing RSASSA-PSS signatures as described in
/// [RFC8017 § 8.1].
///
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
#[derive(Debug, Clone)]
pub struct SigningKey<D>
where
@ -535,10 +550,7 @@ impl<D> SigningKey<D>
where
D: Digest,
{
pub(crate) fn key(&self) -> &RsaPrivateKey {
&self.inner
}
/// Create a new RSASSA-PSS signing key.
pub fn new(key: RsaPrivateKey) -> Self {
Self {
inner: key,
@ -547,6 +559,7 @@ where
}
}
/// Create a new RSASSA-PSS signing key with a salt of the given length.
pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self {
Self {
inner: key,
@ -554,6 +567,10 @@ where
phantom: Default::default(),
}
}
pub(crate) fn key(&self) -> &RsaPrivateKey {
&self.inner
}
}
impl<D> From<RsaPrivateKey> for SigningKey<D>
@ -644,6 +661,8 @@ where
}
}
/// Signing key for producing "blinded" RSASSA-PSS signatures as described in
/// [draft-irtf-cfrg-rsa-blind-signatures](https://datatracker.ietf.org/doc/draft-irtf-cfrg-rsa-blind-signatures/).
#[derive(Debug, Clone)]
pub struct BlindedSigningKey<D>
where
@ -658,10 +677,8 @@ impl<D> BlindedSigningKey<D>
where
D: Digest,
{
pub(crate) fn key(&self) -> &RsaPrivateKey {
&self.inner
}
/// Create a new RSASSA-PSS signing key which produces "blinded"
/// signatures.
pub fn new(key: RsaPrivateKey) -> Self {
Self {
inner: key,
@ -670,6 +687,8 @@ where
}
}
/// Create a new RSASSA-PSS signing key which produces "blinded"
/// signatures with a salt of the given length.
pub fn new_with_salt_len(key: RsaPrivateKey, salt_len: usize) -> Self {
Self {
inner: key,
@ -677,6 +696,10 @@ where
phantom: Default::default(),
}
}
pub(crate) fn key(&self) -> &RsaPrivateKey {
&self.inner
}
}
impl<D> From<RsaPrivateKey> for BlindedSigningKey<D>
@ -767,6 +790,10 @@ where
}
}
/// Verifying key for checking the validity of RSASSA-PSS signatures as
/// described in [RFC8017 § 8.1].
///
/// [RFC8017 § 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1
#[derive(Debug, Clone)]
pub struct VerifyingKey<D>
where
@ -780,6 +807,7 @@ impl<D> VerifyingKey<D>
where
D: Digest,
{
/// Create a new RSASSA-PSS verifying key.
pub fn new(key: RsaPublicKey) -> Self {
Self {
inner: key,
@ -1195,7 +1223,8 @@ mod test {
let verifying_key = VerifyingKey::<Sha1>::new(pub_key);
for (text, sig, expected) in &tests {
let result = verifying_key.verify_prehash(text.as_ref(), &Signature::from_bytes(sig).unwrap());
let result =
verifying_key.verify_prehash(text.as_ref(), &Signature::from_bytes(sig).unwrap());
match expected {
true => result.expect("failed to verify"),
false => {
@ -1216,7 +1245,9 @@ mod test {
let verifying_key = VerifyingKey::from(&signing_key);
for test in &tests {
let sig = signing_key.sign_prehash_with_rng(&mut rng, &test).expect("failed to sign");
let sig = signing_key
.sign_prehash_with_rng(&mut rng, &test)
.expect("failed to sign");
verifying_key
.verify_prehash(&test, &sig)
.expect("failed to verify");
@ -1234,7 +1265,9 @@ mod test {
let verifying_key = VerifyingKey::from(&signing_key);
for test in &tests {
let sig = signing_key.sign_prehash_with_rng(&mut rng, &test).expect("failed to sign");
let sig = signing_key
.sign_prehash_with_rng(&mut rng, &test)
.expect("failed to sign");
verifying_key
.verify_prehash(&test, &sig)
.expect("failed to verify");