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