blob: d41de774b55a82b6fe6096aeed38ba34183ca1ec [file] [log] [blame]
//! Construction of `RsaKeyPair` from ASN1.
use super::super::{keypair, public};
use crate::{
error::{self, KeyRejected},
io::der,
pkcs8,
};
use core::convert::TryFrom;
impl keypair::RsaKeyPair {
/// Parses an unencrypted PKCS#8-encoded RSA private key.
///
/// Only two-prime (not multi-prime) keys are supported. The public modulus
/// (n) must be at least 2047 bits. The public modulus must be no larger
/// than 4096 bits. It is recommended that the public modulus be exactly
/// 2048 or 3072 bits. The public exponent must be at least 65537.
///
/// This will generate a 2048-bit RSA private key of the correct form using
/// OpenSSL's command line tool:
///
/// ```sh
/// openssl genpkey -algorithm RSA \
/// -pkeyopt rsa_keygen_bits:2048 \
/// -pkeyopt rsa_keygen_pubexp:65537 | \
/// openssl pkcs8 -topk8 -nocrypt -outform der > rsa-2048-private-key.pk8
/// ```
///
/// This will generate a 3072-bit RSA private key of the correct form:
///
/// ```sh
/// openssl genpkey -algorithm RSA \
/// -pkeyopt rsa_keygen_bits:3072 \
/// -pkeyopt rsa_keygen_pubexp:65537 | \
/// openssl pkcs8 -topk8 -nocrypt -outform der > rsa-3072-private-key.pk8
/// ```
///
/// Often, keys generated for use in OpenSSL-based software are stored in
/// the Base64 “PEM” format without the PKCS#8 wrapper. Such keys can be
/// converted to binary PKCS#8 form using the OpenSSL command line tool like
/// this:
///
/// ```sh
/// openssl pkcs8 -topk8 -nocrypt -outform der \
/// -in rsa-2048-private-key.pem > rsa-2048-private-key.pk8
/// ```
///
/// Base64 (“PEM”) PKCS#8-encoded keys can be converted to the binary PKCS#8
/// form like this:
///
/// ```sh
/// openssl pkcs8 -nocrypt -outform der \
/// -in rsa-2048-private-key.pem > rsa-2048-private-key.pk8
/// ```
///
/// The private key is validated according to [NIST SP-800-56B rev. 1]
/// section 6.4.1.4.3, crt_pkv (Intended Exponent-Creation Method Unknown),
/// with the following exceptions:
///
/// * Section 6.4.1.2.1, Step 1: Neither a target security level nor an
/// expected modulus length is provided as a parameter, so checks
/// regarding these expectations are not done.
/// * Section 6.4.1.2.1, Step 3: Since neither the public key nor the
/// expected modulus length is provided as a parameter, the consistency
/// check between these values and the private key's value of n isn't
/// done.
/// * Section 6.4.1.2.1, Step 5: No primality tests are done, both for
/// performance reasons and to avoid any side channels that such tests
/// would provide.
/// * Section 6.4.1.2.1, Step 6, and 6.4.1.4.3, Step 7:
/// * *ring* has a slightly looser lower bound for the values of `p`
/// and `q` than what the NIST document specifies. This looser lower
/// bound matches what most other crypto libraries do. The check might
/// be tightened to meet NIST's requirements in the future. Similarly,
/// the check that `p` and `q` are not too close together is skipped
/// currently, but may be added in the future.
/// - The validity of the mathematical relationship of `dP`, `dQ`, `e`
/// and `n` is verified only during signing. Some size checks of `d`,
/// `dP` and `dQ` are performed at construction, but some NIST checks
/// are skipped because they would be expensive and/or they would leak
/// information through side channels. If a preemptive check of the
/// consistency of `dP`, `dQ`, `e` and `n` with each other is
/// necessary, that can be done by signing any message with the key
/// pair.
///
/// * `d` is not fully validated, neither at construction nor during
/// signing. This is OK as far as *ring*'s usage of the key is
/// concerned because *ring* never uses the value of `d` (*ring* always
/// uses `p`, `q`, `dP` and `dQ` via the Chinese Remainder Theorem,
/// instead). However, *ring*'s checks would not be sufficient for
/// validating a key pair for use by some other system; that other
/// system must check the value of `d` itself if `d` is to be used.
///
/// In addition to the NIST requirements, *ring* requires that `p > q` and
/// that `e` must be no more than 33 bits.
///
/// See [RFC 5958] and [RFC 3447 Appendix A.1.2] for more details of the
/// encoding of the key.
///
/// [NIST SP-800-56B rev. 1]:
/// http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br1.pdf
///
/// [RFC 3447 Appendix A.1.2]:
/// https://tools.ietf.org/html/rfc3447#appendix-A.1.2
///
/// [RFC 5958]:
/// https://tools.ietf.org/html/rfc5958
pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
const RSA_ENCRYPTION: &[u8] = include_bytes!("../../data/alg-rsa-encryption.der");
let (der, _) = pkcs8::unwrap_key_(
untrusted::Input::from(&RSA_ENCRYPTION),
pkcs8::Version::V1Only,
untrusted::Input::from(pkcs8),
)?;
Self::from_der(der.as_slice_less_safe())
}
/// Parses an RSA private key that is not inside a PKCS#8 wrapper.
///
/// The private key must be encoded as a binary DER-encoded ASN.1
/// `RSAPrivateKey` as described in [RFC 3447 Appendix A.1.2]). In all other
/// respects, this is just like `from_pkcs8()`. See the documentation for
/// `from_pkcs8()` for more details.
///
/// It is recommended to use `from_pkcs8()` (with a PKCS#8-encoded key)
/// instead.
///
/// [RFC 3447 Appendix A.1.2]:
/// https://tools.ietf.org/html/rfc3447#appendix-A.1.2
///
/// [NIST SP-800-56B rev. 1]:
/// http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br1.pdf
pub fn from_der(input: &[u8]) -> Result<Self, KeyRejected> {
untrusted::Input::from(input).read_all(KeyRejected::invalid_encoding(), |input| {
der::nested(
input,
der::Tag::Sequence,
KeyRejected::invalid_encoding(),
Self::from_der_reader,
)
})
}
fn from_der_reader(input: &mut untrusted::Reader) -> Result<Self, KeyRejected> {
let version = der::small_nonnegative_integer(input)
.map_err(|error::Unspecified| KeyRejected::invalid_encoding())?;
if version != 0 {
return Err(KeyRejected::version_not_supported());
}
fn nonnegative_integer<'a>(
input: &mut untrusted::Reader<'a>,
) -> Result<&'a [u8], KeyRejected> {
der::nonnegative_integer(input, 0)
.map(|input| input.as_slice_less_safe())
.map_err(|error::Unspecified| KeyRejected::invalid_encoding())
}
let n = nonnegative_integer(input)?;
let e = nonnegative_integer(input)?;
let d = nonnegative_integer(input)?;
let p = nonnegative_integer(input)?;
let q = nonnegative_integer(input)?;
let dP = nonnegative_integer(input)?;
let dQ = nonnegative_integer(input)?;
let qInv = nonnegative_integer(input)?;
let components = keypair::Components {
public_key: public::Components { n, e },
d,
p,
q,
dP,
dQ,
qInv,
};
Self::try_from(&components)
}
}