Skip to content

Commit

Permalink
finalizing version 1 of the builder
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Hession committed Nov 17, 2023
1 parent e9d9e42 commit ae62ab6
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 272 deletions.
3 changes: 2 additions & 1 deletion x509-ocsp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ x509-cert = { version = "0.2.4", default-features = false }
# Optional
digest = { version = "0.10.7", optional = true, default-features = false, features = ["oid"] }
rand_core = { version = "0.6.4", optional = true, default-features = false }
signature = { version = "2.1.0", optional = true, default-features = false, features = ["digest"] }
signature = { version = "2.1.0", optional = true, default-features = false, features = ["digest", "rand_core"] }

[dev-dependencies]
hex-literal = "0.4.1"
Expand All @@ -34,6 +34,7 @@ sha1 = { version = "0.10.6", default-features = false, features = ["oid"] }
sha2 = { version = "0.10.8", default-features = false, features = ["oid"] }

[features]
rand = ["rand_core"]
builder = ["digest", "rand_core", "signature"]
std = ["der/std", "x509-cert/std"]

Expand Down
2 changes: 1 addition & 1 deletion x509-ocsp/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod request;
mod response;

pub use self::request::OcspRequestBuilder;
pub use self::response::BasicOcspResponseBuilder;
pub use self::response::OcspResponseBuilder;

/// Error type
#[derive(Debug)]
Expand Down
76 changes: 45 additions & 31 deletions x509-ocsp/src/builder/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
use crate::{builder::Error, OcspRequest, Request, Signature, TbsRequest, Version};
use alloc::vec::Vec;
use der::{asn1::BitString, Encode};
use signature::{SignatureEncoding, Signer};
use spki::DynSignatureAlgorithmIdentifier;
use der::Encode;
use rand_core::CryptoRngCore;
use signature::{RandomizedSigner, Signer};
use spki::{DynSignatureAlgorithmIdentifier, SignatureBitStringEncoding};
use x509_cert::{
ext::{pkix::name::GeneralName, AsExtension, Extensions},
ext::{pkix::name::GeneralName, AsExtension},
name::Name,
Certificate,
};
Expand Down Expand Up @@ -58,36 +59,35 @@ use x509_cert::{
/// ```
#[derive(Clone, Debug, Default)]
pub struct OcspRequestBuilder {
version: Version,
requestor_name: Option<GeneralName>,
request_list: Vec<Request>,
request_extensions: Option<Extensions>,
tbs: TbsRequest,
}

impl OcspRequestBuilder {
/// Returns an `OcspRequestBuilder` with the specified [`Version`]
pub fn new(version: Version) -> Self {
Self {
version,
requestor_name: None,
request_list: Vec::new(),
request_extensions: None,
tbs: TbsRequest {
version,
requestor_name: None,
request_list: Vec::new(),
request_extensions: None,
},
}
}

/// Sets the requestor name as specified in [RFC 6960 Section 4.1.1]
///
/// [RFC 6960 Section 4.1.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1
pub fn with_requestor_name(mut self, requestor_name: GeneralName) -> Self {
self.requestor_name = Some(requestor_name);
self.tbs.requestor_name = Some(requestor_name);
self
}

/// Adds a [`Request`] to the builder as defined in [RFC 6960 Section 4.1.1].
///
/// [RFC 6960 Section 4.1.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1
pub fn with_request(mut self, request: Request) -> Self {
self.request_list.push(request);
self.tbs.request_list.push(request);
self
}

Expand All @@ -97,17 +97,17 @@ impl OcspRequestBuilder {
/// [RFC 6960 Section 4.4]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.4
pub fn with_extension(mut self, ext: impl AsExtension) -> Result<Self, Error> {
let ext = ext.to_extension(&Name::default(), &[])?;
match self.request_extensions {
match self.tbs.request_extensions {
Some(ref mut exts) => exts.push(ext),
None => self.request_extensions = Some(alloc::vec![ext]),
None => self.tbs.request_extensions = Some(alloc::vec![ext]),
}
Ok(self)
}

/// Consumes the builder and returns an [`OcspRequest`]
pub fn build(self) -> OcspRequest {
OcspRequest {
tbs_request: self.into_tbs_request(),
tbs_request: self.tbs,
optional_signature: None,
}
}
Expand All @@ -121,31 +121,45 @@ impl OcspRequestBuilder {
) -> Result<OcspRequest, Error>
where
S: Signer<Sig> + DynSignatureAlgorithmIdentifier,
Sig: SignatureEncoding,
Sig: SignatureBitStringEncoding,
{
let tbs_request = self.into_tbs_request();
let signature_algorithm = signer.signature_algorithm_identifier()?;
let signature = signer.try_sign(&tbs_request.to_der()?)?;
let signature = BitString::from_bytes(signature.to_bytes().as_ref())?;
let signature = signer.try_sign(&self.tbs.to_der()?)?.to_bitstring()?;
let optional_signature = Some(Signature {
signature_algorithm,
signature,
certs: certificate_chain,
});
Ok(OcspRequest {
tbs_request,
tbs_request: self.tbs,
optional_signature,
})
}

/// Consumes the builder and returns a [`TbsRequest`]
fn into_tbs_request(self) -> TbsRequest {
// Maybe verify extensions before building?
TbsRequest {
version: self.version,
requestor_name: self.requestor_name,
request_list: self.request_list,
request_extensions: self.request_extensions,
}
/// Consumes the builder and returns a signed [`OcspRequest`]. Errors when the algorithm
/// identifier encoding, message encoding, or signature generation fails.
pub fn sign_with_rng<S, Sig>(
self,
signer: &mut S,
rng: &mut impl CryptoRngCore,
certificate_chain: Option<Vec<Certificate>>,
) -> Result<OcspRequest, Error>
where
S: RandomizedSigner<Sig> + DynSignatureAlgorithmIdentifier,
Sig: SignatureBitStringEncoding,
{
let signature_algorithm = signer.signature_algorithm_identifier()?;
let signature = signer
.try_sign_with_rng(rng, &self.tbs.to_der()?)?
.to_bitstring()?;
let optional_signature = Some(Signature {
signature_algorithm,
signature,
certs: certificate_chain,
});
Ok(OcspRequest {
tbs_request: self.tbs,
optional_signature,
})
}
}
126 changes: 78 additions & 48 deletions x509-ocsp/src/builder/response.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
//! OCSP response builder
use crate::{
builder::Error, BasicOcspResponse, OcspGeneralizedTime, ResponderId, ResponseData,
SingleResponse, Version,
builder::Error, BasicOcspResponse, OcspGeneralizedTime, OcspResponse, ResponderId,
ResponseData, SingleResponse, Version,
};
use alloc::vec::Vec;
use der::{asn1::BitString, Encode};
use signature::{SignatureEncoding, Signer};
use spki::DynSignatureAlgorithmIdentifier;
use der::Encode;
use rand_core::CryptoRngCore;
use signature::{RandomizedSigner, Signer};
use spki::{DynSignatureAlgorithmIdentifier, SignatureBitStringEncoding};
use x509_cert::{
ext::{AsExtension, Extensions},
name::Name,
Certificate,
};

/// X509 Basic OCSP Response builder
/// X509 OCSP Response builder
///
/// ```
/// use der::{asn1::ObjectIdentifier, DateTime, Decode};
/// use x509_cert::Certificate;
/// use x509_ocsp::builder::BasicOcspResponseBuilder;
/// use x509_ocsp::builder::OcspResponseBuilder;
/// use x509_ocsp::{ext::Nonce, CertStatus, OcspGeneralizedTime, OcspRequest, OcspResponse,
/// SingleResponse, Version,
/// SingleResponse,
/// };
///
/// # const OCSP_REQ_DER: &[u8] = include_bytes!(
Expand All @@ -39,20 +40,17 @@ use x509_cert::{
/// let req = OcspRequest::from_der(OCSP_REQ_DER).unwrap();
/// let ca = Certificate::from_der(CA_DER).unwrap();
///
/// let mut builder = BasicOcspResponseBuilder::new(
/// Version::V1,
/// ca.tbs_certificate.subject.clone(),
/// )
/// .with_single_response(
/// SingleResponse::new(
/// req.tbs_request.request_list[0].req_cert.clone(),
/// CertStatus::good(),
/// OcspGeneralizedTime::from(DateTime::new(2023, 10, 31, 0, 0, 0).unwrap()),
/// )
/// .with_next_update(OcspGeneralizedTime::from(
/// DateTime::new(2024, 1, 1, 0, 0, 0).unwrap()
/// )),
/// );
/// let mut builder = OcspResponseBuilder::new(ca.tbs_certificate.subject.clone())
/// .with_single_response(
/// SingleResponse::new(
/// req.tbs_request.request_list[0].req_cert.clone(),
/// CertStatus::good(),
/// OcspGeneralizedTime::from(DateTime::new(2023, 10, 31, 0, 0, 0).unwrap()),
/// )
/// .with_next_update(OcspGeneralizedTime::from(
/// DateTime::new(2024, 1, 1, 0, 0, 0).unwrap()
/// )),
/// );
///
/// if let Some(nonce) = req.nonce() {
/// builder = builder.with_extension(nonce).unwrap();
Expand All @@ -68,28 +66,23 @@ use x509_cert::{
///
/// let mut signer = rsa_signer();
/// let signer_cert_chain = vec![ca.clone()];
/// let resp = OcspResponse::successful(
/// builder
/// .sign(&mut signer, Some(signer_cert_chain), now)
/// .unwrap(),
/// )
/// .unwrap();
/// let resp = builder
/// .sign(&mut signer, Some(signer_cert_chain), now)
/// .unwrap();
/// ```
#[derive(Clone, Debug)]
pub struct BasicOcspResponseBuilder {
version: Version,
pub struct OcspResponseBuilder {
responder_id: ResponderId,
responses: Vec<SingleResponse>,
response_extensions: Option<Extensions>,
}

impl BasicOcspResponseBuilder {
/// Returns a `BasicOcspResponseBuilder` given the [`Version`], [`ResponderId`], and `Produced
impl OcspResponseBuilder {
/// Returns a `OcspResponseBuilder` given the [`Version`], [`ResponderId`], and `Produced
/// At` values.
pub fn new(version: Version, responder_id: impl Into<ResponderId>) -> Self {
pub fn new(responder_id: impl Into<ResponderId>) -> Self {
let responder_id = responder_id.into();
Self {
version,
responder_id,
responses: Vec::new(),
response_extensions: None,
Expand Down Expand Up @@ -117,7 +110,18 @@ impl BasicOcspResponseBuilder {
Ok(self)
}

/// Consumes the builder and returns a signed [`BasicOcspResponse`]. Errors when the algorithm
/// Consume the builder and returns [`ResponseData`]
fn into_response_data(self, produced_at: OcspGeneralizedTime) -> ResponseData {
ResponseData {
version: Version::default(),
responder_id: self.responder_id,
produced_at,
responses: self.responses,
response_extensions: self.response_extensions,
}
}

/// Consumes the builder and returns a signed [`OcspResponse`]. Errors when the algorithm
/// identifier encoding, message encoding, or signature generation fails.
///
/// Per [RFC 6960 Section 2.4], the `producedAt` value must be the time the request was
Expand All @@ -129,26 +133,52 @@ impl BasicOcspResponseBuilder {
signer: &mut S,
certificate_chain: Option<Vec<Certificate>>,
produced_at: OcspGeneralizedTime,
) -> Result<BasicOcspResponse, Error>
) -> Result<OcspResponse, Error>
where
S: Signer<Sig> + DynSignatureAlgorithmIdentifier,
Sig: SignatureEncoding,
Sig: SignatureBitStringEncoding,
{
let tbs_response_data = ResponseData {
version: self.version,
responder_id: self.responder_id,
produced_at,
responses: self.responses,
response_extensions: self.response_extensions,
};
let tbs_response_data = self.into_response_data(produced_at);
let signature_algorithm = signer.signature_algorithm_identifier()?;
let signature = signer
.try_sign(&tbs_response_data.to_der()?)?
.to_bitstring()?;
Ok(OcspResponse::successful(BasicOcspResponse {
tbs_response_data,
signature_algorithm,
signature,
certs: certificate_chain,
})?)
}

/// Consumes the builder and returns a signed [`OcspResponse`]. Errors when the algorithm
/// identifier encoding, message encoding, or signature generation fails.
///
/// Per [RFC 6960 Section 2.4], the `producedAt` value must be the time the request was
/// signed.
///
/// [RFC 6960 Section 2.4]: https://datatracker.ietf.org/doc/html/rfc6960#section-2.4
pub fn sign_with_rng<S, Sig>(
self,
signer: &mut S,
rng: &mut impl CryptoRngCore,
certificate_chain: Option<Vec<Certificate>>,
produced_at: OcspGeneralizedTime,
) -> Result<OcspResponse, Error>
where
S: RandomizedSigner<Sig> + DynSignatureAlgorithmIdentifier,
Sig: SignatureBitStringEncoding,
{
let tbs_response_data = self.into_response_data(produced_at);
let signature_algorithm = signer.signature_algorithm_identifier()?;
let signature = signer.try_sign(&tbs_response_data.to_der()?)?;
let signature = BitString::from_bytes(signature.to_bytes().as_ref())?;
Ok(BasicOcspResponse {
let signature = signer
.try_sign_with_rng(rng, &tbs_response_data.to_der()?)?
.to_bitstring()?;
Ok(OcspResponse::successful(BasicOcspResponse {
tbs_response_data,
signature_algorithm,
signature,
certs: certificate_chain,
})
})?)
}
}
4 changes: 2 additions & 2 deletions x509-ocsp/src/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use x509_cert::{
name::Name,
};

#[cfg(feature = "rand_core")]
#[cfg(feature = "rand")]
use rand_core::CryptoRngCore;

// x509-cert's is not exported
Expand Down Expand Up @@ -61,7 +61,7 @@ impl Nonce {
/// ```text
/// Nonce ::= OCTET STRING(SIZE(1..32))
/// ```
#[cfg(feature = "rand_core")]
#[cfg(feature = "rand")]
pub fn generate<R>(rng: &mut R, length: usize) -> Result<Self, der::Error>
where
R: CryptoRngCore,
Expand Down
2 changes: 1 addition & 1 deletion x509-ocsp/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl OcspRequest {
/// ```
///
/// [RFC 6960 Section 4.1.1]: https://datatracker.ietf.org/doc/html/rfc6960#section-4.1.1
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
#[derive(Clone, Debug, Default, Eq, PartialEq, Sequence)]
#[allow(missing_docs)]
pub struct TbsRequest {
#[asn1(
Expand Down
Loading

0 comments on commit ae62ab6

Please sign in to comment.