#![deny(missing_docs)]
use async_generic::async_generic;
use async_trait::async_trait;
use crate::{
claim::Claim,
cose_validator::verify_cose,
crypto::{
cose::{
check_end_entity_certificate_profile, sign, sign_async, CertificateTrustPolicy,
TimeStampStorage,
},
raw_signature::{AsyncRawSigner, RawSigner, RawSignerError, SigningAlg},
time_stamp::{AsyncTimeStampProvider, TimeStampError, TimeStampProvider},
},
settings::Settings,
status_tracker::{ErrorBehavior, StatusTracker},
AsyncSigner, Error, Result, Signer,
};
#[async_generic(async_signature(
claim_bytes: &[u8],
signer: &dyn AsyncSigner,
box_size: usize,
settings: &Settings,
))]
pub fn sign_claim(
claim_bytes: &[u8],
signer: &dyn Signer,
box_size: usize,
settings: &Settings,
) -> Result<Vec<u8>> {
let label = "dummy_label";
let claim = Claim::from_data(label, claim_bytes)?;
let tss = if claim.version() > 1 {
TimeStampStorage::V2_sigTst2_CTT
} else {
TimeStampStorage::V1_sigTst
};
let signed_bytes = if _sync {
cose_sign(signer, claim_bytes, box_size, tss, settings)
} else {
cose_sign_async(signer, claim_bytes, box_size, tss, settings).await
};
match signed_bytes {
Ok(signed_bytes) => {
let mut cose_log = StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError);
let passthrough_cap = CertificateTrustPolicy::default();
match verify_cose(
&signed_bytes,
claim_bytes,
b"",
true,
&passthrough_cap,
None,
&mut cose_log,
settings,
) {
Ok(r) => {
if !r.validated {
Err(Error::CoseSignature)
} else {
Ok(signed_bytes)
}
}
Err(err) => Err(err),
}
}
Err(err) => Err(err),
}
}
#[async_generic(async_signature(
signer: &dyn AsyncSigner,
data: &[u8],
box_size: usize,
time_stamp_storage: TimeStampStorage,
settings: &Settings,
))]
pub(crate) fn cose_sign(
signer: &dyn Signer,
data: &[u8],
box_size: usize,
time_stamp_storage: TimeStampStorage,
settings: &Settings,
) -> Result<Vec<u8>> {
let certs = signer.certs()?;
if let Some(signing_cert) = certs.first() {
signing_cert_valid(signing_cert, settings)?;
} else {
return Err(Error::CoseNoCerts);
}
if _sync {
match signer.raw_signer() {
Some(raw_signer) => Ok(sign(*raw_signer, data, Some(box_size), time_stamp_storage)?),
None => {
let wrapper = SignerWrapper(signer);
Ok(sign(&wrapper, data, Some(box_size), time_stamp_storage)?)
}
}
} else {
match signer.async_raw_signer() {
Some(raw_signer) => {
Ok(sign_async(*raw_signer, data, Some(box_size), time_stamp_storage).await?)
}
None => {
let wrapper = AsyncSignerWrapper(signer);
Ok(sign_async(&wrapper, data, Some(box_size), time_stamp_storage).await?)
}
}
}
}
fn signing_cert_valid(signing_cert: &[u8], settings: &Settings) -> Result<()> {
let mut cose_log = StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError);
let mut passthrough_cap = CertificateTrustPolicy::default();
if let Some(trust_config) = &settings.trust.trust_config {
let trust_config = trust_config.to_owned().replace("\\n", "\n").into_bytes();
passthrough_cap.add_valid_ekus(&trust_config);
}
Ok(check_end_entity_certificate_profile(
signing_cert,
&passthrough_cap,
&mut cose_log,
None,
)?)
}
struct SignerWrapper<'a>(&'a dyn Signer);
impl RawSigner for SignerWrapper<'_> {
fn sign(&self, data: &[u8]) -> std::result::Result<Vec<u8>, RawSignerError> {
Ok(self.0.sign(data)?)
}
fn alg(&self) -> SigningAlg {
self.0.alg()
}
fn cert_chain(&self) -> std::result::Result<Vec<Vec<u8>>, RawSignerError> {
Ok(self.0.certs()?)
}
fn reserve_size(&self) -> usize {
self.0.reserve_size()
}
fn ocsp_response(&self) -> Option<Vec<u8>> {
self.0.ocsp_val()
}
}
impl TimeStampProvider for SignerWrapper<'_> {
fn time_stamp_service_url(&self) -> Option<String> {
self.0.time_authority_url()
}
fn time_stamp_request_headers(&self) -> Option<Vec<(String, String)>> {
self.0.timestamp_request_headers()
}
fn time_stamp_request_body(
&self,
message: &[u8],
) -> std::result::Result<Vec<u8>, TimeStampError> {
Ok(self.0.timestamp_request_body(message)?)
}
fn send_time_stamp_request(
&self,
message: &[u8],
) -> Option<std::result::Result<Vec<u8>, TimeStampError>> {
self.0
.send_timestamp_request(message)
.map(|r| r.map_err(|e| e.into()))
}
}
struct AsyncSignerWrapper<'a>(&'a dyn AsyncSigner);
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl AsyncRawSigner for AsyncSignerWrapper<'_> {
async fn sign(&self, data: Vec<u8>) -> std::result::Result<Vec<u8>, RawSignerError> {
Ok(self.0.sign(data).await?)
}
fn alg(&self) -> SigningAlg {
self.0.alg()
}
fn cert_chain(&self) -> std::result::Result<Vec<Vec<u8>>, RawSignerError> {
Ok(self.0.certs()?)
}
fn reserve_size(&self) -> usize {
self.0.reserve_size()
}
async fn ocsp_response(&self) -> Option<Vec<u8>> {
self.0.ocsp_val().await
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl AsyncTimeStampProvider for AsyncSignerWrapper<'_> {
fn time_stamp_service_url(&self) -> Option<String> {
self.0.time_authority_url()
}
fn time_stamp_request_headers(&self) -> Option<Vec<(String, String)>> {
self.0.timestamp_request_headers()
}
fn time_stamp_request_body(
&self,
message: &[u8],
) -> std::result::Result<Vec<u8>, TimeStampError> {
Ok(self.0.timestamp_request_body(message)?)
}
async fn send_time_stamp_request(
&self,
message: &[u8],
) -> Option<std::result::Result<Vec<u8>, TimeStampError>> {
self.0
.send_timestamp_request(message)
.await
.map(|r| r.map_err(|e| e.into()))
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use c2pa_macros::c2pa_test_async;
#[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))]
use wasm_bindgen_test::wasm_bindgen_test;
use super::sign_claim;
use crate::{
claim::Claim,
crypto::raw_signature::SigningAlg,
settings::Settings,
utils::test_signer::{async_test_signer, test_signer},
Result, Signer,
};
#[test]
fn test_sign_claim() {
let mut settings = Settings::default();
settings.verify.verify_trust = false;
let mut claim = Claim::new("extern_sign_test", Some("contentauth"), 1);
claim.build().unwrap();
let claim_bytes = claim.data().unwrap();
let signer = test_signer(SigningAlg::Ps256);
let box_size = Signer::reserve_size(signer.as_ref());
let cose_sign1 = sign_claim(&claim_bytes, signer.as_ref(), box_size, &settings).unwrap();
assert_eq!(cose_sign1.len(), box_size);
}
#[c2pa_test_async]
async fn test_sign_claim_async() {
let mut settings = Settings::default();
settings.verify.verify_trust = false;
use crate::{cose_sign::sign_claim_async, crypto::raw_signature::SigningAlg, AsyncSigner};
let mut claim = Claim::new("extern_sign_test", Some("contentauth"), 1);
claim.build().unwrap();
let claim_bytes = claim.data().unwrap();
let signer = async_test_signer(SigningAlg::Ps256);
let box_size = signer.reserve_size();
let cose_sign1 = sign_claim_async(&claim_bytes, &signer, box_size, &settings)
.await
.unwrap();
assert_eq!(cose_sign1.len(), box_size);
}
struct BogusSigner {}
impl BogusSigner {
pub fn new() -> Self {
BogusSigner {}
}
}
impl Signer for BogusSigner {
fn sign(&self, _data: &[u8]) -> Result<Vec<u8>> {
eprintln!("Canary, canary, please cause this deploy to fail!");
Ok(b"totally bogus signature".to_vec())
}
fn alg(&self) -> crate::crypto::raw_signature::SigningAlg {
crate::crypto::raw_signature::SigningAlg::Ps256
}
fn certs(&self) -> Result<Vec<Vec<u8>>> {
let cert_vec: Vec<u8> = Vec::new();
let certs = vec![cert_vec];
Ok(certs)
}
fn reserve_size(&self) -> usize {
1024
}
fn send_timestamp_request(&self, _message: &[u8]) -> Option<crate::error::Result<Vec<u8>>> {
Some(Ok(Vec::new()))
}
}
#[test]
fn test_bogus_signer() {
let settings = Settings::default();
let mut claim = Claim::new("bogus_sign_test", Some("contentauth"), 1);
claim.build().unwrap();
let claim_bytes = claim.data().unwrap();
let box_size = 10000;
let signer = BogusSigner::new();
let _cose_sign1 = sign_claim(&claim_bytes, &signer, box_size, &settings);
assert!(_cose_sign1.is_err());
}
}