aws_lc_rs/
evp_pkey.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0 OR ISC
3
4use crate::aws_lc::{
5    EVP_DigestSign, EVP_DigestSignInit, EVP_DigestVerify, EVP_DigestVerifyInit, EVP_PKEY_CTX_new,
6    EVP_PKEY_CTX_new_id, EVP_PKEY_bits, EVP_PKEY_cmp, EVP_PKEY_get0_EC_KEY, EVP_PKEY_get0_RSA,
7    EVP_PKEY_get_raw_private_key, EVP_PKEY_get_raw_public_key, EVP_PKEY_id, EVP_PKEY_keygen,
8    EVP_PKEY_keygen_init, EVP_PKEY_new_raw_private_key, EVP_PKEY_new_raw_public_key, EVP_PKEY_size,
9    EVP_PKEY_up_ref, EVP_marshal_private_key, EVP_marshal_private_key_v2, EVP_marshal_public_key,
10    EVP_parse_private_key, EVP_parse_public_key, EC_KEY, EVP_PKEY, EVP_PKEY_CTX, EVP_PKEY_ED25519,
11    RSA,
12};
13#[cfg(all(feature = "unstable", not(feature = "fips")))]
14use crate::aws_lc::{
15    EVP_PKEY_pqdsa_new_raw_private_key, EVP_PKEY_pqdsa_new_raw_public_key, EVP_PKEY_PQDSA,
16    NID_MLDSA44, NID_MLDSA65, NID_MLDSA87,
17};
18use crate::cbb::LcCBB;
19use crate::error::{KeyRejected, Unspecified};
20use crate::pkcs8::Version;
21use crate::ptr::{ConstPointer, LcPtr};
22use crate::{cbs, digest};
23// TODO: Uncomment when MSRV >= 1.64
24// use core::ffi::c_int;
25use crate::digest::digest_ctx::DigestContext;
26use crate::fips::indicator_check;
27use std::os::raw::c_int;
28use std::ptr::{null, null_mut};
29
30impl PartialEq<Self> for LcPtr<EVP_PKEY> {
31    /// Only compares params and public key
32    fn eq(&self, other: &Self) -> bool {
33        // EVP_PKEY_cmp only compares params and public key
34        1 == unsafe { EVP_PKEY_cmp(*self.as_const(), *other.as_const()) }
35    }
36}
37
38#[allow(non_camel_case_types)]
39pub(crate) trait EVP_PKEY_CTX_consumer: Fn(*mut EVP_PKEY_CTX) -> Result<(), ()> {}
40
41impl<T> EVP_PKEY_CTX_consumer for T where T: Fn(*mut EVP_PKEY_CTX) -> Result<(), ()> {}
42
43#[allow(non_upper_case_globals, clippy::type_complexity)]
44pub(crate) const No_EVP_PKEY_CTX_consumer: Option<fn(*mut EVP_PKEY_CTX) -> Result<(), ()>> = None;
45
46impl ConstPointer<'_, EVP_PKEY> {
47    pub(crate) fn validate_as_ed25519(&self) -> Result<(), KeyRejected> {
48        const ED25519_KEY_TYPE: c_int = EVP_PKEY_ED25519;
49        const ED25519_MIN_BITS: c_int = 253;
50        const ED25519_MAX_BITS: c_int = 256;
51
52        let key_type = self.id();
53        if key_type != ED25519_KEY_TYPE {
54            return Err(KeyRejected::wrong_algorithm());
55        }
56
57        let bits: c_int = self.key_size_bits().try_into().unwrap();
58        if bits < ED25519_MIN_BITS {
59            return Err(KeyRejected::too_small());
60        }
61
62        if bits > ED25519_MAX_BITS {
63            return Err(KeyRejected::too_large());
64        }
65        Ok(())
66    }
67
68    // EVP_PKEY_NONE = 0;
69    // EVP_PKEY_RSA = 6;
70    // EVP_PKEY_RSA_PSS = 912;
71    // EVP_PKEY_DSA = 116;
72    // EVP_PKEY_EC = 408;
73    // EVP_PKEY_ED25519 = 949;
74    // EVP_PKEY_X25519 = 948;
75    // EVP_PKEY_KYBER512 = 970;
76    // EVP_PKEY_HKDF = 969;
77    // EVP_PKEY_DH = 28;
78    // EVP_PKEY_RSA2 = 19;
79    // EVP_PKEY_X448 = 961;
80    // EVP_PKEY_ED448 = 960;
81    pub(crate) fn id(&self) -> i32 {
82        unsafe { EVP_PKEY_id(**self) }
83    }
84
85    pub(crate) fn key_size_bytes(&self) -> usize {
86        self.key_size_bits() / 8
87    }
88
89    pub(crate) fn key_size_bits(&self) -> usize {
90        unsafe { EVP_PKEY_bits(**self) }.try_into().unwrap()
91    }
92
93    pub(crate) fn signature_size_bytes(&self) -> usize {
94        unsafe { EVP_PKEY_size(**self) }.try_into().unwrap()
95    }
96
97    #[allow(dead_code)]
98    pub(crate) fn get_ec_key(&self) -> Result<ConstPointer<'_, EC_KEY>, KeyRejected> {
99        self.project_const_lifetime(unsafe { |evp_pkey| EVP_PKEY_get0_EC_KEY(**evp_pkey) })
100            .map_err(|()| KeyRejected::wrong_algorithm())
101    }
102
103    pub(crate) fn get_rsa(&self) -> Result<ConstPointer<'_, RSA>, KeyRejected> {
104        self.project_const_lifetime(unsafe { |evp_pkey| EVP_PKEY_get0_RSA(**evp_pkey) })
105            .map_err(|()| KeyRejected::wrong_algorithm())
106    }
107
108    pub(crate) fn marshal_rfc5280_public_key(&self) -> Result<Vec<u8>, Unspecified> {
109        // Data shows that the SubjectPublicKeyInfo is roughly 356% to 375% increase in size compared to the RSA key
110        // size in bytes for keys ranging from 2048-bit to 4096-bit. So size the initial capacity to be roughly
111        // 500% as a conservative estimate to avoid needing to reallocate for any key in that range.
112        let mut cbb = LcCBB::new(self.key_size_bytes() * 5);
113        if 1 != unsafe { EVP_marshal_public_key(cbb.as_mut_ptr(), **self) } {
114            return Err(Unspecified);
115        }
116        cbb.into_vec()
117    }
118
119    pub(crate) fn marshal_rfc5208_private_key(
120        &self,
121        version: Version,
122    ) -> Result<Vec<u8>, Unspecified> {
123        let key_size_bytes =
124            TryInto::<usize>::try_into(unsafe { EVP_PKEY_bits(**self) }).expect("fit in usize") / 8;
125        let mut cbb = LcCBB::new(key_size_bytes * 5);
126        match version {
127            Version::V1 => {
128                if 1 != unsafe { EVP_marshal_private_key(cbb.as_mut_ptr(), **self) } {
129                    return Err(Unspecified);
130                }
131            }
132            Version::V2 => {
133                if 1 != unsafe { EVP_marshal_private_key_v2(cbb.as_mut_ptr(), **self) } {
134                    return Err(Unspecified);
135                }
136            }
137        }
138        cbb.into_vec()
139    }
140
141    pub(crate) fn marshal_raw_private_key(&self) -> Result<Vec<u8>, Unspecified> {
142        let mut size = 0;
143        if 1 != unsafe { EVP_PKEY_get_raw_private_key(**self, null_mut(), &mut size) } {
144            return Err(Unspecified);
145        }
146        let mut buffer = vec![0u8; size];
147        let buffer_size = self.marshal_raw_private_to_buffer(&mut buffer)?;
148        debug_assert_eq!(buffer_size, size);
149        Ok(buffer)
150    }
151
152    pub(crate) fn marshal_raw_private_to_buffer(
153        &self,
154        buffer: &mut [u8],
155    ) -> Result<usize, Unspecified> {
156        let mut key_len = buffer.len();
157        if 1 == unsafe { EVP_PKEY_get_raw_private_key(**self, buffer.as_mut_ptr(), &mut key_len) } {
158            Ok(key_len)
159        } else {
160            Err(Unspecified)
161        }
162    }
163
164    #[allow(dead_code)]
165    pub(crate) fn marshal_raw_public_key(&self) -> Result<Vec<u8>, Unspecified> {
166        let mut size = 0;
167        if 1 != unsafe { EVP_PKEY_get_raw_public_key(**self, null_mut(), &mut size) } {
168            return Err(Unspecified);
169        }
170        let mut buffer = vec![0u8; size];
171        let buffer_size = self.marshal_raw_public_to_buffer(&mut buffer)?;
172        debug_assert_eq!(buffer_size, size);
173        Ok(buffer)
174    }
175
176    pub(crate) fn marshal_raw_public_to_buffer(
177        &self,
178        buffer: &mut [u8],
179    ) -> Result<usize, Unspecified> {
180        let mut key_len = buffer.len();
181        if 1 == unsafe {
182            // `EVP_PKEY_get_raw_public_key` writes the total length
183            // to `encapsulate_key_size` in the event that the buffer we provide is larger then
184            // required.
185            EVP_PKEY_get_raw_public_key(**self, buffer.as_mut_ptr(), &mut key_len)
186        } {
187            Ok(key_len)
188        } else {
189            Err(Unspecified)
190        }
191    }
192}
193
194impl LcPtr<EVP_PKEY> {
195    pub(crate) fn parse_rfc5280_public_key(
196        bytes: &[u8],
197        evp_pkey_type: c_int,
198    ) -> Result<Self, KeyRejected> {
199        let mut cbs = cbs::build_CBS(bytes);
200        // Also checks the validity of the key
201        let evp_pkey = LcPtr::new(unsafe { EVP_parse_public_key(&mut cbs) })
202            .map_err(|()| KeyRejected::invalid_encoding())?;
203        evp_pkey
204            .as_const()
205            .id()
206            .eq(&evp_pkey_type)
207            .then_some(evp_pkey)
208            .ok_or(KeyRejected::wrong_algorithm())
209    }
210
211    pub(crate) fn parse_rfc5208_private_key(
212        bytes: &[u8],
213        evp_pkey_type: c_int,
214    ) -> Result<Self, KeyRejected> {
215        let mut cbs = cbs::build_CBS(bytes);
216        // Also checks the validity of the key
217        let evp_pkey = LcPtr::new(unsafe { EVP_parse_private_key(&mut cbs) })
218            .map_err(|()| KeyRejected::invalid_encoding())?;
219        evp_pkey
220            .as_const()
221            .id()
222            .eq(&evp_pkey_type)
223            .then_some(evp_pkey)
224            .ok_or(KeyRejected::wrong_algorithm())
225    }
226
227    #[allow(non_snake_case)]
228    pub(crate) fn create_EVP_PKEY_CTX(&self) -> Result<LcPtr<EVP_PKEY_CTX>, ()> {
229        // The only modification made by EVP_PKEY_CTX_new to `priv_key` is to increment its
230        // refcount. The modification is made while holding a global lock:
231        // https://github.com/aws/aws-lc/blob/61503f7fe72457e12d3446853a5452d175560c49/crypto/refcount_lock.c#L29
232        LcPtr::new(unsafe { EVP_PKEY_CTX_new(*self.as_mut_unsafe(), null_mut()) })
233    }
234
235    pub(crate) fn parse_raw_private_key(
236        bytes: &[u8],
237        evp_pkey_type: c_int,
238    ) -> Result<Self, KeyRejected> {
239        #[cfg(all(feature = "unstable", not(feature = "fips")))]
240        if evp_pkey_type == EVP_PKEY_PQDSA {
241            return match bytes.len() {
242                2560 => Self::new(unsafe {
243                    EVP_PKEY_pqdsa_new_raw_private_key(NID_MLDSA44, bytes.as_ptr(), bytes.len())
244                }),
245                4032 => Self::new(unsafe {
246                    EVP_PKEY_pqdsa_new_raw_private_key(NID_MLDSA65, bytes.as_ptr(), bytes.len())
247                }),
248                4896 => Self::new(unsafe {
249                    EVP_PKEY_pqdsa_new_raw_private_key(NID_MLDSA87, bytes.as_ptr(), bytes.len())
250                }),
251                _ => Err(()),
252            }
253            .map_err(|()| KeyRejected::invalid_encoding());
254        }
255
256        Self::new(unsafe {
257            EVP_PKEY_new_raw_private_key(evp_pkey_type, null_mut(), bytes.as_ptr(), bytes.len())
258        })
259        .map_err(|()| KeyRejected::unspecified())
260    }
261
262    pub(crate) fn parse_raw_public_key(
263        bytes: &[u8],
264        evp_pkey_type: c_int,
265    ) -> Result<Self, KeyRejected> {
266        #[cfg(all(feature = "unstable", not(feature = "fips")))]
267        if evp_pkey_type == EVP_PKEY_PQDSA {
268            return match bytes.len() {
269                1312 => Self::new(unsafe {
270                    EVP_PKEY_pqdsa_new_raw_public_key(NID_MLDSA44, bytes.as_ptr(), bytes.len())
271                }),
272                1952 => Self::new(unsafe {
273                    EVP_PKEY_pqdsa_new_raw_public_key(NID_MLDSA65, bytes.as_ptr(), bytes.len())
274                }),
275                2592 => Self::new(unsafe {
276                    EVP_PKEY_pqdsa_new_raw_public_key(NID_MLDSA87, bytes.as_ptr(), bytes.len())
277                }),
278                _ => Err(()),
279            }
280            .map_err(|()| KeyRejected::unspecified());
281        }
282
283        Self::new(unsafe {
284            EVP_PKEY_new_raw_public_key(evp_pkey_type, null_mut(), bytes.as_ptr(), bytes.len())
285        })
286        .map_err(|()| KeyRejected::invalid_encoding())
287    }
288
289    pub(crate) fn sign<F>(
290        &self,
291        message: &[u8],
292        digest: Option<&'static digest::Algorithm>,
293        padding_fn: Option<F>,
294    ) -> Result<Box<[u8]>, Unspecified>
295    where
296        F: EVP_PKEY_CTX_consumer,
297    {
298        let mut md_ctx = DigestContext::new_uninit();
299        let evp_md = if let Some(alg) = digest {
300            *digest::match_digest_type(&alg.id)
301        } else {
302            null()
303        };
304        let mut pctx = null_mut::<EVP_PKEY_CTX>();
305        if 1 != unsafe {
306            // EVP_DigestSignInit does not mutate |pkey| for thread-safety purposes and may be
307            // used concurrently with other non-mutating functions on |pkey|.
308            // https://github.com/aws/aws-lc/blob/9b4b5a15a97618b5b826d742419ccd54c819fa42/include/openssl/evp.h#L297-L313
309            EVP_DigestSignInit(
310                md_ctx.as_mut_ptr(),
311                &mut pctx,
312                evp_md,
313                null_mut(),
314                *self.as_mut_unsafe(),
315            )
316        } {
317            return Err(Unspecified);
318        }
319
320        if let Some(pad_fn) = padding_fn {
321            pad_fn(pctx)?;
322        }
323
324        // Determine the maximum length of the signature.
325        let mut sig_len = 0;
326        if 1 != unsafe {
327            EVP_DigestSign(
328                md_ctx.as_mut_ptr(),
329                null_mut(),
330                &mut sig_len,
331                message.as_ptr(),
332                message.len(),
333            )
334        } {
335            return Err(Unspecified);
336        }
337        if sig_len == 0 {
338            return Err(Unspecified);
339        }
340
341        let mut signature = vec![0u8; sig_len];
342        if 1 != indicator_check!(unsafe {
343            EVP_DigestSign(
344                md_ctx.as_mut_ptr(),
345                signature.as_mut_ptr(),
346                &mut sig_len,
347                message.as_ptr(),
348                message.len(),
349            )
350        }) {
351            return Err(Unspecified);
352        }
353        signature.truncate(sig_len);
354        Ok(signature.into_boxed_slice())
355    }
356
357    pub(crate) fn verify<F>(
358        &self,
359        msg: &[u8],
360        digest: Option<&'static digest::Algorithm>,
361        padding_fn: Option<F>,
362        signature: &[u8],
363    ) -> Result<(), Unspecified>
364    where
365        F: EVP_PKEY_CTX_consumer,
366    {
367        let mut md_ctx = DigestContext::new_uninit();
368
369        let evp_md = if let Some(alg) = digest {
370            *digest::match_digest_type(&alg.id)
371        } else {
372            null()
373        };
374
375        let mut pctx = null_mut::<EVP_PKEY_CTX>();
376
377        if 1 != unsafe {
378            EVP_DigestVerifyInit(
379                md_ctx.as_mut_ptr(),
380                &mut pctx,
381                evp_md,
382                null_mut(),
383                *self.as_mut_unsafe(),
384            )
385        } {
386            return Err(Unspecified);
387        }
388        if let Some(pad_fn) = padding_fn {
389            pad_fn(pctx)?;
390        }
391
392        if 1 != indicator_check!(unsafe {
393            EVP_DigestVerify(
394                md_ctx.as_mut_ptr(),
395                signature.as_ptr(),
396                signature.len(),
397                msg.as_ptr(),
398                msg.len(),
399            )
400        }) {
401            return Err(Unspecified);
402        }
403
404        Ok(())
405    }
406
407    pub(crate) fn generate<F>(pkey_type: c_int, params_fn: Option<F>) -> Result<Self, Unspecified>
408    where
409        F: EVP_PKEY_CTX_consumer,
410    {
411        let mut pkey_ctx = LcPtr::new(unsafe { EVP_PKEY_CTX_new_id(pkey_type, null_mut()) })?;
412
413        if 1 != unsafe { EVP_PKEY_keygen_init(*pkey_ctx.as_mut()) } {
414            return Err(Unspecified);
415        }
416
417        if let Some(pad_fn) = params_fn {
418            pad_fn(*pkey_ctx.as_mut())?;
419        }
420
421        let mut pkey = null_mut::<EVP_PKEY>();
422
423        if 1 != indicator_check!(unsafe { EVP_PKEY_keygen(*pkey_ctx.as_mut(), &mut pkey) }) {
424            return Err(Unspecified);
425        }
426
427        Ok(LcPtr::new(pkey)?)
428    }
429}
430
431impl Clone for LcPtr<EVP_PKEY> {
432    fn clone(&self) -> Self {
433        // EVP_PKEY_up_ref increments the refcount while holding a global lock:
434        // https://github.com/aws/aws-lc/blob/61503f7fe72457e12d3446853a5452d175560c49/crypto/refcount_lock.c#L29
435        assert_eq!(
436            1,
437            unsafe { EVP_PKEY_up_ref(*self.as_mut_unsafe()) },
438            "infallible AWS-LC function"
439        );
440        Self::new(unsafe { *self.as_mut_unsafe() }).expect("non-null AWS-LC EVP_PKEY pointer")
441    }
442}