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