Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add X509 pubkey related types and methods #1902

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions openssl-sys/src/handwritten/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
Expand Down
51 changes: 51 additions & 0 deletions openssl-sys/src/handwritten/x509.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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! {
Expand Down Expand Up @@ -435,6 +444,46 @@ 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;
}
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;
}
}

extern "C" {
#[cfg(any(ossl110, libressl281))]
pub fn X509_CRL_get_REVOKED(crl: *mut X509_CRL) -> *mut stack_st_X509_REVOKED;
Expand All @@ -447,6 +496,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;
}
Expand Down
136 changes: 135 additions & 1 deletion openssl/src/x509/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<DigestBytes, ErrorStack> {
Expand All @@ -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<DigestBytes, ErrorStack> {
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 {
Expand Down Expand Up @@ -1352,6 +1382,97 @@ 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<T>(key: &PKeyRef<T>) -> Result<Self, ErrorStack>
where
T: HasPublic,
{
let mut p = ptr::null_mut();
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<X509Pubkey, ErrorStack> {
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<PKey<Public>, ErrorStack> {
unsafe {
let key = cvt_p(ffi::X509_PUBKEY_get(self.as_ptr()))?;
Ok(PKey::from_ptr(key))
}
}

/// 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;
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, pkt_len as usize))
}
}
}

/// A builder used to construct an `X509Req`.
pub struct X509ReqBuilder(X509Req);

Expand Down Expand Up @@ -1579,6 +1700,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.
Expand Down
34 changes: 33 additions & 1 deletion openssl/src/x509/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -265,6 +266,37 @@ 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.encoded_bytes().unwrap();
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.encoded_bytes().unwrap();

let y_pkey = cert.x509_pubkey().unwrap();
let ybytes = y_pkey.encoded_bytes().unwrap();
assert_eq!(xbytes, ybytes);
}

#[test]
fn test_aia_ca_issuer() {
// With AIA
Expand Down
Loading