From 81d1ee797e33f55e5d027f716ea7f5401b5dfe9d Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sat, 29 Oct 2022 10:47:23 -0600 Subject: [PATCH] Add missing rustdoc comments; enable `missing_docs` lint 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. --- src/errors.rs | 36 ++++++++++++++++++++++ src/key.rs | 1 + src/lib.rs | 20 ++++++++++--- src/oaep.rs | 20 +++++++++---- src/padding.rs | 31 +++++++++++++++++-- src/pkcs1v15.rs | 79 ++++++++++++++++++++++++++++--------------------- src/pss.rs | 49 +++++++++++++++++++++++------- 7 files changed, 179 insertions(+), 57 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 53339950..da048630 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,26 +1,62 @@ +/// Alias for [`core::result::Result`] with the `rsa` crate's [`Error`] type. pub type Result = core::result::Result; /// 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, } diff --git a/src/key.rs b/src/key.rs index 12e60a77..6c0f1f9e 100644 --- a/src/key.rs +++ b/src/key.rs @@ -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; diff --git a/src/lib.rs b/src/lib.rs index 14f33e92..ac8c66f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,8 +112,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 +156,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 +183,14 @@ //! # Ok(()) //! # } //! ``` +//! +//! [`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; @@ -202,9 +206,17 @@ pub mod algorithms; pub mod errors; /// Supported padding schemes. pub mod padding; -/// RSASSA-PKCS1-v1_5 Signature support +/// PKCS#1 v1.5 support as described in [RFC8017 § 8.2]. +/// +/// [RFC8017 § 8.2]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.2 pub mod pkcs1v15; -/// RSASSA-PSS Signature support +/// Support for the [Probabilistic Signature Scheme] (PSS) designed by +/// Mihir Bellare and Phillip Rogaway, a.k.a. RSASSA-PSS. +/// +/// Specified in [RFC3447 § 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 pub mod pss; mod dummy_rng; diff --git a/src/oaep.rs b/src/oaep.rs index ee25b0a5..adc166d6 100644 --- a/src/oaep.rs +++ b/src/oaep.rs @@ -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( rng: &mut R, @@ -63,14 +67,18 @@ pub fn encrypt( 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( rng: Option<&mut R>, diff --git a/src/padding.rs b/src/padding.rs index 71c3e2ce..0801a120 100644 --- a/src/padding.rs +++ b/src/padding.rs @@ -11,29 +11,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, + + /// 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, + + /// Digest to use for Mask Generation Function (MGF). mgf_digest: Box, + + /// Optional label. label: Option, }, + /// Sign and Verify using PSS padding. PSS { + /// Digest type to use. digest: Box, + + /// Salt length. salt_len: Option, }, } @@ -58,10 +73,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 +88,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() -> Self where D: Digest + AssociatedOid, @@ -159,6 +182,7 @@ impl PaddingScheme { } } + /// New PSS padding for the given digest. pub fn new_pss() -> Self { PaddingScheme::PSS { digest: Box::new(T::new()), @@ -166,6 +190,7 @@ impl PaddingScheme { } } + /// New PSS padding for the given digest with a salt value of the given length. pub fn new_pss_with_salt(len: usize) -> Self { PaddingScheme::PSS { digest: Box::new(T::new()), diff --git a/src/pkcs1v15.rs b/src/pkcs1v15.rs index 5fb377e8..a7b88c93 100644 --- a/src/pkcs1v15.rs +++ b/src/pkcs1v15.rs @@ -1,4 +1,3 @@ -use alloc::vec; use alloc::vec::Vec; use core::fmt::{Debug, Display, Formatter, LowerHex, UpperHex}; use core::marker::PhantomData; @@ -20,6 +19,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, @@ -95,9 +97,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( rng: &mut R, @@ -122,13 +124,14 @@ pub(crate) fn encrypt( } /// 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( rng: Option<&mut R>, @@ -145,19 +148,19 @@ pub(crate) fn decrypt( 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( rng: Option<&mut R>, @@ -218,7 +221,7 @@ pub(crate) fn verify( Ok(()) } -// prefix = 0x30 0x30 0x06 oid 0x05 0x00 0x04 +/// prefix = 0x30 0x30 0x06 oid 0x05 0x00 0x04 #[inline] pub(crate) fn generate_prefix() -> Vec where @@ -305,6 +308,9 @@ fn non_zero_random_bytes(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 where @@ -319,14 +325,7 @@ impl SigningKey where D: Digest, { - pub(crate) fn key(&self) -> &RsaPrivateKey { - &self.inner - } - - pub(crate) fn prefix(&self) -> Vec { - 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 +333,14 @@ where phantom: Default::default(), } } + + pub(crate) fn key(&self) -> &RsaPrivateKey { + &self.inner + } + + pub(crate) fn prefix(&self) -> Vec { + self.prefix.clone() + } } impl From for SigningKey @@ -358,6 +365,7 @@ impl SigningKey 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 +462,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 where @@ -468,6 +479,7 @@ impl VerifyingKey where D: Digest, { + /// Create a new verifying key from an RSA public key. pub fn new(key: RsaPublicKey) -> Self { Self { inner: key, @@ -499,6 +511,7 @@ impl VerifyingKey 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, diff --git a/src/pss.rs b/src/pss.rs index e9a8953e..976fc713 100644 --- a/src/pss.rs +++ b/src/pss.rs @@ -19,6 +19,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, @@ -128,6 +131,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 +176,7 @@ fn generate_salt( } /// 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 +526,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 where @@ -535,10 +544,7 @@ impl SigningKey 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 +553,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 +561,10 @@ where phantom: Default::default(), } } + + pub(crate) fn key(&self) -> &RsaPrivateKey { + &self.inner + } } impl From for SigningKey @@ -644,6 +655,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 where @@ -658,10 +671,8 @@ impl BlindedSigningKey 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 +681,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 +690,10 @@ where phantom: Default::default(), } } + + pub(crate) fn key(&self) -> &RsaPrivateKey { + &self.inner + } } impl From for BlindedSigningKey @@ -767,6 +784,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 where @@ -780,6 +801,7 @@ impl VerifyingKey where D: Digest, { + /// Create a new RSASSA-PSS verifying key. pub fn new(key: RsaPublicKey) -> Self { Self { inner: key, @@ -1195,7 +1217,8 @@ mod test { let verifying_key = VerifyingKey::::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 +1239,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 +1259,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");