use async_generic::async_generic;
use bcder::{decode::Constructed, encode::Values};
use http::header;
use crate::{
crypto::{
asn1::rfc3161::{TimeStampReq, TimeStampResp},
cose::CertificateTrustPolicy,
time_stamp::{
response::TimeStampResponse,
verify::{verify_time_stamp, verify_time_stamp_async},
TimeStampError,
},
},
http::{AsyncHttpResolver, HttpResolverError, SyncHttpResolver},
status_tracker::StatusTracker,
};
#[async_generic(async_signature(
url: &str,
headers: Option<Vec<(String, String)>>,
data: &[u8],
message: &[u8],
http_resolver: &(impl AsyncHttpResolver + ?Sized),
))]
pub fn default_rfc3161_request(
url: &str,
headers: Option<Vec<(String, String)>>,
data: &[u8],
message: &[u8],
http_resolver: &(impl SyncHttpResolver + ?Sized),
) -> Result<Vec<u8>, TimeStampError> {
let request = Constructed::decode(
bcder::decode::SliceSource::new(data),
bcder::Mode::Der,
TimeStampReq::take_from,
)
.map_err(|_err| {
TimeStampError::InternalError("failure to decode Constructed TimeStampReq".to_string())
})?;
let ts = if _sync {
time_stamp_request_http(url, headers, &request, http_resolver)?
} else {
time_stamp_request_http_async(url, headers, &request, http_resolver).await?
};
let mut local_log = StatusTracker::default();
let ctp = CertificateTrustPolicy::passthrough();
if _sync {
verify_time_stamp(&ts, message, &ctp, &mut local_log, false)?;
} else {
verify_time_stamp_async(&ts, message, &ctp, &mut local_log, false).await?;
}
Ok(ts)
}
#[async_generic(async_signature(
url: &str,
headers: Option<Vec<(String, String)>>,
timestamp_request: &TimeStampReq,
http_resolver: &(impl AsyncHttpResolver + ?Sized),
))]
fn time_stamp_request_http(
url: &str,
headers: Option<Vec<(String, String)>>,
timestamp_request: &TimeStampReq,
http_resolver: &(impl SyncHttpResolver + ?Sized),
) -> Result<Vec<u8>, TimeStampError> {
use std::io::Read;
const HTTP_CONTENT_TYPE_REQUEST: &str = "application/timestamp-query";
const HTTP_CONTENT_TYPE_RESPONSE: &str = "application/timestamp-reply";
let mut body = Vec::<u8>::new();
timestamp_request
.encode_ref()
.write_encoded(bcder::Mode::Der, &mut body)?;
let mut request = http::Request::post(url);
if let Some(headers) = headers {
for (ref name, ref value) in headers {
request = request.header(name.as_str(), value.as_str());
}
}
let request = request.header(header::CONTENT_TYPE, HTTP_CONTENT_TYPE_REQUEST);
let response = if _sync {
http_resolver.http_resolve(request.body(body).map_err(HttpResolverError::Http)?)?
} else {
http_resolver
.http_resolve_async(request.body(body).map_err(HttpResolverError::Http)?)
.await?
};
let content_type = response
.headers()
.get(header::CONTENT_TYPE)
.and_then(|header| header.to_str().ok());
if response.status() == 200 && content_type == Some(HTTP_CONTENT_TYPE_RESPONSE) {
let len = response
.headers()
.get(header::CONTENT_LENGTH)
.and_then(|content_length| content_length.to_str().ok())
.and_then(|content_length| content_length.parse().ok())
.unwrap_or(20000);
let mut response_bytes: Vec<u8> = Vec::with_capacity(len);
response
.into_body()
.take(1000000)
.read_to_end(&mut response_bytes)?;
let res = TimeStampResponse(
Constructed::decode(response_bytes.as_ref(), bcder::Mode::Der, |cons| {
TimeStampResp::take_from(cons)
})
.map_err(|e| TimeStampError::DecodeError(e.to_string()))?,
);
if res.is_success() {
if let Some(tst_info) = res.tst_info()? {
if tst_info.nonce != timestamp_request.nonce {
return Err(TimeStampError::NonceMismatch);
}
}
}
Ok(response_bytes)
} else {
Err(TimeStampError::HttpErrorResponse(
response.status().as_u16(),
content_type.map(|content_type| content_type.to_owned()),
))
}
}