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 ] |
56 | fn alg_id_value(&self) -> untrusted::Input { |
57 | untrusted::Input::from(self.alg_id_value_()) |
58 | } |
59 | |
60 | fn alg_id_value_(&self) -> &[u8] { |
61 | &self.bytes[self.alg_id_range.start..self.alg_id_range.end] |
62 | } |
63 | |
64 | #[inline ] |
65 | 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 |
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 |
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(incomplete_read:error::KeyRejected::invalid_encoding(), |input: &mut Reader<'_>| { |
100 | der::nested( |
101 | input, |
102 | der::Tag::Sequence, |
103 | error:error::KeyRejected::invalid_encoding(), |
104 | |input: &mut Reader<'_>| unwrap_key__(alg_id, version, input), |
105 | ) |
106 | }) |
107 | } |
108 | |
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 ] |
185 | fn as_ref(&self) -> &[u8] { |
186 | &self.bytes[..self.len] |
187 | } |
188 | } |
189 | |
190 | pub(crate) fn wrap_key(template: &Template, private_key: &[u8], public_key: &[u8]) -> Document { |
191 | let mut result: Document = 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`. |
206 | fn wrap_key_(template: &Template, private_key: &[u8], public_key: &[u8], bytes: &mut [u8]) { |
207 | let (before_private_key: &[u8], after_private_key: &[u8]) = |
208 | template.bytes.split_at(mid:template.private_key_index); |
209 | let private_key_end_index: usize = template.private_key_index + private_key.len(); |
210 | bytes[..template.private_key_index].copy_from_slice(src:before_private_key); |
211 | bytes[template.private_key_index..private_key_end_index].copy_from_slice(src:private_key); |
212 | bytes[private_key_end_index..(private_key_end_index + after_private_key.len())] |
213 | .copy_from_slice(src:after_private_key); |
214 | bytes[(private_key_end_index + after_private_key.len())..].copy_from_slice(src:public_key); |
215 | } |
216 | |