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) enum Version {
22    V1Only,
23    V1OrV2,
24    V2Only,
25}
26
27/// A template for constructing PKCS#8 documents.
28///
29/// Note that this only works for ECC.
30pub(crate) struct Template {
31    pub bytes: &'static [u8],
32
33    // The range within `bytes` that holds the value (not including the tag and
34    // length) for use in the PKCS#8 document's privateKeyAlgorithm field.
35    pub alg_id_range: core::ops::Range<usize>,
36
37    // `bytes[alg_id_range][curve_id_index..]` contains the OID identifying the,
38    // curve, including the tag and length.
39    pub curve_id_index: usize,
40
41    // `bytes` will be split into two parts at `private_key_index`, where the
42    // first part is written before the private key and the second part is
43    // written after the private key. The public key is written after the second
44    // part.
45    pub private_key_index: usize,
46}
47
48impl Template {
49    #[inline]
50    fn alg_id_value(&self) -> untrusted::Input {
51        untrusted::Input::from(self.alg_id_value_())
52    }
53
54    fn alg_id_value_(&self) -> &[u8] {
55        &self.bytes[self.alg_id_range.start..self.alg_id_range.end]
56    }
57
58    #[inline]
59    pub fn curve_oid(&self) -> untrusted::Input {
60        untrusted::Input::from(&self.alg_id_value_()[self.curve_id_index..])
61    }
62}
63
64/// Parses an unencrypted PKCS#8 private key, verifies that it is the right type
65/// of key, and returns the key value.
66///
67/// PKCS#8 is specified in [RFC 5958].
68///
69/// [RFC 5958]: https://tools.ietf.org/html/rfc5958.
70pub(crate) fn unwrap_key<'a>(
71    template: &Template,
72    version: Version,
73    input: untrusted::Input<'a>,
74) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected> {
75    unwrap_key_(template.alg_id_value(), version, input)
76}
77
78/// Parses an unencrypted PKCS#8 private key, verifies that it is the right type
79/// of key, and returns the key value.
80///
81/// `alg_id` must be the encoded value (not including the outermost `SEQUENCE`
82/// tag and length) of the `AlgorithmIdentifier` that identifies the key type.
83/// The result will be an encoded `RSAPrivateKey` or `ECPrivateKey` or similar.
84///
85/// PKCS#8 is specified in [RFC 5958].
86///
87/// [RFC 5958]: https://tools.ietf.org/html/rfc5958.
88pub(crate) fn unwrap_key_<'a>(
89    alg_id: untrusted::Input,
90    version: Version,
91    input: untrusted::Input<'a>,
92) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected> {
93    input.read_all(error::KeyRejected::invalid_encoding(), |input| {
94        der::nested(
95            input,
96            der::Tag::Sequence,
97            error::KeyRejected::invalid_encoding(),
98            |input| unwrap_key__(alg_id, version, input),
99        )
100    })
101}
102
103fn unwrap_key__<'a>(
104    alg_id: untrusted::Input,
105    version: Version,
106    input: &mut untrusted::Reader<'a>,
107) -> Result<(untrusted::Input<'a>, Option<untrusted::Input<'a>>), error::KeyRejected> {
108    let actual_version = der::small_nonnegative_integer(input)
109        .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
110
111    // Do things in a specific order to return more useful errors:
112    // 1. Check for completely unsupported version.
113    // 2. Check for algorithm mismatch.
114    // 3. Check for algorithm-specific version mismatch.
115
116    if actual_version > 1 {
117        return Err(error::KeyRejected::version_not_supported());
118    };
119
120    let actual_alg_id = der::expect_tag_and_get_value(input, der::Tag::Sequence)
121        .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
122    if actual_alg_id != alg_id {
123        return Err(error::KeyRejected::wrong_algorithm());
124    }
125
126    let require_public_key = match (actual_version, version) {
127        (0, Version::V1Only) => false,
128        (0, Version::V1OrV2) => false,
129        (1, Version::V1OrV2) | (1, Version::V2Only) => true,
130        _ => {
131            return Err(error::KeyRejected::version_not_supported());
132        }
133    };
134
135    let private_key = der::expect_tag_and_get_value(input, der::Tag::OctetString)
136        .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
137
138    // Ignore any attributes that are present.
139    if input.peek(der::Tag::ContextSpecificConstructed0 as u8) {
140        let _ = der::expect_tag_and_get_value(input, der::Tag::ContextSpecificConstructed0)
141            .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
142    }
143
144    let public_key = if require_public_key {
145        if input.at_end() {
146            return Err(error::KeyRejected::public_key_is_missing());
147        }
148        let public_key = der::nested(
149            input,
150            der::Tag::ContextSpecificConstructed1,
151            error::Unspecified,
152            der::bit_string_with_no_unused_bits,
153        )
154        .map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
155        Some(public_key)
156    } else {
157        None
158    };
159
160    Ok((private_key, public_key))
161}
162
163/// A generated PKCS#8 document.
164pub struct Document {
165    bytes: [u8; ec::PKCS8_DOCUMENT_MAX_LEN],
166    len: usize,
167}
168
169impl AsRef<[u8]> for Document {
170    #[inline]
171    fn as_ref(&self) -> &[u8] {
172        &self.bytes[..self.len]
173    }
174}
175
176pub(crate) fn wrap_key(template: &Template, private_key: &[u8], public_key: &[u8]) -> Document {
177    let mut result = Document {
178        bytes: [0; ec::PKCS8_DOCUMENT_MAX_LEN],
179        len: template.bytes.len() + private_key.len() + public_key.len(),
180    };
181    wrap_key_(
182        template,
183        private_key,
184        public_key,
185        &mut result.bytes[..result.len],
186    );
187    result
188}
189
190/// Formats a private key "prefix||private_key||middle||public_key" where
191/// `template` is "prefix||middle" split at position `private_key_index`.
192fn wrap_key_(template: &Template, private_key: &[u8], public_key: &[u8], bytes: &mut [u8]) {
193    let (before_private_key, after_private_key) =
194        template.bytes.split_at(template.private_key_index);
195    let private_key_end_index = template.private_key_index + private_key.len();
196    bytes[..template.private_key_index].copy_from_slice(before_private_key);
197    bytes[template.private_key_index..private_key_end_index].copy_from_slice(&private_key);
198    bytes[private_key_end_index..(private_key_end_index + after_private_key.len())]
199        .copy_from_slice(after_private_key);
200    bytes[(private_key_end_index + after_private_key.len())..].copy_from_slice(public_key);
201}