diff --git a/openssl-sys/src/handwritten/ocsp.rs b/openssl-sys/src/handwritten/ocsp.rs index c194a831b9..1df00372e6 100644 --- a/openssl-sys/src/handwritten/ocsp.rs +++ b/openssl-sys/src/handwritten/ocsp.rs @@ -21,6 +21,7 @@ const_ptr_api! { extern "C" { pub fn OCSP_request_add0_id(r: *mut OCSP_REQUEST, id: *mut OCSP_CERTID) -> *mut OCSP_ONEREQ; + pub fn OCSP_request_add1_nonce(req: *mut OCSP_REQUEST, val: *mut c_uchar, len: c_int) -> c_int; pub fn OCSP_resp_find_status( bs: *mut OCSP_BASICRESP, @@ -47,6 +48,10 @@ extern "C" { pub fn OCSP_BASICRESP_free(r: *mut OCSP_BASICRESP); pub fn OCSP_RESPONSE_new() -> *mut OCSP_RESPONSE; pub fn OCSP_RESPONSE_free(r: *mut OCSP_RESPONSE); + + pub fn OCSP_basic_add1_nonce(resp: *mut OCSP_BASICRESP, val: *mut c_uchar, len: c_int) + -> c_int; + pub fn OCSP_copy_nonce(resp: *mut OCSP_BASICRESP, req: *mut OCSP_REQUEST) -> c_int; } const_ptr_api! { @@ -86,4 +91,6 @@ extern "C" { st: *mut X509_STORE, flags: c_ulong, ) -> c_int; + + pub fn OCSP_check_nonce(req: *mut OCSP_REQUEST, resp: *mut OCSP_BASICRESP) -> c_int; } diff --git a/openssl/src/ocsp.rs b/openssl/src/ocsp.rs index 93a5d36b7e..28d1d73136 100644 --- a/openssl/src/ocsp.rs +++ b/openssl/src/ocsp.rs @@ -109,6 +109,26 @@ impl OcspRevokedStatus { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct OcspCheckNonceStatus(c_int); + +impl OcspCheckNonceStatus { + pub const REQUEST_ONLY: OcspCheckNonceStatus = OcspCheckNonceStatus(-1); + pub const NOT_EQUAL: OcspCheckNonceStatus = OcspCheckNonceStatus(0); + pub const EQUAL: OcspCheckNonceStatus = OcspCheckNonceStatus(1); + pub const ABSENT: OcspCheckNonceStatus = OcspCheckNonceStatus(2); + pub const RESPONSE_ONLY: OcspCheckNonceStatus = OcspCheckNonceStatus(3); + + pub fn from_raw(raw: c_int) -> OcspResponseStatus { + OcspResponseStatus(raw) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + pub fn as_raw(&self) -> c_int { + self.0 + } +} + pub struct OcspStatus<'a> { /// The overall status of the response. pub status: OcspCertStatus, @@ -209,6 +229,30 @@ impl OcspBasicResponseRef { } } } + + /// Add a nonce value to the response. + /// + /// If `val` is `None`, a random nonce is used. + #[corresponds(OCSP_basic_add1_nonce)] + pub fn add_nonce(&mut self, val: Option<&[u8]>) -> Result<(), ErrorStack> { + unsafe { + let (ptr, len) = match val { + Some(slice) => (slice.as_ptr() as *mut _, slice.len() as c_int), + None => (ptr::null_mut(), 0), + }; + cvt(ffi::OCSP_basic_add1_nonce(self.as_ptr(), ptr, len))?; + Ok(()) + } + } + + /// Copy the nonce value from `req` to the response. + #[corresponds(OCSP_copy_nonce)] + pub fn copy_nonce(&mut self, req: &OcspRequestRef) -> Result<(), ErrorStack> { + unsafe { + cvt(ffi::OCSP_copy_nonce(self.as_ptr(), req.as_ptr()))?; + Ok(()) + } + } } foreign_type_and_impl_send_sync! { @@ -341,6 +385,21 @@ impl OcspRequestRef { Ok(OcspOneReqRef::from_ptr_mut(ptr)) } } + + /// Add a nonce value to the request. + /// + /// If `val` is `None`, a random nonce is used. + #[corresponds(OCSP_request_add1_nonce)] + pub fn add_nonce(&mut self, val: Option<&[u8]>) -> Result<(), ErrorStack> { + unsafe { + let (ptr, len) = match val { + Some(slice) => (slice.as_ptr() as *mut _, slice.len() as c_int), + None => (ptr::null_mut(), 0), + }; + cvt(ffi::OCSP_request_add1_nonce(self.as_ptr(), ptr, len))?; + Ok(()) + } + } } foreign_type_and_impl_send_sync! { @@ -350,3 +409,120 @@ foreign_type_and_impl_send_sync! { pub struct OcspOneReq; pub struct OcspOneReqRef; } + +/// Compares the nonce value in `req` and `resp`. +#[corresponds(OCSP_check_nonce)] +pub fn check_nonce(req: &OcspRequestRef, resp: &OcspBasicResponseRef) -> OcspCheckNonceStatus { + unsafe { + let r = ffi::OCSP_check_nonce(req.as_ptr(), resp.as_ptr()); + OcspCheckNonceStatus(r) + } +} + +#[cfg(test)] +mod tests { + use hex::FromHex; + + use super::*; + + const TEST_NONCE_HEX_1: &str = "2AE3D741A4112D3A0FD345FE89134AD1"; // nonce in test request + const TEST_NONCE_HEX_2: &str = "5E18AA1A113648F054A2F5A396F1636F"; + + #[test] + fn test_ocsp_create_request_with_nonce() { + let req_der = include_bytes!("../test/ocsp_req.der"); + let req_nonce_der = include_bytes!("../test/ocsp_req_nonce.der"); + let mut req = OcspRequest::from_der(req_der.as_slice()).unwrap(); + + assert_hex_eq(req.to_der().unwrap(), req_der); + + let nonce = Vec::from_hex(TEST_NONCE_HEX_1).unwrap(); + req.add_nonce(Some(&nonce)).unwrap(); + + assert_hex_eq(req.to_der().unwrap(), req_nonce_der); + } + + #[test] + fn test_ocsp_check_nonce() { + let req_der = include_bytes!("../test/ocsp_req.der"); + let resp_der = include_bytes!("../test/ocsp_resp.der"); + let mut req = OcspRequest::from_der(req_der.as_slice()).unwrap(); + let mut resp = OcspResponse::from_der(resp_der.as_slice()) + .unwrap() + .basic() + .unwrap(); + let nonce1 = Vec::from_hex(TEST_NONCE_HEX_1).unwrap(); + let nonce2 = Vec::from_hex(TEST_NONCE_HEX_2).unwrap(); + + assert_eq!( + check_nonce(req.as_ref(), resp.as_ref()), + OcspCheckNonceStatus::ABSENT + ); + + req.add_nonce(Some(&nonce1)).unwrap(); + assert_eq!( + check_nonce(req.as_ref(), resp.as_ref()), + OcspCheckNonceStatus::REQUEST_ONLY + ); + + resp.add_nonce(Some(&nonce1)).unwrap(); + assert_eq!( + check_nonce(req.as_ref(), resp.as_ref()), + OcspCheckNonceStatus::EQUAL + ); + + resp.add_nonce(Some(&nonce2)).unwrap(); + assert_eq!( + check_nonce(req.as_ref(), resp.as_ref()), + OcspCheckNonceStatus::NOT_EQUAL + ); + } + + #[test] + fn test_ocsp_copy_nonce() { + let req_der = include_bytes!("../test/ocsp_req_nonce.der"); + let resp_der = include_bytes!("../test/ocsp_resp.der"); + let req = OcspRequest::from_der(req_der.as_slice()).unwrap(); + let mut resp = OcspResponse::from_der(resp_der.as_slice()) + .unwrap() + .basic() + .unwrap(); + + assert_eq!( + check_nonce(req.as_ref(), resp.as_ref()), + OcspCheckNonceStatus::REQUEST_ONLY + ); + + resp.copy_nonce(req.as_ref()).unwrap(); + assert_eq!( + check_nonce(req.as_ref(), resp.as_ref()), + OcspCheckNonceStatus::EQUAL + ); + } + + #[test] + fn test_ocsp_copy_no_nonce() { + let req_der = include_bytes!("../test/ocsp_req.der"); + let resp_der = include_bytes!("../test/ocsp_resp.der"); + let req = OcspRequest::from_der(req_der.as_slice()).unwrap(); + let mut resp = OcspResponse::from_der(resp_der.as_slice()) + .unwrap() + .basic() + .unwrap(); + + assert_eq!( + check_nonce(req.as_ref(), resp.as_ref()), + OcspCheckNonceStatus::ABSENT + ); + + resp.copy_nonce(req.as_ref()).unwrap(); + assert_eq!( + check_nonce(req.as_ref(), resp.as_ref()), + OcspCheckNonceStatus::ABSENT + ); + } + + fn assert_hex_eq(left: impl AsRef<[u8]>, right: impl AsRef<[u8]>) { + assert_eq!(hex::encode(left.as_ref()), hex::encode(right.as_ref())) + } +} diff --git a/openssl/test/ocsp_req.der b/openssl/test/ocsp_req.der new file mode 100644 index 0000000000..82b5c87cd7 Binary files /dev/null and b/openssl/test/ocsp_req.der differ diff --git a/openssl/test/ocsp_req_nonce.der b/openssl/test/ocsp_req_nonce.der new file mode 100644 index 0000000000..b44c012f83 Binary files /dev/null and b/openssl/test/ocsp_req_nonce.der differ diff --git a/openssl/test/ocsp_resp.der b/openssl/test/ocsp_resp.der new file mode 100644 index 0000000000..36f595cde8 Binary files /dev/null and b/openssl/test/ocsp_resp.der differ