Skip to main content

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