• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Construction of `RsaKeyPair` from ASN1.
2 
3 use super::super::{keypair, public};
4 use crate::{
5     error::{self, KeyRejected},
6     io::der,
7     pkcs8,
8 };
9 use core::convert::TryFrom;
10 
11 impl keypair::RsaKeyPair {
12     /// Parses an unencrypted PKCS#8-encoded RSA private key.
13     ///
14     /// Only two-prime (not multi-prime) keys are supported. The public modulus
15     /// (n) must be at least 2047 bits. The public modulus must be no larger
16     /// than 4096 bits. It is recommended that the public modulus be exactly
17     /// 2048 or 3072 bits. The public exponent must be at least 65537.
18     ///
19     /// This will generate a 2048-bit RSA private key of the correct form using
20     /// OpenSSL's command line tool:
21     ///
22     /// ```sh
23     ///    openssl genpkey -algorithm RSA \
24     ///        -pkeyopt rsa_keygen_bits:2048 \
25     ///        -pkeyopt rsa_keygen_pubexp:65537 | \
26     ///      openssl pkcs8 -topk8 -nocrypt -outform der > rsa-2048-private-key.pk8
27     /// ```
28     ///
29     /// This will generate a 3072-bit RSA private key of the correct form:
30     ///
31     /// ```sh
32     ///    openssl genpkey -algorithm RSA \
33     ///        -pkeyopt rsa_keygen_bits:3072 \
34     ///        -pkeyopt rsa_keygen_pubexp:65537 | \
35     ///      openssl pkcs8 -topk8 -nocrypt -outform der > rsa-3072-private-key.pk8
36     /// ```
37     ///
38     /// Often, keys generated for use in OpenSSL-based software are stored in
39     /// the Base64 “PEM” format without the PKCS#8 wrapper. Such keys can be
40     /// converted to binary PKCS#8 form using the OpenSSL command line tool like
41     /// this:
42     ///
43     /// ```sh
44     /// openssl pkcs8 -topk8 -nocrypt -outform der \
45     ///     -in rsa-2048-private-key.pem > rsa-2048-private-key.pk8
46     /// ```
47     ///
48     /// Base64 (“PEM”) PKCS#8-encoded keys can be converted to the binary PKCS#8
49     /// form like this:
50     ///
51     /// ```sh
52     /// openssl pkcs8 -nocrypt -outform der \
53     ///     -in rsa-2048-private-key.pem > rsa-2048-private-key.pk8
54     /// ```
55     ///
56     /// The private key is validated according to [NIST SP-800-56B rev. 1]
57     /// section 6.4.1.4.3, crt_pkv (Intended Exponent-Creation Method Unknown),
58     /// with the following exceptions:
59     ///
60     /// * Section 6.4.1.2.1, Step 1: Neither a target security level nor an
61     ///   expected modulus length is provided as a parameter, so checks
62     ///   regarding these expectations are not done.
63     /// * Section 6.4.1.2.1, Step 3: Since neither the public key nor the
64     ///   expected modulus length is provided as a parameter, the consistency
65     ///   check between these values and the private key's value of n isn't
66     ///   done.
67     /// * Section 6.4.1.2.1, Step 5: No primality tests are done, both for
68     ///   performance reasons and to avoid any side channels that such tests
69     ///   would provide.
70     /// * Section 6.4.1.2.1, Step 6, and 6.4.1.4.3, Step 7:
71     ///     * *ring* has a slightly looser lower bound for the values of `p`
72     ///     and `q` than what the NIST document specifies. This looser lower
73     ///     bound matches what most other crypto libraries do. The check might
74     ///     be tightened to meet NIST's requirements in the future. Similarly,
75     ///     the check that `p` and `q` are not too close together is skipped
76     ///     currently, but may be added in the future.
77     ///     - The validity of the mathematical relationship of `dP`, `dQ`, `e`
78     ///     and `n` is verified only during signing. Some size checks of `d`,
79     ///     `dP` and `dQ` are performed at construction, but some NIST checks
80     ///     are skipped because they would be expensive and/or they would leak
81     ///     information through side channels. If a preemptive check of the
82     ///     consistency of `dP`, `dQ`, `e` and `n` with each other is
83     ///     necessary, that can be done by signing any message with the key
84     ///     pair.
85     ///
86     ///     * `d` is not fully validated, neither at construction nor during
87     ///     signing. This is OK as far as *ring*'s usage of the key is
88     ///     concerned because *ring* never uses the value of `d` (*ring* always
89     ///     uses `p`, `q`, `dP` and `dQ` via the Chinese Remainder Theorem,
90     ///     instead). However, *ring*'s checks would not be sufficient for
91     ///     validating a key pair for use by some other system; that other
92     ///     system must check the value of `d` itself if `d` is to be used.
93     ///
94     /// In addition to the NIST requirements, *ring* requires that `p > q` and
95     /// that `e` must be no more than 33 bits.
96     ///
97     /// See [RFC 5958] and [RFC 3447 Appendix A.1.2] for more details of the
98     /// encoding of the key.
99     ///
100     /// [NIST SP-800-56B rev. 1]:
101     ///     http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br1.pdf
102     ///
103     /// [RFC 3447 Appendix A.1.2]:
104     ///     https://tools.ietf.org/html/rfc3447#appendix-A.1.2
105     ///
106     /// [RFC 5958]:
107     ///     https://tools.ietf.org/html/rfc5958
from_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected>108     pub fn from_pkcs8(pkcs8: &[u8]) -> Result<Self, KeyRejected> {
109         const RSA_ENCRYPTION: &[u8] = include_bytes!("../../data/alg-rsa-encryption.der");
110         let (der, _) = pkcs8::unwrap_key_(
111             untrusted::Input::from(&RSA_ENCRYPTION),
112             pkcs8::Version::V1Only,
113             untrusted::Input::from(pkcs8),
114         )?;
115         Self::from_der(der.as_slice_less_safe())
116     }
117 
118     /// Parses an RSA private key that is not inside a PKCS#8 wrapper.
119     ///
120     /// The private key must be encoded as a binary DER-encoded ASN.1
121     /// `RSAPrivateKey` as described in [RFC 3447 Appendix A.1.2]). In all other
122     /// respects, this is just like `from_pkcs8()`. See the documentation for
123     /// `from_pkcs8()` for more details.
124     ///
125     /// It is recommended to use `from_pkcs8()` (with a PKCS#8-encoded key)
126     /// instead.
127     ///
128     /// [RFC 3447 Appendix A.1.2]:
129     ///     https://tools.ietf.org/html/rfc3447#appendix-A.1.2
130     ///
131     /// [NIST SP-800-56B rev. 1]:
132     ///     http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br1.pdf
from_der(input: &[u8]) -> Result<Self, KeyRejected>133     pub fn from_der(input: &[u8]) -> Result<Self, KeyRejected> {
134         untrusted::Input::from(input).read_all(KeyRejected::invalid_encoding(), |input| {
135             der::nested(
136                 input,
137                 der::Tag::Sequence,
138                 KeyRejected::invalid_encoding(),
139                 Self::from_der_reader,
140             )
141         })
142     }
143 
from_der_reader(input: &mut untrusted::Reader) -> Result<Self, KeyRejected>144     fn from_der_reader(input: &mut untrusted::Reader) -> Result<Self, KeyRejected> {
145         let version = der::small_nonnegative_integer(input)
146             .map_err(|error::Unspecified| KeyRejected::invalid_encoding())?;
147         if version != 0 {
148             return Err(KeyRejected::version_not_supported());
149         }
150 
151         fn nonnegative_integer<'a>(
152             input: &mut untrusted::Reader<'a>,
153         ) -> Result<&'a [u8], KeyRejected> {
154             der::nonnegative_integer(input, 0)
155                 .map(|input| input.as_slice_less_safe())
156                 .map_err(|error::Unspecified| KeyRejected::invalid_encoding())
157         }
158 
159         let n = nonnegative_integer(input)?;
160         let e = nonnegative_integer(input)?;
161         let d = nonnegative_integer(input)?;
162         let p = nonnegative_integer(input)?;
163         let q = nonnegative_integer(input)?;
164         let dP = nonnegative_integer(input)?;
165         let dQ = nonnegative_integer(input)?;
166         let qInv = nonnegative_integer(input)?;
167 
168         let components = keypair::Components {
169             public_key: public::Components { n, e },
170             d,
171             p,
172             q,
173             dP,
174             dQ,
175             qInv,
176         };
177 
178         Self::try_from(&components)
179     }
180 }
181