#![allow(clippy::unwrap_used)]
use rasn_ocsp::{BasicOcspResponse, OcspResponse};
#[cfg(feature = "openssl")]
pub(crate) use responder::TestResponder;
const GOOD_RESPONSE: &[u8] =
include_bytes!("../../../tests/fixtures/crypto/ocsp/response_good.der");
pub(crate) fn good_basic_response() -> BasicOcspResponse {
let response: OcspResponse = rasn::der::decode(GOOD_RESPONSE).unwrap();
rasn::der::decode(&response.bytes.unwrap().response).unwrap()
}
pub(crate) fn encode_response(basic: &BasicOcspResponse) -> Vec<u8> {
let mut response: OcspResponse = rasn::der::decode(GOOD_RESPONSE).unwrap();
response.bytes.as_mut().unwrap().response = rasn::der::encode(basic).unwrap().into();
rasn::der::encode(&response).unwrap()
}
#[cfg(feature = "openssl")]
mod responder {
use openssl::{
asn1::Asn1Time,
bn::{BigNum, MsbOption},
ec::{EcGroup, EcKey},
hash::MessageDigest,
nid::Nid,
pkey::{PKey, Private},
sign::Signer,
x509::{
extension::{
AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage,
SubjectKeyIdentifier,
},
X509Builder, X509Name, X509NameBuilder, X509,
},
};
use rasn_ocsp::BasicOcspResponse;
use super::encode_response;
const NOT_BEFORE_UNIX: i64 = 1_577_836_800;
pub(crate) struct TestResponder {
key: PKey<Private>,
cert: X509,
}
fn generate_key() -> PKey<Private> {
let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap();
PKey::from_ec_key(EcKey::generate(&group).unwrap()).unwrap()
}
fn name(common_name: &str) -> X509Name {
let mut name = X509NameBuilder::new().unwrap();
name.append_entry_by_nid(Nid::COMMONNAME, common_name)
.unwrap();
name.build()
}
fn cert_builder(subject: &X509Name, issuer: &X509Name, key: &PKey<Private>) -> X509Builder {
let mut serial = BigNum::new().unwrap();
serial.rand(64, MsbOption::MAYBE_ZERO, false).unwrap();
let mut builder = X509::builder().unwrap();
builder.set_version(2).unwrap();
builder
.set_serial_number(&serial.to_asn1_integer().unwrap())
.unwrap();
builder.set_subject_name(subject).unwrap();
builder.set_issuer_name(issuer).unwrap();
builder.set_pubkey(key).unwrap();
builder
.set_not_before(&Asn1Time::from_unix(NOT_BEFORE_UNIX as _).unwrap())
.unwrap();
builder
.set_not_after(&Asn1Time::days_from_now(30).unwrap())
.unwrap();
builder
}
impl TestResponder {
pub(crate) fn new() -> Self {
let ca_key = generate_key();
let ca_name = name("C2PA Test OCSP CA");
let mut ca = cert_builder(&ca_name, &ca_name, &ca_key);
ca.append_extension(BasicConstraints::new().critical().ca().build().unwrap())
.unwrap();
ca.append_extension(KeyUsage::new().critical().key_cert_sign().build().unwrap())
.unwrap();
let ski = SubjectKeyIdentifier::new()
.build(&ca.x509v3_context(None, None))
.unwrap();
ca.append_extension(ski).unwrap();
ca.sign(&ca_key, MessageDigest::sha256()).unwrap();
let ca = ca.build();
let key = generate_key();
let mut builder = cert_builder(&name("C2PA Test OCSP Responder"), &ca_name, &key);
builder
.append_extension(BasicConstraints::new().critical().build().unwrap())
.unwrap();
builder
.append_extension(
KeyUsage::new()
.critical()
.digital_signature()
.build()
.unwrap(),
)
.unwrap();
builder
.append_extension(
ExtendedKeyUsage::new()
.critical()
.other("1.3.6.1.5.5.7.3.9")
.build()
.unwrap(),
)
.unwrap();
let aki = AuthorityKeyIdentifier::new()
.keyid(false)
.build(&builder.x509v3_context(Some(&ca), None))
.unwrap();
builder.append_extension(aki).unwrap();
builder.sign(&ca_key, MessageDigest::sha256()).unwrap();
Self {
key,
cert: builder.build(),
}
}
pub(crate) fn cert_pem(&self) -> Vec<u8> {
self.cert.to_pem().unwrap()
}
pub(crate) fn sign(&self, mut basic: BasicOcspResponse) -> Vec<u8> {
basic.certs = Some(vec![
rasn::der::decode(&self.cert.to_der().unwrap()).unwrap()
]);
basic.signature_algorithm.algorithm = rasn::types::ObjectIdentifier::new_unchecked(
vec![1, 2, 840, 10045, 4, 3, 2].into(),
);
basic.signature_algorithm.parameters = None;
let mut signer = Signer::new(MessageDigest::sha256(), &self.key).unwrap();
signer
.update(&rasn::der::encode(&basic.tbs_response_data).unwrap())
.unwrap();
basic.signature = rasn::types::BitString::from_vec(signer.sign_to_vec().unwrap());
encode_response(&basic)
}
}
}