From 02c7470a447005834c3d3a5cf982f6b38c460e7c Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 3 Jun 2025 16:32:24 -0700 Subject: [PATCH 1/3] Expose PKey::raw_{private,public}_key --- boring/src/pkey.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/boring/src/pkey.rs b/boring/src/pkey.rs index 1c4012ca4..ce60439af 100644 --- a/boring/src/pkey.rs +++ b/boring/src/pkey.rs @@ -56,7 +56,7 @@ use crate::ec::EcKey; use crate::error::ErrorStack; use crate::rsa::Rsa; use crate::util::{invoke_passwd_cb, CallbackState}; -use crate::{cvt, cvt_p}; +use crate::{cvt, cvt_0i, cvt_p}; /// A tag type indicating that a key only has parameters. pub enum Params {} @@ -222,6 +222,26 @@ where { unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 } } + + /// Outputs a copy of the "raw" form of the public key. Only supported for certain key types. + /// + /// Returns the number of bytes written, or the size of the raw form if `out` is empty. + #[corresponds(EVP_PKEY_get_raw_public_key)] + pub fn raw_public_key(&self, out: &mut [u8]) -> Result { + unsafe { + let mut size = out.len(); + _ = cvt_0i(ffi::EVP_PKEY_get_raw_public_key( + self.as_ptr(), + if size == 0 { + std::ptr::null_mut() + } else { + out.as_mut_ptr() + }, + &mut size, + ))?; + Ok(size) + } + } } impl PKeyRef @@ -260,6 +280,26 @@ where private_key_to_der_pkcs8_passphrase, ffi::i2d_PKCS8PrivateKey_bio } + + /// Outputs a copy of the "raw" form of the private key. Only supported for certain key types. + /// + /// Returns the number of bytes written, or the size of the raw form if `out` is empty. + #[corresponds(EVP_PKEY_get_raw_private_key)] + pub fn raw_private_key(&self, out: &mut [u8]) -> Result { + unsafe { + let mut size = out.len(); + _ = cvt_0i(ffi::EVP_PKEY_get_raw_private_key( + self.as_ptr(), + if size == 0 { + std::ptr::null_mut() + } else { + out.as_mut_ptr() + }, + &mut size, + ))?; + Ok(size) + } + } } impl fmt::Debug for PKey { @@ -445,6 +485,8 @@ use crate::ffi::EVP_PKEY_up_ref; #[cfg(test)] mod tests { + use hex::FromHex as _; + use crate::ec::EcKey; use crate::nid::Nid; use crate::rsa::Rsa; @@ -555,4 +597,28 @@ mod tests { assert_eq!(pkey.id(), Id::EC); assert!(pkey.rsa().is_err()); } + + #[test] + fn test_raw_accessors() { + const ED25519_PRIVATE_KEY_DER: &str = concat!( + "302e020100300506032b6570042204207c8c6497f9960d5595d7815f550569e5", + "f77764ac97e63e339aaa68cc1512b683" + ); + let pkey = + PKey::private_key_from_der(&Vec::from_hex(ED25519_PRIVATE_KEY_DER).unwrap()).unwrap(); + assert_eq!(pkey.id(), Id::ED25519); + + let priv_len = pkey.raw_private_key(&mut []).unwrap(); + assert_eq!(priv_len, 32); + let mut raw_private_key = [0; 32]; + _ = pkey.raw_private_key(&mut raw_private_key).unwrap(); + assert_ne!(raw_private_key, [0; 32]); + + let pub_len = pkey.raw_public_key(&mut []).unwrap(); + assert_eq!(pub_len, 32); + let mut raw_public_key = [0; 32]; + _ = pkey.raw_public_key(&mut raw_public_key).unwrap(); + assert_ne!(raw_public_key, [0; 32]); + assert_ne!(raw_public_key, raw_private_key); + } } From 60a70b8c354538bdd896df42f92bb82a7662fd90 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Thu, 5 Jun 2025 10:10:48 -0700 Subject: [PATCH 2/3] Split out raw_{private,public}_key_len for a more ergonomic API --- boring/src/pkey.rs | 74 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/boring/src/pkey.rs b/boring/src/pkey.rs index ce60439af..5a1e2c76d 100644 --- a/boring/src/pkey.rs +++ b/boring/src/pkey.rs @@ -223,23 +223,35 @@ where unsafe { ffi::EVP_PKEY_cmp(self.as_ptr(), other.as_ptr()) == 1 } } + /// Returns the length of the "raw" form of the public key. Only supported for certain key types. + /// + /// Returns the used portion of `out`. + #[corresponds(EVP_PKEY_get_raw_public_key)] + pub fn raw_public_key_len(&self) -> Result { + unsafe { + let mut size = 0; + _ = cvt_0i(ffi::EVP_PKEY_get_raw_public_key( + self.as_ptr(), + std::ptr::null_mut(), + &mut size, + ))?; + Ok(size) + } + } + /// Outputs a copy of the "raw" form of the public key. Only supported for certain key types. /// - /// Returns the number of bytes written, or the size of the raw form if `out` is empty. + /// Returns the used portion of `out`. #[corresponds(EVP_PKEY_get_raw_public_key)] - pub fn raw_public_key(&self, out: &mut [u8]) -> Result { + pub fn raw_public_key<'a>(&self, out: &'a mut [u8]) -> Result<&'a [u8], ErrorStack> { unsafe { let mut size = out.len(); _ = cvt_0i(ffi::EVP_PKEY_get_raw_public_key( self.as_ptr(), - if size == 0 { - std::ptr::null_mut() - } else { - out.as_mut_ptr() - }, + out.as_mut_ptr(), &mut size, ))?; - Ok(size) + Ok(&out[..size]) } } } @@ -281,23 +293,35 @@ where ffi::i2d_PKCS8PrivateKey_bio } + /// Returns the length of the "raw" form of the private key. Only supported for certain key types. + /// + /// Returns the used portion of `out`. + #[corresponds(EVP_PKEY_get_raw_private_key)] + pub fn raw_private_key_len(&self) -> Result { + unsafe { + let mut size = 0; + _ = cvt_0i(ffi::EVP_PKEY_get_raw_private_key( + self.as_ptr(), + std::ptr::null_mut(), + &mut size, + ))?; + Ok(size) + } + } + /// Outputs a copy of the "raw" form of the private key. Only supported for certain key types. /// - /// Returns the number of bytes written, or the size of the raw form if `out` is empty. + /// Returns the used portion of `out`. #[corresponds(EVP_PKEY_get_raw_private_key)] - pub fn raw_private_key(&self, out: &mut [u8]) -> Result { + pub fn raw_private_key<'a>(&self, out: &'a mut [u8]) -> Result<&'a [u8], ErrorStack> { unsafe { let mut size = out.len(); _ = cvt_0i(ffi::EVP_PKEY_get_raw_private_key( self.as_ptr(), - if size == 0 { - std::ptr::null_mut() - } else { - out.as_mut_ptr() - }, + out.as_mut_ptr(), &mut size, ))?; - Ok(size) + Ok(&out[..size]) } } } @@ -608,17 +632,23 @@ mod tests { PKey::private_key_from_der(&Vec::from_hex(ED25519_PRIVATE_KEY_DER).unwrap()).unwrap(); assert_eq!(pkey.id(), Id::ED25519); - let priv_len = pkey.raw_private_key(&mut []).unwrap(); + let priv_len = pkey.raw_private_key_len().unwrap(); assert_eq!(priv_len, 32); - let mut raw_private_key = [0; 32]; - _ = pkey.raw_private_key(&mut raw_private_key).unwrap(); + let mut raw_private_key_buf = [0; 40]; + let raw_private_key = pkey.raw_private_key(&mut raw_private_key_buf).unwrap(); + assert_eq!(raw_private_key.len(), 32); assert_ne!(raw_private_key, [0; 32]); + pkey.raw_private_key(&mut [0; 5]) + .expect_err("buffer too small"); - let pub_len = pkey.raw_public_key(&mut []).unwrap(); + let pub_len = pkey.raw_public_key_len().unwrap(); assert_eq!(pub_len, 32); - let mut raw_public_key = [0; 32]; - _ = pkey.raw_public_key(&mut raw_public_key).unwrap(); + let mut raw_public_key_buf = [0; 40]; + let raw_public_key = pkey.raw_public_key(&mut raw_public_key_buf).unwrap(); + assert_eq!(raw_public_key.len(), 32); assert_ne!(raw_public_key, [0; 32]); assert_ne!(raw_public_key, raw_private_key); + pkey.raw_public_key(&mut [0; 5]) + .expect_err("buffer too small"); } } From 3f7ca98304d090e82ac9e0cb1ed18db7ba017cee Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Thu, 5 Jun 2025 13:12:49 -0700 Subject: [PATCH 3/3] Fix copy-pasted doc comment --- boring/src/pkey.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/boring/src/pkey.rs b/boring/src/pkey.rs index 5a1e2c76d..51ad09c59 100644 --- a/boring/src/pkey.rs +++ b/boring/src/pkey.rs @@ -224,8 +224,6 @@ where } /// Returns the length of the "raw" form of the public key. Only supported for certain key types. - /// - /// Returns the used portion of `out`. #[corresponds(EVP_PKEY_get_raw_public_key)] pub fn raw_public_key_len(&self) -> Result { unsafe { @@ -294,8 +292,6 @@ where } /// Returns the length of the "raw" form of the private key. Only supported for certain key types. - /// - /// Returns the used portion of `out`. #[corresponds(EVP_PKEY_get_raw_private_key)] pub fn raw_private_key_len(&self) -> Result { unsafe {