| 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_(alg_id: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<'a>| { |
| 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.into()) { |
| 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 = if options.accept_legacy_ed25519_public_key_tag |
| 157 | && input.peek(INCORRECT_LEGACY.into()) |
| 158 | { |
| 159 | der::nested( |
| 160 | input, |
| 161 | INCORRECT_LEGACY, |
| 162 | error::Unspecified, |
| 163 | der::bit_string_with_no_unused_bits, |
| 164 | ) |
| 165 | } else { |
| 166 | der::bit_string_tagged_with_no_unused_bits(der::Tag::ContextSpecific1, input) |
| 167 | }; |
| 168 | let public_key = |
| 169 | result.map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?; |
| 170 | Some(public_key) |
| 171 | } else { |
| 172 | None |
| 173 | }; |
| 174 | |
| 175 | Ok((private_key, public_key)) |
| 176 | } |
| 177 | |
| 178 | /// A generated PKCS#8 document. |
| 179 | pub struct Document { |
| 180 | bytes: [u8; ec::PKCS8_DOCUMENT_MAX_LEN], |
| 181 | len: usize, |
| 182 | } |
| 183 | |
| 184 | impl AsRef<[u8]> for Document { |
| 185 | #[inline ] |
| 186 | fn as_ref(&self) -> &[u8] { |
| 187 | &self.bytes[..self.len] |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | pub(crate) fn wrap_key(template: &Template, private_key: &[u8], public_key: &[u8]) -> Document { |
| 192 | let mut result: Document = Document { |
| 193 | bytes: [0; ec::PKCS8_DOCUMENT_MAX_LEN], |
| 194 | len: template.bytes.len() + private_key.len() + public_key.len(), |
| 195 | }; |
| 196 | wrap_key_( |
| 197 | template, |
| 198 | private_key, |
| 199 | public_key, |
| 200 | &mut result.bytes[..result.len], |
| 201 | ); |
| 202 | result |
| 203 | } |
| 204 | |
| 205 | /// Formats a private key "prefix||private_key||middle||public_key" where |
| 206 | /// `template` is "prefix||middle" split at position `private_key_index`. |
| 207 | fn wrap_key_(template: &Template, private_key: &[u8], public_key: &[u8], bytes: &mut [u8]) { |
| 208 | let (before_private_key: &[u8], after_private_key: &[u8]) = |
| 209 | template.bytes.split_at(mid:template.private_key_index); |
| 210 | let private_key_end_index: usize = template.private_key_index + private_key.len(); |
| 211 | bytes[..template.private_key_index].copy_from_slice(src:before_private_key); |
| 212 | bytes[template.private_key_index..private_key_end_index].copy_from_slice(src:private_key); |
| 213 | bytes[private_key_end_index..(private_key_end_index + after_private_key.len())] |
| 214 | .copy_from_slice(src:after_private_key); |
| 215 | bytes[(private_key_end_index + after_private_key.len())..].copy_from_slice(src:public_key); |
| 216 | } |
| 217 | |