From f42bcb1cdba8d9d2b9588d9b19261ff64a16de2e Mon Sep 17 00:00:00 2001 From: Zhang Jingqiang Date: Mon, 24 Apr 2023 13:45:00 +0800 Subject: [PATCH 1/5] add x509_pubkey related types and methods --- openssl-sys/src/handwritten/types.rs | 2 + openssl-sys/src/handwritten/x509.rs | 67 ++++++++++++++ openssl/src/x509/mod.rs | 131 ++++++++++++++++++++++++++- openssl/src/x509/tests.rs | 23 ++++- 4 files changed, 221 insertions(+), 2 deletions(-) diff --git a/openssl-sys/src/handwritten/types.rs b/openssl-sys/src/handwritten/types.rs index a03a878305..3323ade7e3 100644 --- a/openssl-sys/src/handwritten/types.rs +++ b/openssl-sys/src/handwritten/types.rs @@ -346,6 +346,8 @@ pub enum X509_LOOKUP_METHOD {} pub enum X509_NAME {} +pub enum X509_PUBKEY {} + cfg_if! { if #[cfg(any(ossl110, libressl270))] { pub enum X509_STORE {} diff --git a/openssl-sys/src/handwritten/x509.rs b/openssl-sys/src/handwritten/x509.rs index f5e3c24289..bad3f0e564 100644 --- a/openssl-sys/src/handwritten/x509.rs +++ b/openssl-sys/src/handwritten/x509.rs @@ -152,6 +152,13 @@ extern "C" { len: *mut c_uint, ) -> c_int; + pub fn X509_pubkey_digest( + data: *const X509, + type_: *const EVP_MD, + md: *mut c_uchar, + len: *mut c_uint, + ) -> c_int; + pub fn X509_REQ_sign(x: *mut X509_REQ, pkey: *mut EVP_PKEY, md: *const EVP_MD) -> c_int; } @@ -356,6 +363,8 @@ const_ptr_api! { extern "C" { pub fn X509_REQ_set_pubkey(req: *mut X509_REQ, pkey: *mut EVP_PKEY) -> c_int; pub fn X509_REQ_get_pubkey(req: *mut X509_REQ) -> *mut EVP_PKEY; + #[cfg(ossl110)] + pub fn X509_REQ_get_X509_PUBKEY(req: *mut X509_REQ) -> *mut X509_PUBKEY; pub fn X509_REQ_get_extensions(req: *mut X509_REQ) -> *mut stack_st_X509_EXTENSION; } const_ptr_api! { @@ -435,6 +444,62 @@ const_ptr_api! { } } +extern "C" { + pub fn X509_PUBKEY_new() -> *mut X509_PUBKEY; + pub fn X509_PUBKEY_free(a: *mut X509_PUBKEY); + #[cfg(ossl300)] + pub fn X509_PUBKEY_dup(a: *const X509_PUBKEY) -> *mut X509_PUBKEY; + #[cfg(ossl300)] + pub fn X509_PUBKEY_eq(a: *const X509_PUBKEY, b: *const X509_PUBKEY) -> c_int; + pub fn d2i_X509_PUBKEY( + a: *mut *mut X509_PUBKEY, + in_: *mut *const c_uchar, + len: c_long, + ) -> *mut X509_PUBKEY; + #[cfg(ossl300)] + pub fn X509_PUBKEY_new_ex(libctx: *mut OSSL_LIB_CTX, propq: *const c_char) -> *mut X509_PUBKEY; + pub fn X509_PUBKEY_set(x: *mut *mut X509_PUBKEY, pkey: *mut EVP_PKEY) -> c_int; + + pub fn X509_PUBKEY_set0_param( + pub_: *mut X509_PUBKEY, + aobj: *mut ASN1_OBJECT, + ptype: c_int, + pval: *mut c_void, + penc: *mut c_uchar, + penclen: c_int, + ) -> c_int; +} +cfg_if! { + if #[cfg(ossl300)] { + extern "C" { + pub fn i2d_X509_PUBKEY(a: *const X509_PUBKEY, out: *mut *mut c_uchar) -> c_int; + pub fn X509_PUBKEY_get0_param( + ppkalg: *mut *mut ASN1_OBJECT, + pk: *mut *const c_uchar, + ppklen: *mut c_int, + pa: *mut *mut X509_ALGOR, + pub_: *const X509_PUBKEY, + ) -> c_int; + pub fn X509_PUBKEY_get(key: *const X509_PUBKEY) -> *mut EVP_PKEY; + pub fn X509_PUBKEY_get0(key: *const X509_PUBKEY) -> *mut EVP_PKEY; + } + } else { + extern "C" { + pub fn i2d_X509_PUBKEY(a: *mut X509_PUBKEY, out: *mut *mut c_uchar) -> c_int; + pub fn X509_PUBKEY_get0_param( + ppkalg: *mut *mut ASN1_OBJECT, + pk: *mut *const c_uchar, + ppklen: *mut c_int, + pa: *mut *mut X509_ALGOR, + pub_: *mut X509_PUBKEY, + ) -> c_int; + pub fn X509_PUBKEY_get(key: *mut X509_PUBKEY) -> *mut EVP_PKEY; + #[cfg(ossl110)] + pub fn X509_PUBKEY_get0(key: *mut X509_PUBKEY) -> *mut EVP_PKEY; + } + } +} + extern "C" { #[cfg(any(ossl110, libressl281))] pub fn X509_CRL_get_REVOKED(crl: *mut X509_CRL) -> *mut stack_st_X509_REVOKED; @@ -447,6 +512,8 @@ extern "C" { #[cfg(ossl110)] pub fn X509_get0_extensions(req: *const X509) -> *const stack_st_X509_EXTENSION; + #[cfg(ossl110)] + pub fn X509_get_X509_PUBKEY(x: *const X509) -> *mut X509_PUBKEY; pub fn X509_CRL_set_version(crl: *mut X509_CRL, version: c_long) -> c_int; } diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index 97242ff4d8..cfba35161c 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -9,7 +9,7 @@ use cfg_if::cfg_if; use foreign_types::{ForeignType, ForeignTypeRef, Opaque}; -use libc::{c_int, c_long, c_uint, c_void}; +use libc::{c_int, c_long, c_uchar, c_uint, c_void}; use std::cmp::{self, Ordering}; use std::convert::{TryFrom, TryInto}; use std::error::Error; @@ -539,6 +539,15 @@ impl X509Ref { } } + #[corresponds(X509_get_X509_PUBKEY)] + #[cfg(ossl110)] + pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> { + unsafe { + let key = cvt_p(ffi::X509_get_X509_PUBKEY(self.as_ptr()))?; + Ok(X509PubkeyRef::from_ptr(key)) + } + } + /// Returns a digest of the DER representation of the certificate. #[corresponds(X509_digest)] pub fn digest(&self, hash_type: MessageDigest) -> Result { @@ -565,6 +574,27 @@ impl X509Ref { self.digest(hash_type).map(|b| b.to_vec()) } + /// Returns a digest of the DER representation of the public key in the certificate. + #[corresponds(X509_pubkey_digest)] + pub fn pubkey_digest(&self, hash_type: MessageDigest) -> Result { + unsafe { + let mut digest = DigestBytes { + buf: [0; ffi::EVP_MAX_MD_SIZE as usize], + len: ffi::EVP_MAX_MD_SIZE as usize, + }; + let mut len = ffi::EVP_MAX_MD_SIZE as c_uint; + cvt(ffi::X509_pubkey_digest( + self.as_ptr(), + hash_type.as_ptr(), + digest.buf.as_mut_ptr() as *mut _, + &mut len, + ))?; + digest.len = len as usize; + + Ok(digest) + } + } + /// Returns the certificate's Not After validity period. #[corresponds(X509_getm_notAfter)] pub fn not_after(&self) -> &Asn1TimeRef { @@ -1352,6 +1382,92 @@ impl fmt::Debug for X509NameEntryRef { } } +foreign_type_and_impl_send_sync! { + type CType = ffi::X509_PUBKEY; + fn drop = ffi::X509_PUBKEY_free; + + /// The SubjectPublicKeyInfo of an `X509` certificate. + pub struct X509Pubkey; + /// Reference to `X509Pubkey`. + pub struct X509PubkeyRef; +} + +impl X509Pubkey { + from_der! { + /// Deserializes a DER-encoded X509 SubjectPublicKeyInfo. + /// + /// This corresponds to [`d2i_X509_PUBKEY`]. + /// + /// [`d2i_X509_PUBKEY`]: https://www.openssl.org/docs/manmaster/crypto/d2i_X509_PUBKEY.html + from_der, + X509Pubkey, + ffi::d2i_X509_PUBKEY + } + + /// Build a X509Pubkey from the public key. + /// + /// This corresponds to [`X509_PUBKEY_set`]. + /// + /// [`X509_PUBKEY_set`]: https://www.openssl.org/docs/manmaster/crypto/X509_PUBKEY_set.html + pub fn from_pubkey(key: &PKeyRef) -> Result + where + T: HasPublic, + { + let mut p = ptr::null_mut() as *mut ffi::X509_PUBKEY; + unsafe { + cvt(ffi::X509_PUBKEY_set(&mut p as *mut _, key.as_ptr()))?; + } + Ok(X509Pubkey(p)) + } +} + +impl X509PubkeyRef { + /// Copies the X509 SubjectPublicKeyInfo to a new `X509Pubkey`. + #[corresponds(X509_PUBKEY_dup)] + #[cfg(ossl300)] + pub fn to_owned(&self) -> Result { + unsafe { cvt_p(ffi::X509_PUBKEY_dup(self.as_ptr())).map(|n| X509Pubkey::from_ptr(n)) } + } + + to_der! { + /// Serializes the X509 SubjectPublicKeyInfo to DER-encoded. + /// + /// This corresponds to [`i2d_X509_PUBKEY`]. + /// + /// [`i2d_X509_PUBKEY`]: https://www.openssl.org/docs/manmaster/crypto/i2d_X509_PUBKEY.html + to_der, + ffi::i2d_X509_PUBKEY + } + + /// Returns the public key of the X509 SubjectPublicKeyInfo. + /// + /// This corresponds to [`X509_PUBKEY_get"] + /// + /// [`X509_PUBKEY_get`]: https://www.openssl.org/docs/manmaster/crypto/X509_PUBKEY_get.html + pub fn public_key(&self) -> Result, ErrorStack> { + unsafe { + let key = cvt_p(ffi::X509_PUBKEY_get(self.as_ptr()))?; + Ok(PKey::from_ptr(key)) + } + } + + pub fn public_key_bytes(&self) -> Result<&[u8], ErrorStack> { + unsafe { + let mut pk = ptr::null_mut() as *const c_uchar; + let mut pkt_len: c_int = 0; + cvt(ffi::X509_PUBKEY_get0_param( + ptr::null_mut(), + &mut pk as *mut _, + &mut pkt_len as *mut _, + ptr::null_mut(), + self.as_ptr(), + ))?; + + Ok(slice::from_raw_parts(pk as *const u8, pkt_len as usize)) + } + } +} + /// A builder used to construct an `X509Req`. pub struct X509ReqBuilder(X509Req); @@ -1579,6 +1695,19 @@ impl X509ReqRef { } } + /// Returns the X509Pubkey of the certificate request. + /// + /// This corresponds to [`X509_REQ_get_X509_PUBKEY"] + /// + /// [`X509_REQ_get_X509_PUBKEY`]: https://www.openssl.org/docs/manmaster/crypto/X509_REQ_get_X509_PUBKEY.html + #[cfg(ossl110)] + pub fn x509_pubkey(&self) -> Result<&X509PubkeyRef, ErrorStack> { + unsafe { + let key = cvt_p(ffi::X509_REQ_get_X509_PUBKEY(self.as_ptr()))?; + Ok(X509PubkeyRef::from_ptr(key)) + } + } + /// Check if the certificate request is signed using the given public key. /// /// Returns `true` if verification succeeds. diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index a4a3de970c..c095efdb67 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -25,7 +25,8 @@ use crate::x509::X509PurposeRef; #[cfg(ossl110)] use crate::x509::{CrlReason, X509Builder}; use crate::x509::{ - CrlStatus, X509Crl, X509Extension, X509Name, X509Req, X509StoreContext, X509VerifyResult, X509, + CrlStatus, X509Crl, X509Extension, X509Name, X509Pubkey, X509Req, X509StoreContext, + X509VerifyResult, X509, }; #[cfg(ossl110)] @@ -265,6 +266,26 @@ fn test_subject_alt_name_iter() { assert!(subject_alt_names_iter.next().is_none()); } +#[test] +fn test_x509_pubkey() { + let cert = include_bytes!("../../test/certv3.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let pubkey_digest = cert.pubkey_digest(MessageDigest::sha1()).unwrap(); + + let pkey = cert.public_key().unwrap(); + let x_pkey = X509Pubkey::from_pubkey(&pkey).unwrap(); + + let xbytes = x_pkey.public_key_bytes().unwrap(); + let hashed = crate::hash::hash(MessageDigest::sha1(), xbytes).unwrap(); + + assert_eq!(pubkey_digest.as_ref(), hashed.as_ref()); + + let y_pkey = cert.x509_pubkey().unwrap(); + let ybytes = y_pkey.public_key_bytes().unwrap(); + assert_eq!(xbytes, ybytes); +} + #[test] fn test_aia_ca_issuer() { // With AIA From af7d59c6674e299c99746a30516bcd0538e800d9 Mon Sep 17 00:00:00 2001 From: Zhang Jingqiang Date: Mon, 24 Apr 2023 22:13:41 +0800 Subject: [PATCH 2/5] fix testcase --- openssl/src/x509/tests.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index c095efdb67..53d2b2bfa1 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -280,6 +280,17 @@ fn test_x509_pubkey() { let hashed = crate::hash::hash(MessageDigest::sha1(), xbytes).unwrap(); assert_eq!(pubkey_digest.as_ref(), hashed.as_ref()); +} + +#[test] +#[cfg(ossl110)] +fn test_x509_pubkey_ref() { + let cert = include_bytes!("../../test/certv3.pem"); + let cert = X509::from_pem(cert).unwrap(); + + let pkey = cert.public_key().unwrap(); + let x_pkey = X509Pubkey::from_pubkey(&pkey).unwrap(); + let xbytes = x_pkey.public_key_bytes().unwrap(); let y_pkey = cert.x509_pubkey().unwrap(); let ybytes = y_pkey.public_key_bytes().unwrap(); From cccbd13a37f6494b65b9efd53bb255e40a6c450a Mon Sep 17 00:00:00 2001 From: Zhang Jingqiang Date: Sun, 7 May 2023 09:27:52 +0800 Subject: [PATCH 3/5] rename X509Pubkey functions and add comment --- openssl/src/x509/mod.rs | 7 ++++++- openssl/src/x509/tests.rs | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index cfba35161c..76091aa5fa 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -1451,7 +1451,12 @@ impl X509PubkeyRef { } } - pub fn public_key_bytes(&self) -> Result<&[u8], ErrorStack> { + /// Get the encoded bytes of the X509 SubjectPublicKeyInfo. + /// + /// This corresponds to ['X509_PUBKEY_get0_param'] + /// + /// ['X509_PUBKEY_get0_param']: https://www.openssl.org/docs/man3.0/man3/X509_PUBKEY_get0_param.html + pub fn encoded_bytes(&self) -> Result<&[u8], ErrorStack> { unsafe { let mut pk = ptr::null_mut() as *const c_uchar; let mut pkt_len: c_int = 0; diff --git a/openssl/src/x509/tests.rs b/openssl/src/x509/tests.rs index 53d2b2bfa1..adc610f314 100644 --- a/openssl/src/x509/tests.rs +++ b/openssl/src/x509/tests.rs @@ -276,7 +276,7 @@ fn test_x509_pubkey() { let pkey = cert.public_key().unwrap(); let x_pkey = X509Pubkey::from_pubkey(&pkey).unwrap(); - let xbytes = x_pkey.public_key_bytes().unwrap(); + let xbytes = x_pkey.encoded_bytes().unwrap(); let hashed = crate::hash::hash(MessageDigest::sha1(), xbytes).unwrap(); assert_eq!(pubkey_digest.as_ref(), hashed.as_ref()); @@ -290,10 +290,10 @@ fn test_x509_pubkey_ref() { let pkey = cert.public_key().unwrap(); let x_pkey = X509Pubkey::from_pubkey(&pkey).unwrap(); - let xbytes = x_pkey.public_key_bytes().unwrap(); + let xbytes = x_pkey.encoded_bytes().unwrap(); let y_pkey = cert.x509_pubkey().unwrap(); - let ybytes = y_pkey.public_key_bytes().unwrap(); + let ybytes = y_pkey.encoded_bytes().unwrap(); assert_eq!(xbytes, ybytes); } From e06799badfbcd98902c768831ae9bd4636237b5a Mon Sep 17 00:00:00 2001 From: Zhang Jingqiang Date: Mon, 6 Nov 2023 11:24:14 +0800 Subject: [PATCH 4/5] fix clippy warning --- openssl/src/x509/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index 76091aa5fa..e14d8fff4b 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -1413,7 +1413,7 @@ impl X509Pubkey { where T: HasPublic, { - let mut p = ptr::null_mut() as *mut ffi::X509_PUBKEY; + let mut p = ptr::null_mut(); unsafe { cvt(ffi::X509_PUBKEY_set(&mut p as *mut _, key.as_ptr()))?; } @@ -1468,7 +1468,7 @@ impl X509PubkeyRef { self.as_ptr(), ))?; - Ok(slice::from_raw_parts(pk as *const u8, pkt_len as usize)) + Ok(slice::from_raw_parts(pk, pkt_len as usize)) } } } From b5ac7ae579cef4e66058e8ab59bd0f45818868c9 Mon Sep 17 00:00:00 2001 From: Zhang Jingqiang Date: Wed, 3 Jan 2024 17:30:35 +0800 Subject: [PATCH 5/5] use const api macro --- openssl-sys/src/handwritten/x509.rs | 40 +++++++++-------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/openssl-sys/src/handwritten/x509.rs b/openssl-sys/src/handwritten/x509.rs index bad3f0e564..dbfedefb45 100644 --- a/openssl-sys/src/handwritten/x509.rs +++ b/openssl-sys/src/handwritten/x509.rs @@ -469,34 +469,18 @@ extern "C" { penclen: c_int, ) -> c_int; } -cfg_if! { - if #[cfg(ossl300)] { - extern "C" { - pub fn i2d_X509_PUBKEY(a: *const X509_PUBKEY, out: *mut *mut c_uchar) -> c_int; - pub fn X509_PUBKEY_get0_param( - ppkalg: *mut *mut ASN1_OBJECT, - pk: *mut *const c_uchar, - ppklen: *mut c_int, - pa: *mut *mut X509_ALGOR, - pub_: *const X509_PUBKEY, - ) -> c_int; - pub fn X509_PUBKEY_get(key: *const X509_PUBKEY) -> *mut EVP_PKEY; - pub fn X509_PUBKEY_get0(key: *const X509_PUBKEY) -> *mut EVP_PKEY; - } - } else { - extern "C" { - pub fn i2d_X509_PUBKEY(a: *mut X509_PUBKEY, out: *mut *mut c_uchar) -> c_int; - pub fn X509_PUBKEY_get0_param( - ppkalg: *mut *mut ASN1_OBJECT, - pk: *mut *const c_uchar, - ppklen: *mut c_int, - pa: *mut *mut X509_ALGOR, - pub_: *mut X509_PUBKEY, - ) -> c_int; - pub fn X509_PUBKEY_get(key: *mut X509_PUBKEY) -> *mut EVP_PKEY; - #[cfg(ossl110)] - pub fn X509_PUBKEY_get0(key: *mut X509_PUBKEY) -> *mut EVP_PKEY; - } +const_ptr_api! { + extern "C" { + pub fn i2d_X509_PUBKEY(a: #[const_ptr_if(ossl300)] X509_PUBKEY, out: *mut *mut c_uchar) -> c_int; + pub fn X509_PUBKEY_get0_param( + ppkalg: *mut *mut ASN1_OBJECT, + pk: *mut *const c_uchar, + ppklen: *mut c_int, + pa: *mut *mut X509_ALGOR, + pub_: #[const_ptr_if(ossl300)] X509_PUBKEY, + ) -> c_int; + pub fn X509_PUBKEY_get(key: #[const_ptr_if(ossl300)] X509_PUBKEY) -> *mut EVP_PKEY; + pub fn X509_PUBKEY_get0(key: #[const_ptr_if(ossl300)] X509_PUBKEY) -> *mut EVP_PKEY; } }