Coverage Report

Created: 2025-07-11 06:15

/rust/registry/src/index.crates.io-6f17d22bba15001f/ring-0.17.14/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 AUTHOR DISCLAIMS ALL WARRANTIES
8
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.into()) {
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 = if options.accept_legacy_ed25519_public_key_tag
157
0
            && input.peek(INCORRECT_LEGACY.into())
158
        {
159
0
            der::nested(
160
0
                input,
161
0
                INCORRECT_LEGACY,
162
0
                error::Unspecified,
163
0
                der::bit_string_with_no_unused_bits,
164
0
            )
165
        } else {
166
0
            der::bit_string_tagged_with_no_unused_bits(der::Tag::ContextSpecific1, input)
167
        };
168
0
        let public_key =
169
0
            result.map_err(|error::Unspecified| error::KeyRejected::invalid_encoding())?;
170
0
        Some(public_key)
171
    } else {
172
0
        None
173
    };
174
175
0
    Ok((private_key, public_key))
176
0
}
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
0
    fn as_ref(&self) -> &[u8] {
187
0
        &self.bytes[..self.len]
188
0
    }
189
}
190
191
0
pub(crate) fn wrap_key(template: &Template, private_key: &[u8], public_key: &[u8]) -> Document {
192
0
    let mut result = Document {
193
0
        bytes: [0; ec::PKCS8_DOCUMENT_MAX_LEN],
194
0
        len: template.bytes.len() + private_key.len() + public_key.len(),
195
0
    };
196
0
    wrap_key_(
197
0
        template,
198
0
        private_key,
199
0
        public_key,
200
0
        &mut result.bytes[..result.len],
201
0
    );
202
0
    result
203
0
}
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
0
fn wrap_key_(template: &Template, private_key: &[u8], public_key: &[u8], bytes: &mut [u8]) {
208
0
    let (before_private_key, after_private_key) =
209
0
        template.bytes.split_at(template.private_key_index);
210
0
    let private_key_end_index = template.private_key_index + private_key.len();
211
0
    bytes[..template.private_key_index].copy_from_slice(before_private_key);
212
0
    bytes[template.private_key_index..private_key_end_index].copy_from_slice(private_key);
213
0
    bytes[private_key_end_index..(private_key_end_index + after_private_key.len())]
214
0
        .copy_from_slice(after_private_key);
215
0
    bytes[(private_key_end_index + after_private_key.len())..].copy_from_slice(public_key);
216
0
}