1 // Copyright 2017 Brian Smith.
2 //
3 // Permission to use, copy, modify, and/or distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10 // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 
15 //! PKCS#8 is specified in [RFC 5958].
16 //!
17 //! [RFC 5958]: https://tools.ietf.org/html/rfc5958
18 
19 use crate::{ec, error, io::der};
20 
21 pub(crate) struct PublicKeyOptions {
22     /// Should the wrong public key ASN.1 tagging used by early implementations
23     /// of PKCS#8 v2 (including earlier versions of *ring*) be accepted?
24     pub accept_legacy_ed25519_public_key_tag: bool,
25 }
26 
27 pub(crate) enum Version {
28     V1Only,
29     V1OrV2(PublicKeyOptions),
30     V2Only(PublicKeyOptions),
31 }
32 
33 /// A template for constructing PKCS#8 documents.
34 ///
35 /// Note that this only works for ECC.
36 pub(crate) struct Template {
37     pub bytes: &'static [u8],
38 
39     // The range within `bytes` that holds the value (not including the tag and
40     // length) for use in the PKCS#8 document's privateKeyAlgorithm field.
41     pub alg_id_range: core::ops::Range<usize>,
42 
43     // `bytes[alg_id_range][curve_id_index..]` contains the OID identifying the,
44     // curve, including the tag and length.
45     pub curve_id_index: usize,
46 
47     // `bytes` will be split into two parts at `private_key_index`, where the
48     // first part is written before the private key and the second part is
49     // written after the private key. The public key is written after the second
50     // part.
51     pub private_key_index: usize,
52 }
53 
54 impl Template {
55     #[inline]
alg_id_value(&self) -> untrusted::Input56     fn alg_id_value(&self) -> untrusted::Input {
57         untrusted::Input::from(self.alg_id_value_())
58     }
59 
alg_id_value_(&self) -> &[u8]60     fn alg_id_value_(&self) -> &[u8] {
61         &self.bytes[self.alg_id_range.start..self.alg_id_range.end]
62     }
63 
64     #[inline]
curve_oid(&self) -> untrusted::Input65     pub fn curve_oid(&self) -> untrusted::Input {
66         untrusted::Input::from(&self.alg_id_value_()[self.curve_id_index..])
67     }
68 }
69 
70 /// Parses an unencrypted PKCS#8 private key, verifies that it is the right type
71 /// of key, and returns the key value.
72 ///
73 /// PKCS#8 is specified in [RFC 5958].
74 ///
75 /// [RFC 5958]: https://tools.ietf.org/html/rfc5958
unwrap_key<'a>( template: &Template, version: Version, input: untrusted::Input<'a>, ) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected>76 pub(crate) fn unwrap_key<'a>(
77     template: &Template,
78     version: Version,
79     input: untrusted::Input<'a>,
80 ) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected> {
81     unwrap_key_(template.alg_id_value(), version, input)
82 }
83 
84 /// Parses an unencrypted PKCS#8 private key, verifies that it is the right type
85 /// of key, and returns the key value.
86 ///
87 /// `alg_id` must be the encoded value (not including the outermost `SEQUENCE`
88 /// tag and length) of the `AlgorithmIdentifier` that identifies the key type.
89 /// The result will be an encoded `RSAPrivateKey` or `ECPrivateKey` or similar.
90 ///
91 /// PKCS#8 is specified in [RFC 5958].
92 ///
93 /// [RFC 5958]: https://tools.ietf.org/html/rfc5958
unwrap_key_<'a>( alg_id: untrusted::Input, version: Version, input: untrusted::Input<'a>, ) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected>94 pub(crate) fn unwrap_key_<'a>(
95     alg_id: untrusted::Input,
96     version: Version,
97     input: untrusted::Input<'a>,
98 ) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected> {
99     input.read_all(error::KeyRejected::invalid_encoding(), |input| {
100         der::nested(
101             input,
102             der::Tag::Sequence,
103             error::KeyRejected::invalid_encoding(),
104             |input| unwrap_key__(alg_id, version, input),
105         )
106     })
107 }
108 
unwrap_key__<'a>( alg_id: untrusted::Input, version: Version, input: &mut untrusted::Reader<'a>, ) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected>109 fn unwrap_key__<'a>(
110     alg_id: untrusted::Input,
111     version: Version,
112     input: &mut untrusted::Reader<'a>,
113 ) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected> {
114     let actual_version = der::small_nonnegative_integer(input)
115         .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
116 
117     // Do things in a specific order to return more useful errors:
118     // 1. Check for completely unsupported version.
119     // 2. Check for algorithm mismatch.
120     // 3. Check for algorithm-specific version mismatch.
121 
122     if actual_version > 1 {
123         return Err(error::KeyRejected::version_not_supported());
124     };
125 
126     let actual_alg_id = der::expect_tag_and_get_value(input, der::Tag::Sequence)
127         .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
128     if actual_alg_id.as_slice_less_safe() != alg_id.as_slice_less_safe() {
129         return Err(error::KeyRejected::wrong_algorithm());
130     }
131 
132     let public_key_options = match (actual_version, version) {
133         (0, Version::V1Only) => None,
134         (0, Version::V1OrV2(_)) => None,
135         (1, Version::V1OrV2(options)) | (1, Version::V2Only(options)) => Some(options),
136         _ => {
137             return Err(error::KeyRejected::version_not_supported());
138         }
139     };
140 
141     let private_key = der::expect_tag_and_get_value(input, der::Tag::OctetString)
142         .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
143 
144     // Ignore any attributes that are present.
145     if input.peek(der::Tag::ContextSpecificConstructed0 as u8) {
146         let _ = der::expect_tag_and_get_value(input, der::Tag::ContextSpecificConstructed0)
147             .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
148     }
149 
150     let public_key = if let Some(options) = public_key_options {
151         if input.at_end() {
152             return Err(error::KeyRejected::public_key_is_missing());
153         }
154 
155         const INCORRECT_LEGACY: der::Tag = der::Tag::ContextSpecificConstructed1;
156         let result =
157             if options.accept_legacy_ed25519_public_key_tag && input.peek(INCORRECT_LEGACY as u8) {
158                 der::nested(
159                     input,
160                     INCORRECT_LEGACY,
161                     error::Unspecified,
162                     der::bit_string_with_no_unused_bits,
163                 )
164             } else {
165                 der::bit_string_tagged_with_no_unused_bits(der::Tag::ContextSpecific1, input)
166             };
167         let public_key =
168             result.map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
169         Some(public_key)
170     } else {
171         None
172     };
173 
174     Ok((private_key, public_key))
175 }
176 
177 /// A generated PKCS#8 document.
178 pub struct Document {
179     bytes: [u8; ec::PKCS8_DOCUMENT_MAX_LEN],
180     len: usize,
181 }
182 
183 impl AsRef<[u8]> for Document {
184     #[inline]
as_ref(&self) -> &[u8]185     fn as_ref(&self) -> &[u8] {
186         &self.bytes[..self.len]
187     }
188 }
189 
wrap_key(template: &Template, private_key: &[u8], public_key: &[u8]) -> Document190 pub(crate) fn wrap_key(template: &Template, private_key: &[u8], public_key: &[u8]) -> Document {
191     let mut result = Document {
192         bytes: [0; ec::PKCS8_DOCUMENT_MAX_LEN],
193         len: template.bytes.len() + private_key.len() + public_key.len(),
194     };
195     wrap_key_(
196         template,
197         private_key,
198         public_key,
199         &mut result.bytes[..result.len],
200     );
201     result
202 }
203 
204 /// Formats a private key "prefix||private_key||middle||public_key" where
205 /// `template` is "prefix||middle" split at position `private_key_index`.
wrap_key_(template: &Template, private_key: &[u8], public_key: &[u8], bytes: &mut [u8])206 fn wrap_key_(template: &Template, private_key: &[u8], public_key: &[u8], bytes: &mut [u8]) {
207     let (before_private_key, after_private_key) =
208         template.bytes.split_at(template.private_key_index);
209     let private_key_end_index = template.private_key_index + private_key.len();
210     bytes[..template.private_key_index].copy_from_slice(before_private_key);
211     bytes[template.private_key_index..private_key_end_index].copy_from_slice(private_key);
212     bytes[private_key_end_index..(private_key_end_index + after_private_key.len())]
213         .copy_from_slice(after_private_key);
214     bytes[(private_key_end_index + after_private_key.len())..].copy_from_slice(public_key);
215 }
216