/rust/registry/src/index.crates.io-6f17d22bba15001f/ring-0.17.8/src/pkcs8.rs
Line | Count | Source (jump to first uncovered line) |
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 | 0 | fn alg_id_value(&self) -> untrusted::Input { |
57 | 0 | untrusted::Input::from(self.alg_id_value_()) |
58 | 0 | } |
59 | | |
60 | 0 | fn alg_id_value_(&self) -> &[u8] { |
61 | 0 | &self.bytes[self.alg_id_range.start..self.alg_id_range.end] |
62 | 0 | } |
63 | | |
64 | | #[inline] |
65 | 0 | pub fn curve_oid(&self) -> untrusted::Input { |
66 | 0 | untrusted::Input::from(&self.alg_id_value_()[self.curve_id_index..]) |
67 | 0 | } |
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 | 0 | pub(crate) fn unwrap_key<'a>( |
77 | 0 | template: &Template, |
78 | 0 | version: Version, |
79 | 0 | input: untrusted::Input<'a>, |
80 | 0 | ) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected> { |
81 | 0 | unwrap_key_(template.alg_id_value(), version, input) |
82 | 0 | } |
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 | 0 | pub(crate) fn unwrap_key_<'a>( |
95 | 0 | alg_id: untrusted::Input, |
96 | 0 | version: Version, |
97 | 0 | input: untrusted::Input<'a>, |
98 | 0 | ) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected> { |
99 | 0 | input.read_all(error::KeyRejected::invalid_encoding(), |input| { |
100 | 0 | der::nested( |
101 | 0 | input, |
102 | 0 | der::Tag::Sequence, |
103 | 0 | error::KeyRejected::invalid_encoding(), |
104 | 0 | |input| unwrap_key__(alg_id, version, input), |
105 | 0 | ) |
106 | 0 | }) |
107 | 0 | } |
108 | | |
109 | 0 | fn unwrap_key__<'a>( |
110 | 0 | alg_id: untrusted::Input, |
111 | 0 | version: Version, |
112 | 0 | input: &mut untrusted::Reader<'a>, |
113 | 0 | ) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected> { |
114 | 0 | let actual_version = der::small_nonnegative_integer(input) |
115 | 0 | .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 | 0 | if actual_version > 1 { |
123 | 0 | return Err(error::KeyRejected::version_not_supported()); |
124 | 0 | }; |
125 | | |
126 | 0 | let actual_alg_id = der::expect_tag_and_get_value(input, der::Tag::Sequence) |
127 | 0 | .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; |
128 | 0 | if actual_alg_id.as_slice_less_safe() != alg_id.as_slice_less_safe() { |
129 | 0 | return Err(error::KeyRejected::wrong_algorithm()); |
130 | 0 | } |
131 | | |
132 | 0 | let public_key_options = match (actual_version, version) { |
133 | 0 | (0, Version::V1Only) => None, |
134 | 0 | (0, Version::V1OrV2(_)) => None, |
135 | 0 | (1, Version::V1OrV2(options)) | (1, Version::V2Only(options)) => Some(options), |
136 | | _ => { |
137 | 0 | return Err(error::KeyRejected::version_not_supported()); |
138 | | } |
139 | | }; |
140 | | |
141 | 0 | let private_key = der::expect_tag_and_get_value(input, der::Tag::OctetString) |
142 | 0 | .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; |
143 | | |
144 | | // Ignore any attributes that are present. |
145 | 0 | if input.peek(der::Tag::ContextSpecificConstructed0 as u8) { |
146 | 0 | let _ = der::expect_tag_and_get_value(input, der::Tag::ContextSpecificConstructed0) |
147 | 0 | .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; |
148 | 0 | } |
149 | | |
150 | 0 | let public_key = if let Some(options) = public_key_options { |
151 | 0 | if input.at_end() { |
152 | 0 | return Err(error::KeyRejected::public_key_is_missing()); |
153 | 0 | } |
154 | | |
155 | | const INCORRECT_LEGACY: der::Tag = der::Tag::ContextSpecificConstructed1; |
156 | 0 | let result = |
157 | 0 | if options.accept_legacy_ed25519_public_key_tag && input.peek(INCORRECT_LEGACY as u8) { |
158 | 0 | der::nested( |
159 | 0 | input, |
160 | 0 | INCORRECT_LEGACY, |
161 | 0 | error::Unspecified, |
162 | 0 | der::bit_string_with_no_unused_bits, |
163 | 0 | ) |
164 | | } else { |
165 | 0 | der::bit_string_tagged_with_no_unused_bits(der::Tag::ContextSpecific1, input) |
166 | | }; |
167 | 0 | let public_key = |
168 | 0 | result.map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; |
169 | 0 | Some(public_key) |
170 | | } else { |
171 | 0 | None |
172 | | }; |
173 | | |
174 | 0 | Ok((private_key, public_key)) |
175 | 0 | } |
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 | 0 | fn as_ref(&self) -> &[u8] { |
186 | 0 | &self.bytes[..self.len] |
187 | 0 | } |
188 | | } |
189 | | |
190 | 0 | pub(crate) fn wrap_key(template: &Template, private_key: &[u8], public_key: &[u8]) -> Document { |
191 | 0 | let mut result = Document { |
192 | 0 | bytes: [0; ec::PKCS8_DOCUMENT_MAX_LEN], |
193 | 0 | len: template.bytes.len() + private_key.len() + public_key.len(), |
194 | 0 | }; |
195 | 0 | wrap_key_( |
196 | 0 | template, |
197 | 0 | private_key, |
198 | 0 | public_key, |
199 | 0 | &mut result.bytes[..result.len], |
200 | 0 | ); |
201 | 0 | result |
202 | 0 | } |
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 | 0 | fn wrap_key_(template: &Template, private_key: &[u8], public_key: &[u8], bytes: &mut [u8]) { |
207 | 0 | let (before_private_key, after_private_key) = |
208 | 0 | template.bytes.split_at(template.private_key_index); |
209 | 0 | let private_key_end_index = template.private_key_index + private_key.len(); |
210 | 0 | bytes[..template.private_key_index].copy_from_slice(before_private_key); |
211 | 0 | bytes[template.private_key_index..private_key_end_index].copy_from_slice(private_key); |
212 | 0 | bytes[private_key_end_index..(private_key_end_index + after_private_key.len())] |
213 | 0 | .copy_from_slice(after_private_key); |
214 | 0 | bytes[(private_key_end_index + after_private_key.len())..].copy_from_slice(public_key); |
215 | 0 | } |