diff --git a/openssl-sys/src/handwritten/x509.rs b/openssl-sys/src/handwritten/x509.rs index 0bb682764..2b33a6593 100644 --- a/openssl-sys/src/handwritten/x509.rs +++ b/openssl-sys/src/handwritten/x509.rs @@ -210,8 +210,18 @@ extern "C" { pub fn X509_to_X509_REQ(x: *mut X509, pkey: *mut EVP_PKEY, md: *const EVP_MD) -> *mut X509_REQ; + pub fn X509_ALGOR_new() -> *mut X509_ALGOR; pub fn X509_ALGOR_free(x: *mut X509_ALGOR); + pub fn X509_ALGOR_set0( + alg: *mut X509_ALGOR, + aobj: *mut ASN1_OBJECT, + ptype: c_int, + pval: *mut c_void, + ) -> c_int; + + pub fn X509_ALGOR_cmp(alg0: *const X509_ALGOR, alg1: *const X509_ALGOR) -> c_int; + pub fn X509_REVOKED_new() -> *mut X509_REVOKED; pub fn X509_REVOKED_free(x: *mut X509_REVOKED); } diff --git a/openssl-sys/src/lib.rs b/openssl-sys/src/lib.rs index 0e23386fd..ce91e6d95 100644 --- a/openssl-sys/src/lib.rs +++ b/openssl-sys/src/lib.rs @@ -62,6 +62,7 @@ mod openssl { pub use self::ssl::*; pub use self::ssl3::*; pub use self::tls1::*; + pub use self::ts::*; pub use self::types::*; pub use self::x509::*; pub use self::x509_vfy::*; @@ -92,6 +93,7 @@ mod openssl { mod ssl; mod ssl3; mod tls1; + mod ts; mod types; mod x509; mod x509_vfy; diff --git a/openssl-sys/src/ts.rs b/openssl-sys/src/ts.rs new file mode 100644 index 000000000..31075afc2 --- /dev/null +++ b/openssl-sys/src/ts.rs @@ -0,0 +1,131 @@ +use libc::*; + +use super::*; + +pub enum TS_MSG_IMPRINT {} +pub enum TS_REQ {} +pub enum TS_RESP {} +pub enum TS_TST_INFO {} +pub enum TS_RESP_CTX {} +pub enum TS_VERIFY_CTX {} + +pub const TS_VFY_SIGNATURE: c_uint = 0x1; +pub const TS_VFY_VERSION: c_uint = 0x2; +pub const TS_VFY_POLICY: c_uint = 0x4; +pub const TS_VFY_IMPRINT: c_uint = 0x8; +pub const TS_VFY_DATA: c_uint = 0x10; +pub const TS_VFY_NONCE: c_uint = 0x20; +pub const TS_VFY_SIGNER: c_uint = 0x40; +pub const TS_VFY_TSA_NAME: c_uint = 0x80; + +pub const TS_VFY_ALL_IMPRINT: c_uint = TS_VFY_SIGNATURE + | TS_VFY_VERSION + | TS_VFY_POLICY + | TS_VFY_IMPRINT + | TS_VFY_NONCE + | TS_VFY_SIGNER + | TS_VFY_TSA_NAME; +pub const TS_VFY_ALL_DATA: c_uint = TS_VFY_SIGNATURE + | TS_VFY_VERSION + | TS_VFY_POLICY + | TS_VFY_DATA + | TS_VFY_NONCE + | TS_VFY_SIGNER + | TS_VFY_TSA_NAME; + +pub const TS_STATUS_GRANTED: c_uint = 0; +pub const TS_STATUS_GRANTED_WITH_MODS: c_uint = 1; +pub const TS_STATUS_REJECTION: c_uint = 2; +pub const TS_STATUS_WAITING: c_uint = 3; +pub const TS_STATUS_REVOCATION_WARNING: c_uint = 4; +pub const TS_STATUS_REVOCATION_NOTIFICATION: c_uint = 5; + +extern "C" { + pub fn TS_MSG_IMPRINT_new() -> *mut TS_MSG_IMPRINT; + pub fn TS_MSG_IMPRINT_free(a: *mut TS_MSG_IMPRINT); + pub fn TS_MSG_IMPRINT_set_algo(a: *mut TS_MSG_IMPRINT, alg: *mut X509_ALGOR) -> c_int; + pub fn TS_MSG_IMPRINT_get_algo(a: *mut TS_MSG_IMPRINT) -> *mut X509_ALGOR; + pub fn TS_MSG_IMPRINT_set_msg(a: *mut TS_MSG_IMPRINT, d: *mut c_uchar, length: c_int) -> c_int; + pub fn TS_MSG_IMPRINT_get_msg(a: *mut TS_MSG_IMPRINT) -> *mut ASN1_OCTET_STRING; + + pub fn TS_REQ_new() -> *mut TS_REQ; + pub fn TS_REQ_free(a: *mut TS_REQ); + pub fn d2i_TS_REQ(a: *mut *mut TS_REQ, pp: *mut *const c_uchar, length: c_long) -> *mut TS_REQ; + pub fn i2d_TS_REQ(a: *const TS_REQ, pp: *mut *mut c_uchar) -> c_int; + pub fn TS_REQ_set_version(a: *mut TS_REQ, version: c_long) -> c_int; + pub fn TS_REQ_set_msg_imprint(a: *mut TS_REQ, msg_imprint: *mut TS_MSG_IMPRINT) -> c_int; + pub fn TS_REQ_get_msg_imprint(a: *mut TS_REQ) -> *mut TS_MSG_IMPRINT; + pub fn TS_REQ_set_nonce(a: *mut TS_REQ, nonce: *const ASN1_INTEGER) -> c_int; + pub fn TS_REQ_set_cert_req(a: *mut TS_REQ, cert_req: c_int) -> c_int; + + pub fn TS_RESP_new() -> *mut TS_RESP; + pub fn TS_RESP_free(a: *mut TS_RESP); + pub fn d2i_TS_RESP( + a: *mut *mut TS_RESP, + pp: *mut *const c_uchar, + length: c_long, + ) -> *mut TS_RESP; + pub fn i2d_TS_RESP(a: *const TS_RESP, pp: *mut *mut c_uchar) -> c_int; + + pub fn TS_VERIFY_CTX_new() -> *mut TS_VERIFY_CTX; + pub fn TS_VERIFY_CTX_free(ctx: *mut TS_VERIFY_CTX); + #[cfg(ossl110)] + pub fn TS_VERIFY_CTX_set_imprint( + ctx: *mut TS_VERIFY_CTX, + hexstr: *mut c_uchar, + length: c_long, + ) -> *mut c_uchar; + pub fn TS_RESP_verify_response(ctx: *mut TS_VERIFY_CTX, response: *mut TS_RESP) -> c_int; + + pub fn TS_REQ_to_TS_VERIFY_CTX(req: *mut TS_REQ, ctx: *mut TS_VERIFY_CTX) + -> *mut TS_VERIFY_CTX; + + pub fn TS_RESP_CTX_new() -> *mut TS_RESP_CTX; + pub fn TS_RESP_CTX_free(ctx: *mut TS_RESP_CTX); + pub fn TS_RESP_CTX_set_signer_cert(ctx: *mut TS_RESP_CTX, signer: *mut X509) -> c_int; + pub fn TS_RESP_CTX_set_signer_key(ctx: *mut TS_RESP_CTX, key: *mut EVP_PKEY) -> c_int; + pub fn TS_RESP_CTX_add_md(ctx: *mut TS_RESP_CTX, md: *const EVP_MD) -> c_int; + + pub fn TS_RESP_create_response(ctx: *mut TS_RESP_CTX, req_bio: *mut BIO) -> *mut TS_RESP; + + pub fn TS_RESP_get_tst_info(a: *mut TS_RESP) -> *mut TS_TST_INFO; + pub fn TS_TST_INFO_get_time(a: *const TS_TST_INFO) -> *const ASN1_STRING; + pub fn TS_TST_INFO_free(a: *mut TS_TST_INFO); +} + +cfg_if! { + if #[cfg(any(ossl110, libressl280))] { + extern "C" { + pub fn TS_REQ_set_policy_id( + a: *mut TS_REQ, + policy: *const ASN1_OBJECT + ) -> c_int; + pub fn TS_RESP_CTX_set_def_policy( + ctx: *mut TS_RESP_CTX, + def_policy: *const ASN1_OBJECT + ) -> c_int; + } + } else { + extern "C" { + pub fn TS_REQ_set_policy_id( + a: *mut TS_REQ, + policy: *mut ASN1_OBJECT + ) -> c_int; + pub fn TS_RESP_CTX_set_def_policy( + ctx: *mut TS_RESP_CTX, + def_policy: *mut ASN1_OBJECT + ) -> c_int; + } + } +} + +cfg_if! { + if #[cfg(ossl110)] { + extern "C" { + pub fn TS_RESP_CTX_set_signer_digest( + ctx: *mut TS_RESP_CTX, + signer_digest: *const EVP_MD, + ) -> c_int; + } + } +} diff --git a/openssl/src/asn1.rs b/openssl/src/asn1.rs index 03340820d..20d5b54eb 100644 --- a/openssl/src/asn1.rs +++ b/openssl/src/asn1.rs @@ -651,6 +651,18 @@ impl Asn1OctetStringRef { } } +impl PartialEq for Asn1OctetStringRef { + fn eq(&self, other: &Self) -> bool { + self.as_slice() == other.as_slice() + } +} + +impl PartialEq for Asn1OctetString { + fn eq(&self, other: &Self) -> bool { + Asn1OctetStringRef::eq(self, other) + } +} + foreign_type_and_impl_send_sync! { type CType = ffi::ASN1_OBJECT; fn drop = ffi::ASN1_OBJECT_free; diff --git a/openssl/src/lib.rs b/openssl/src/lib.rs index 555eda972..6d74569c5 100644 --- a/openssl/src/lib.rs +++ b/openssl/src/lib.rs @@ -193,6 +193,8 @@ pub mod ssl; pub mod stack; pub mod string; pub mod symm; +#[cfg(all(not(boringssl), not(libressl)))] +pub mod ts; pub mod version; pub mod x509; diff --git a/openssl/src/ts.rs b/openssl/src/ts.rs new file mode 100644 index 000000000..297529236 --- /dev/null +++ b/openssl/src/ts.rs @@ -0,0 +1,511 @@ +//! Partial interface to OpenSSL Time-Stamp Protocol (RFC 3161) implementation. +//! +//! This module provides a partial interface to OpenSSL's TSP implementation. +//! The aim is to provide enough functionality for a client to request and +//! verify timestamps returned by a Time Stamp Authority. +use bitflags::bitflags; +use ffi::{ASN1_OBJECT_free, EVP_MD_type, OBJ_nid2obj, X509_ALGOR_set0}; +use foreign_types::{ForeignType, ForeignTypeRef}; +use libc::{c_int, c_long, c_uint}; +use openssl_macros::corresponds; + +use std::convert::TryFrom; +use std::ptr; + +use crate::asn1::{Asn1IntegerRef, Asn1ObjectRef, Asn1OctetString, Asn1StringRef}; +use crate::bio::MemBioSlice; +use crate::error::ErrorStack; +use crate::hash::{Hasher, MessageDigest}; +use crate::pkey::{HasPrivate, PKeyRef}; +use crate::util::{ForeignTypeExt, ForeignTypeRefExt}; +use crate::x509::{X509Algorithm, X509AlgorithmRef, X509Ref}; +use crate::{cvt, cvt_p}; + +foreign_type_and_impl_send_sync! { + type CType = ffi::TS_MSG_IMPRINT; + fn drop = ffi::TS_MSG_IMPRINT_free; + + /// A message imprint contains the hash of the data to be timestamped. + pub struct TsMsgImprint; + + /// Reference to `TsMsgImprint`. + pub struct TsMsgImprintRef; +} + +impl PartialEq for TsMsgImprintRef { + fn eq(&self, other: &Self) -> bool { + self.get_msg() == other.get_msg() && self.get_algo() == other.get_algo() + } +} +impl PartialEq for TsMsgImprint { + fn eq(&self, other: &Self) -> bool { + TsMsgImprintRef::eq(self, other) + } +} + +impl TsMsgImprint { + /// Creates a new message imprint. + #[corresponds(TS_MSG_IMPRINT_new)] + pub fn new() -> Result { + ffi::init(); + unsafe { + let imprint = cvt_p(ffi::TS_MSG_IMPRINT_new())?; + Ok(TsMsgImprint::from_ptr(imprint)) + } + } + + /// Sets the algorithm identifier of the message digest algorithm. + #[corresponds(TS_MSG_IMPRINT_set_algo)] + pub fn set_algo(&mut self, algo: &X509AlgorithmRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::TS_MSG_IMPRINT_set_algo(self.as_ptr(), algo.as_ptr())).map(|_| ()) } + } + + /// Sets the message **digest** of the data to be timestamped. + /// It is named this way to match the name in openssl itself + #[corresponds(TS_MSG_IMPRINT_set_msg)] + pub fn set_msg(&mut self, digest: &[u8]) -> Result<(), ErrorStack> { + let len = if digest.len() > c_int::MAX as usize { + panic!("digest length is too large"); + } else { + digest.len() as c_int + }; + + unsafe { + cvt(ffi::TS_MSG_IMPRINT_set_msg( + self.as_ptr(), + digest.as_ptr() as *mut _, + len, + )) + .map(|_| ()) + } + } + + /// Creates a ready-to-use message imprint from a message and a specified hash algorithm. + pub fn from_message_with_algo(msg: &[u8], md: MessageDigest) -> Result { + let mut h = Hasher::new(md)?; + h.update(msg)?; + let hash = h.finish()?; + Self::from_prehash_with_algo(&hash, md) + } + + /// Creates a ready-to-use message imprint from the hash of a message and a specified hash algorithm. + /// + /// `hash` must have originated from the hash function specified by `md`. + pub fn from_prehash_with_algo(hash: &[u8], md: MessageDigest) -> Result { + let algo = X509Algorithm::new()?; + + unsafe { + let aobj = cvt_p(OBJ_nid2obj(EVP_MD_type(md.as_ptr())))?; + let res = X509_ALGOR_set0(algo.as_ptr(), aobj, ffi::V_ASN1_NULL, ptr::null_mut()); + + if let Err(e) = cvt(res) { + ASN1_OBJECT_free(aobj); + return Err(e); + } + }; + + let mut imprint = Self::new()?; + imprint.set_algo(&algo)?; + imprint.set_msg(hash)?; + + Ok(imprint) + } +} + +impl TsMsgImprintRef { + #[corresponds(TS_MSG_IMPRINT_get_msg)] + pub fn get_msg(&self) -> Option { + unsafe { + let octet = ffi::TS_MSG_IMPRINT_get_msg(self.as_ptr()); + Asn1OctetString::from_ptr_opt(octet) + } + } + + #[corresponds(TS_MSG_IMPRINT_get_algo)] + pub fn get_algo(&self) -> Option { + unsafe { + let algo = ffi::TS_MSG_IMPRINT_get_algo(self.as_ptr()); + X509Algorithm::from_ptr_opt(algo) + } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::TS_REQ; + fn drop = ffi::TS_REQ_free; + + /// A timestamp request. + pub struct TsReq; + + /// Reference to `TsReq`. + pub struct TsReqRef; +} + +impl TsReq { + from_der! { + /// Deserializes a DER-encoded TimeStampReq structure. + #[corresponds(d2i_TS_REQ)] + from_der, + TsReq, + ffi::d2i_TS_REQ + } +} + +impl TsReqRef { + to_der! { + /// Serializes the timestamp request into a DER-encoded TimeStampReq structure. + #[corresponds(i2d_TS_REQ)] + to_der, + ffi::i2d_TS_REQ + } +} + +impl TsReq { + /// Creates a new timestamp request. + #[corresponds(TS_REQ_new)] + pub fn new() -> Result { + unsafe { + ffi::init(); + let req: *mut ffi::TS_REQ = cvt_p(ffi::TS_REQ_new())?; + Ok(TsReq::from_ptr(req)) + } + } + + /// Set the version of the timestamp request. + /// + /// RFC 3161 requires this to be 1. + #[corresponds(TS_REQ_set_version)] + pub fn set_version(&mut self, version: c_long) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::TS_REQ_set_version(self.as_ptr(), version)).map(|_| ()) } + } + + /// Set the message imprint. + #[corresponds(TS_REQ_set_msg_imprint)] + pub fn set_msg_imprint(&mut self, imprint: &TsMsgImprintRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::TS_REQ_set_msg_imprint(self.as_ptr(), imprint.as_ptr())).map(|_| ()) } + } + + /// Get the message imprint + #[corresponds(TS_REQ_get_msg_imprint)] + pub fn get_msg_imprint(&mut self) -> Option { + unsafe { + let imprint = ffi::TS_REQ_get_msg_imprint(self.as_ptr()); + TsMsgImprint::from_ptr_opt(imprint) + } + } + + /// Sets the OID of the policy under which we're requesting the timestamp. + #[corresponds(TS_REQ_set_policy_id)] + pub fn set_policy_id(&mut self, policy: &Asn1ObjectRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::TS_REQ_set_policy_id(self.as_ptr(), policy.as_ptr())).map(|_| ()) } + } + + /// Sets the nonce. + #[corresponds(TS_REQ_set_nonce)] + pub fn set_nonce(&mut self, nonce: &Asn1IntegerRef) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::TS_REQ_set_nonce(self.as_ptr(), nonce.as_ptr())).map(|_| ()) } + } + + /// Sets whether to request the public key certificate in the response. + #[corresponds(TS_REQ_set_cert_req)] + pub fn set_cert_req(&mut self, cert_req: bool) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::TS_REQ_set_cert_req(self.as_ptr(), cert_req as c_int)).map(|_| ()) } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::TS_RESP; + fn drop = ffi::TS_RESP_free; + + /// A time-stamping response. + pub struct TsResp; + + /// Reference to `TsResp`. + pub struct TsRespRef; +} + +impl TsResp { + from_der! { + /// Deserializes a DER-encoded TimeStampResp structure. + #[corresponds(d2i_TS_RESP)] + from_der, + TsResp, + ffi::d2i_TS_RESP + } +} + +impl TsRespRef { + to_der! { + /// Serializes the timestamp request into a DER-encoded TimeStampResp structure. + #[corresponds(i2d_TS_RESP)] + to_der, + ffi::i2d_TS_RESP + } + + /// Verifies a timestamp response. + #[corresponds(TS_RESP_verify_response)] + pub fn verify(&self, context: &TsVerifyContext) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::TS_RESP_verify_response( + context.as_ptr(), + self.as_ptr(), + )) + .map(|_| ()) + } + } + + // idk what the null-ness guarantees of this function is as it's not documented in openssl + #[corresponds(TS_RESP_get_tst_info)] + pub fn get_tst(&self) -> Option<&TsTstInfoRef> { + unsafe { + let info = ffi::TS_RESP_get_tst_info(self.as_ptr()); + if info.is_null() { + None + } else { + Some(TsTstInfoRef::from_const_ptr(info)) + } + } + } +} + +bitflags! { + /// Flags controlling timestamp verification behaviour. + pub struct VerifyFlags: c_uint { + const SIGNATURE = ffi::TS_VFY_SIGNATURE; + const VERSION = ffi::TS_VFY_VERSION; + const POLICY = ffi::TS_VFY_POLICY; + const IMPRINT = ffi::TS_VFY_IMPRINT; + const DATA = ffi::TS_VFY_DATA; + const NONCE = ffi::TS_VFY_NONCE; + const SIGNER = ffi::TS_VFY_SIGNER; + const TSA_NAME = ffi::TS_VFY_TSA_NAME; + + const ALL_IMPRINT = ffi::TS_VFY_ALL_IMPRINT; + const ALL_DATA = ffi::TS_VFY_ALL_DATA; + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::TS_VERIFY_CTX; + fn drop = ffi::TS_VERIFY_CTX_free; + + /// A context object specifying time-stamping response verification parameters. + pub struct TsVerifyContext; + + /// Reference to `TsVerifyContext`. + pub struct TsVerifyContextRef; +} + +impl TsVerifyContext { + /// Construct a verify context from a timestamping request. + /// + /// Corresponds to `TS_REQ_to_TS_VERIFY_CTX`. + pub fn from_req(request: &TsReqRef) -> Result { + unsafe { + let ctx = cvt_p(ffi::TS_REQ_to_TS_VERIFY_CTX( + request.as_ptr(), + ptr::null_mut(), + ))?; + Ok(TsVerifyContext::from_ptr(ctx)) + } + } +} + +impl TryFrom<&TsReqRef> for TsVerifyContext { + type Error = ErrorStack; + + fn try_from(value: &TsReqRef) -> Result { + Self::from_req(value) + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::TS_RESP_CTX; + fn drop = ffi::TS_RESP_CTX_free; + + /// A context object used to sign timestamp requests. + pub struct TsRespContext; + + /// Reference to `TsRespContext`. + pub struct TsRespContextRef; +} + +impl TsRespContextRef { + /// Creates a signed timestamp response for the request. + #[corresponds(TS_RESP_create_response)] + pub fn create_response(&mut self, request: &TsReqRef) -> Result { + unsafe { + let der = request.to_der()?; + let bio = MemBioSlice::new(&der)?; + let response = cvt_p(ffi::TS_RESP_create_response(self.as_ptr(), bio.as_ptr()))?; + Ok(TsResp::from_ptr(response)) + } + } +} + +impl TsRespContext { + /// Creates a new response context. + #[corresponds(TS_RESP_CTX_new)] + pub fn new() -> Result { + unsafe { + ffi::init(); + let resp_context: *mut ffi::TS_RESP_CTX = cvt_p(ffi::TS_RESP_CTX_new())?; + Ok(TsRespContext::from_ptr(resp_context)) + } + } + + /// Sets the OID of the default policy used by the TSA. + #[corresponds(TS_RESP_CTX_set_def_policy)] + pub fn set_default_policy(&mut self, policy: &Asn1ObjectRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::TS_RESP_CTX_set_def_policy( + self.as_ptr(), + policy.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets the certificate the TSA uses to sign the request. + #[corresponds(TS_RESP_CTX_set_signer_cert)] + pub fn set_signer_cert(&mut self, cert: &X509Ref) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::TS_RESP_CTX_set_signer_cert( + self.as_ptr(), + cert.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets the private key the TSA uses to sign the request. + /// + /// The private key match the X.509 certificate set by `set_signer_cert`. + #[corresponds(TS_RESP_CTX_set_signer_key)] + pub fn set_signer_key(&mut self, pkey: &PKeyRef) -> Result<(), ErrorStack> + where + T: HasPrivate, + { + unsafe { + cvt(ffi::TS_RESP_CTX_set_signer_key( + self.as_ptr(), + pkey.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Sets the message digest algorithm to use for the signature. + /// + /// + /// Requires OpenSSL 1.1.0 or newer. + #[cfg(ossl110)] + #[corresponds(TS_RESP_CTX_set_signer_digest)] + pub fn set_signer_digest(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::TS_RESP_CTX_set_signer_digest( + self.as_ptr(), + md.as_ptr(), + )) + .map(|_| ()) + } + } + + /// Add an accepted message digest algorithm. + /// + /// At least one accepted digest algorithm should be added to the context. + #[corresponds(TS_RESP_CTX_add_md)] + pub fn add_md(&mut self, md: MessageDigest) -> Result<(), ErrorStack> { + unsafe { cvt(ffi::TS_RESP_CTX_add_md(self.as_ptr(), md.as_ptr())).map(|_| ()) } + } +} + +foreign_type_and_impl_send_sync! { + type CType = ffi::TS_TST_INFO; + fn drop = ffi::TS_TST_INFO_free; + pub struct TsTstInfo; + pub struct TsTstInfoRef; +} + +impl TsTstInfoRef { + // idk what the null-ness guarantees of this function is as it's not documented in openssl + #[corresponds(TS_TST_INFO_get_time)] + pub fn get_time(&self) -> Option<&Asn1StringRef> { + unsafe { + let ptr = ffi::TS_TST_INFO_get_time(self.as_ptr()); + if ptr.is_null() { + None + } else { + Some(Asn1StringRef::from_const_ptr(ptr)) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::asn1::{Asn1Integer, Asn1Object}; + use crate::bn::BigNum; + use crate::hash::MessageDigest; + use crate::pkey::PKey; + use crate::x509::X509; + + #[test] + fn test_request() { + let imprint = + TsMsgImprint::from_message_with_algo(b"BLAHBLAHBLAH\n", MessageDigest::sha512()) + .unwrap(); + + let mut request = TsReq::new().unwrap(); + request.set_version(1).unwrap(); + request.set_msg_imprint(&imprint).unwrap(); + request.set_cert_req(true).unwrap(); + let nonce = + Asn1Integer::from_bn(&BigNum::from_hex_str("F3AA393032C93DC1").unwrap()).unwrap(); + request.set_nonce(&nonce).unwrap(); + + let der = request.to_der().unwrap(); + + let request = TsReq::from_der(&der).unwrap(); + assert_eq!(request.to_der().unwrap(), der); + } + + #[test] + fn test_response_der_serialization() { + let original_der = include_bytes!("../test/ts-response.der").to_vec(); + let response = TsResp::from_der(&original_der).unwrap(); + let der = response.to_der().unwrap(); + assert_eq!(der, original_der); + } + + #[test] + fn test_verify() { + let request = TsReq::from_der(include_bytes!("../test/ts-request.der")).unwrap(); + let response = TsResp::from_der(include_bytes!("../test/ts-response.der")).unwrap(); + + let context = TsVerifyContext::from_req(&request).unwrap(); + response.verify(&context).unwrap(); + } + + #[test] + fn test_response_context() { + let mut response_context = TsRespContext::new().unwrap(); + response_context + .set_default_policy(&Asn1Object::from_str("1.2.3.4").unwrap()) + .unwrap(); + let cert = X509::from_pem(include_bytes!("../test/ts-cert.pem")).unwrap(); + response_context.set_signer_cert(&cert).unwrap(); + let key = PKey::private_key_from_pem(include_bytes!("../test/ts-key.pem")).unwrap(); + response_context.set_signer_key(&key).unwrap(); + + response_context.add_md(MessageDigest::sha512()).unwrap(); + + let request = TsReq::from_der(include_bytes!("../test/ts-request.der")).unwrap(); + let response = response_context.create_response(&request).unwrap(); + + let context = TsVerifyContext::from_req(&request).unwrap(); + response.verify(&context).unwrap(); + } +} diff --git a/openssl/src/x509/mod.rs b/openssl/src/x509/mod.rs index d0cd00e3e..13e548995 100644 --- a/openssl/src/x509/mod.rs +++ b/openssl/src/x509/mod.rs @@ -2271,6 +2271,16 @@ foreign_type_and_impl_send_sync! { pub struct X509AlgorithmRef; } +impl X509Algorithm { + pub fn new() -> Result { + ffi::init(); + unsafe { + let ptr = cvt_p(ffi::X509_ALGOR_new())?; + Ok(Self::from_ptr(ptr)) + } + } +} + impl X509AlgorithmRef { /// Returns the ASN.1 OID of this algorithm. pub fn object(&self) -> &Asn1ObjectRef { @@ -2282,6 +2292,18 @@ impl X509AlgorithmRef { } } +impl PartialEq for X509AlgorithmRef { + fn eq(&self, other: &Self) -> bool { + unsafe { ffi::X509_ALGOR_cmp(self.as_ptr(), other.as_ptr()) == 0 } + } +} + +impl PartialEq for X509Algorithm { + fn eq(&self, other: &Self) -> bool { + X509AlgorithmRef::eq(self, other) + } +} + foreign_type_and_impl_send_sync! { type CType = ffi::X509_OBJECT; fn drop = X509_OBJECT_free; diff --git a/openssl/test/ts-cert.pem b/openssl/test/ts-cert.pem new file mode 100644 index 000000000..e13bd4d21 --- /dev/null +++ b/openssl/test/ts-cert.pem @@ -0,0 +1,20 @@ +-----BEGIN CERTIFICATE----- +MIIDOjCCAiKgAwIBAgIFN8DNd/kwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMC +QVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdp +dHMgUHR5IEx0ZDAeFw0yMDEwMjcwODQwMTBaFw0zMTEwMTAwODQwMTBaMFwxCzAJ +BgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5l +dCBXaWRnaXRzIFB0eSBMdGQxFTATBgNVBAMTDFRpbWVzdGFtcGluZzCCASIwDQYJ +KoZIhvcNAQEBBQADggEPADCCAQoCggEBANUTsyhUuzou06s/XXelCLSF7sd8xtFO +4OJFEWAulg5K4m7w/GG/VIaelvqgNxdSHdzheT1l1UrMP6na2tAcAS5tBv+X0Q0C +T3+FqlqcgV2HSUNKJy3CJ1CoNQCuN6eaqO7y3O9yfdze8jCpHcrYrx4BFNISw8/T +KaAKQRHjrVbAOlIA+nCc7MGYXJS9ZVwfXNASrhRBoswLoesSf4mX1PPXwjnhGJMq +nVhVI+1G1gD9t4l7CrdOhx5vONffOoSjmqDVfRmcyYNT33V8zlynUoKwLF985TKo +hJAfA8qQDczLkPv6shssNWk8BJ+mYRz3sU9QHM4dkX1oql3oWlOx+o0CAwEAAaMa +MBgwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDQYJKoZIhvcNAQELBQADggEBAC1D +6A5FrPsYV4Q2Zar/MdYsuA2XM2j8SA/H2m4uGz8Sg52/V1VWBouB4JVz1EHytAlG +BD2A+71Bk/Y6JpxdU51O/ZgGlrOaCs+1L4+WLe1cgXGcgC65fP7eMCF3ajuOYZid +q5cJxpbBEspecus7ArqEQ9+ahAVZXcSuKfHOcW+3DxqP3/GPvbt4vLdxjPbe1Dbx +Le+UkPPIQoKNk7yOILMuZrR+O4E4O0cn7E2qodUoYIxSOWIg9euvfntFyR66NzXL ++pDtbVzWPKNiqgvhx5n5GjdyQGHA0X2gEpepT3p3S9dHMcdjJfDixVKp8F5f9mbx +6wgs4XG7rb8sRDsAmAc= +-----END CERTIFICATE----- diff --git a/openssl/test/ts-key.pem b/openssl/test/ts-key.pem new file mode 100644 index 000000000..9a3cf9d64 --- /dev/null +++ b/openssl/test/ts-key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA1ROzKFS7Oi7Tqz9dd6UItIXux3zG0U7g4kURYC6WDkribvD8 +Yb9Uhp6W+qA3F1Id3OF5PWXVSsw/qdra0BwBLm0G/5fRDQJPf4WqWpyBXYdJQ0on +LcInUKg1AK43p5qo7vLc73J93N7yMKkdytivHgEU0hLDz9MpoApBEeOtVsA6UgD6 +cJzswZhclL1lXB9c0BKuFEGizAuh6xJ/iZfU89fCOeEYkyqdWFUj7UbWAP23iXsK +t06HHm841986hKOaoNV9GZzJg1PfdXzOXKdSgrAsX3zlMqiEkB8DypANzMuQ+/qy +Gyw1aTwEn6ZhHPexT1Aczh2RfWiqXehaU7H6jQIDAQABAoIBAQDCoR76DQOELvfL +qbKfshDUjK5Ca9hTokBKjppDh+orHf3dJqTySElWOhBg1+3akHiUpSQQkC8XBqB0 +b2OFyr7NgGtvFmavAlhJfHfSErkcDJJAM8C7zGgLBcp8V6agouYCdbaXxbXwBXmm +NyPugKTcvFIfXWKdOB4CgLtVMunHnVz97+2O6HwN3nPfnsUTBNlImyO9UFO+2utB +kk8vx9qvxhVo6bRmL85+CmuPAN0XiYaXqKRyF55b37c69A1mrMsGimRCO9fxuhhW +JW7UwbXM/RHEDKRfD2P8dWMWyw+jQEBzQKrGZ/2dbjuXOVsTT6h/UVnLdFR7ntrk +HljlNsXhAoGBAPwOyAIPBe9MZPmItO4Ssw2hr62MglmXYQITT+i6uuun2LMr6X/E +zTlMNZGTOXPt8mDuPmELXX4rpKu1lBTuhM9/csbYG32kQK2I6j0QuY1fQ+NHnzcA +4e9SkctZSx0auSo4u3+8SaoRpFfbxs6+tv7xJCcmvxkJZrKnTS7Z0SOJAoGBANho +17fBqN63ogcf8y74D15KyxJi/spr7ZAZY1mH212TGf2xOC2gWpLyl+mENjj9L0+j +qRh0J7y0wcnX9ZuuZHmU647/9bM3q21ZLpoP/gbUdZEBU9CnurCk+tTlGZ49iiN0 +KqX86JuynNlou/gcNFJS1v62TCnTr+aWGH6vOmnlAoGBALbTQd+8ZeGc1+Dnd9T3 +W0iX7oVDVYkGdCa9O0jjqJElvdi4ETXL2c+lp3VgBFxCS3xjUnuxcq8BmP+zRSWp +nEuldesk9Uu8x+0XUk/Ywb35S5SfbqzGxxqAGaAVtJX3vDcTz2xndkcVZM8Vaq6r +RrDE2CRNxm6ykvsivqks9LWBAoGAfuqF0KPHyM5DPRB0y0/5m2Ab1m2uZcKEMWVi +SaiOc0OJE6pyeve3BsU1aGL8ddGuhHNEAS5l+5q6qAh6Z1IQZOl8eIIOc4urgtax +qPLGFPVW+bKgmBc2OtCWtnKh4pbOw9omBPDc7isDJ9HvoyPPX5RruDfrVQBsAbx3 +IxzbEi0CgYAXZDdGCX1GiW1qBOGzgN/omrEKuXVQ9QEQYfAy+EWYw6EfWYSzgi3d +QRV2x5q/T4IdPlrFM5fNilQGH2F9fGLX/CSJLWqnvPEKaB6e89y2ncFFZFIaP0YT +UxDNUSmAmdP+GBYZCl3cYAUtVBKrjjyHRkCy8to62ldbSV7jsngX0A== +-----END RSA PRIVATE KEY----- diff --git a/openssl/test/ts-request.der b/openssl/test/ts-request.der new file mode 100644 index 000000000..fcaf12932 Binary files /dev/null and b/openssl/test/ts-request.der differ diff --git a/openssl/test/ts-response.der b/openssl/test/ts-response.der new file mode 100644 index 000000000..771e203d4 Binary files /dev/null and b/openssl/test/ts-response.der differ diff --git a/systest/build.rs b/systest/build.rs index 56230ada6..f359077d2 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -67,7 +67,8 @@ fn main() { .header("openssl/aes.h") .header("openssl/ocsp.h") .header("openssl/evp.h") - .header("openssl/x509_vfy.h"); + .header("openssl/x509_vfy.h") + .header("openssl/ts.h"); if let Some(version) = libressl_version { cfg.header("openssl/poly1305.h");