ring/
pkcs8.rs

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
19use crate::{ec, error, io::der};
20
21pub(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
27pub(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.
36pub(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
54impl 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
76pub(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
94pub(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
109fn 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.
178pub struct Document {
179    bytes: [u8; ec::PKCS8_DOCUMENT_MAX_LEN],
180    len: usize,
181}
182
183impl AsRef<[u8]> for Document {
184    #[inline]
185    fn as_ref(&self) -> &[u8] {
186        &self.bytes[..self.len]
187    }
188}
189
190pub(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`.
206fn 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}