Skip to content

Commit be23bdc

Browse files
feat: Introduce c2pa_crypto::Verifier::verify_trust (#798)
1 parent f1f356b commit be23bdc

File tree

6 files changed

+98
-110
lines changed

6 files changed

+98
-110
lines changed

internal/crypto/src/cose/error.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313

1414
use thiserror::Error;
1515

16-
use crate::{cose::CertificateProfileError, time_stamp::TimeStampError};
16+
use crate::{
17+
cose::{CertificateProfileError, CertificateTrustError},
18+
time_stamp::TimeStampError,
19+
};
1720

1821
/// Describes errors that can occur when processing or generating [COSE]
1922
/// signatures.
@@ -50,6 +53,10 @@ pub enum CoseError {
5053
#[error(transparent)]
5154
CertificateProfileError(#[from] CertificateProfileError),
5255

56+
/// The signing certificate(s) did not match the required trust policy.
57+
#[error(transparent)]
58+
CertificateTrustError(#[from] CertificateTrustError),
59+
5360
/// An unexpected internal error occured while requesting the time stamp
5461
/// response.
5562
#[error("internal error ({0})")]

internal/crypto/src/cose/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,5 @@ pub use sigtst::{
3838
validate_cose_tst_info, validate_cose_tst_info_async, TstToken,
3939
};
4040

41-
mod verify;
42-
pub use verify::Verifier;
41+
mod verifier;
42+
pub use verifier::Verifier;

internal/crypto/src/cose/verify.rs renamed to internal/crypto/src/cose/verifier.rs

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,20 @@
1414
use async_generic::async_generic;
1515
use c2pa_status_tracker::{
1616
log_item,
17-
validation_codes::{TIMESTAMP_MISMATCH, TIMESTAMP_OUTSIDE_VALIDITY},
17+
validation_codes::{
18+
SIGNING_CREDENTIAL_TRUSTED, SIGNING_CREDENTIAL_UNTRUSTED, TIMESTAMP_MISMATCH,
19+
TIMESTAMP_OUTSIDE_VALIDITY,
20+
},
1821
StatusTracker,
1922
};
2023
use coset::CoseSign1;
2124

2225
use crate::{
2326
asn1::rfc3161::TstInfo,
24-
cose::{cert_chain_from_sign1, check_certificate_profile, CertificateTrustPolicy, CoseError},
27+
cose::{
28+
cert_chain_from_sign1, check_certificate_profile, CertificateTrustError,
29+
CertificateTrustPolicy, CoseError,
30+
},
2531
time_stamp::TimeStampError,
2632
};
2733

@@ -117,4 +123,74 @@ impl Verifier<'_> {
117123
}
118124
}
119125
}
126+
127+
/// Verify certificate profile if so configured.
128+
///
129+
/// TO DO: This might not need to be public after refactoring.
130+
#[async_generic]
131+
pub fn verify_trust(
132+
&self,
133+
sign1: &CoseSign1,
134+
tst_info_res: &Result<TstInfo, CoseError>,
135+
validation_log: &mut impl StatusTracker,
136+
) -> Result<(), CoseError> {
137+
// IMPORTANT: This function assumes that verify_profile has already been called.
138+
139+
let ctp = match self {
140+
Self::VerifyTrustPolicy(ctp) => *ctp,
141+
142+
Self::VerifyCertificateProfileOnly(_ctp) => {
143+
return Ok(());
144+
}
145+
146+
Self::IgnoreProfileAndTrustPolicy => {
147+
return Ok(());
148+
}
149+
};
150+
151+
let certs = cert_chain_from_sign1(sign1)?;
152+
let end_entity_cert_der = &certs[0];
153+
let chain_der = &certs[1..];
154+
155+
let signing_time_epoch = tst_info_res.as_ref().ok().map(|tst_info| {
156+
let dt: chrono::DateTime<chrono::Utc> = tst_info.gen_time.clone().into();
157+
dt.timestamp()
158+
});
159+
160+
let verify_result = if _sync {
161+
ctp.check_certificate_trust(chain_der, end_entity_cert_der, signing_time_epoch)
162+
} else {
163+
ctp.check_certificate_trust_async(chain_der, end_entity_cert_der, signing_time_epoch)
164+
.await
165+
};
166+
167+
match verify_result {
168+
Ok(()) => {
169+
log_item!("Cose_Sign1", "signing certificate trusted", "verify_cose")
170+
.validation_status(SIGNING_CREDENTIAL_TRUSTED)
171+
.success(validation_log);
172+
173+
Ok(())
174+
}
175+
176+
Err(CertificateTrustError::CertificateNotTrusted) => {
177+
log_item!("Cose_Sign1", "signing certificate untrusted", "verify_cose")
178+
.validation_status(SIGNING_CREDENTIAL_UNTRUSTED)
179+
.failure_no_throw(validation_log, CertificateTrustError::CertificateNotTrusted);
180+
181+
Err(CertificateTrustError::CertificateNotTrusted.into())
182+
}
183+
184+
Err(e) => {
185+
log_item!("Cose_Sign1", "signing certificate untrusted", "verify_cose")
186+
.validation_status(SIGNING_CREDENTIAL_UNTRUSTED)
187+
.failure_no_throw(validation_log, &e);
188+
189+
// TO REVIEW: Mixed message: Are we using CoseCertUntrusted in log or &e from
190+
// above? validation_log.log(log_item,
191+
// Error::CoseCertUntrusted)?;
192+
Err(e.into())
193+
}
194+
}
195+
}
120196
}

sdk/src/cose_validator.rs

Lines changed: 5 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@ use std::io::Cursor;
1515

1616
use async_generic::async_generic;
1717
use c2pa_crypto::{
18-
asn1::rfc3161::TstInfo,
1918
cose::{
2019
cert_chain_from_sign1, parse_cose_sign1, signing_alg_from_sign1, validate_cose_tst_info,
21-
validate_cose_tst_info_async, CertificateTrustError, CertificateTrustPolicy,
22-
OcspFetchPolicy, Verifier,
20+
validate_cose_tst_info_async, CertificateTrustPolicy, OcspFetchPolicy, Verifier,
2321
},
2422
ocsp::OcspResponse,
2523
p1363::parse_ec_der_sig,
@@ -68,65 +66,6 @@ pub(crate) fn check_ocsp_status(
6866
}
6967
}
7068

71-
#[async_generic(async_signature(
72-
ctp: &CertificateTrustPolicy,
73-
chain_der: &[Vec<u8>],
74-
cert_der: &[u8],
75-
signing_time_epoch: Option<i64>,
76-
validation_log: &mut impl StatusTracker
77-
))]
78-
#[allow(unused)]
79-
fn check_trust(
80-
ctp: &CertificateTrustPolicy,
81-
chain_der: &[Vec<u8>],
82-
cert_der: &[u8],
83-
signing_time_epoch: Option<i64>,
84-
validation_log: &mut impl StatusTracker,
85-
) -> Result<()> {
86-
// just return is trust checks are disabled or misconfigured
87-
match get_settings_value::<bool>("verify.verify_trust") {
88-
Ok(verify_trust) => {
89-
if !verify_trust {
90-
return Ok(());
91-
}
92-
}
93-
Err(e) => return Err(e),
94-
}
95-
96-
let verify_result = if _sync {
97-
ctp.check_certificate_trust(chain_der, cert_der, signing_time_epoch)
98-
} else {
99-
ctp.check_certificate_trust_async(chain_der, cert_der, signing_time_epoch)
100-
.await
101-
};
102-
103-
match verify_result {
104-
Ok(()) => {
105-
log_item!("Cose_Sign1", "signing certificate trusted", "verify_cose")
106-
.validation_status(SIGNING_CREDENTIAL_TRUSTED)
107-
.success(validation_log);
108-
109-
Ok(())
110-
}
111-
Err(CertificateTrustError::CertificateNotTrusted) => {
112-
log_item!("Cose_Sign1", "signing certificate untrusted", "verify_cose")
113-
.validation_status(SIGNING_CREDENTIAL_UNTRUSTED)
114-
.failure_no_throw(validation_log, Error::CoseCertUntrusted);
115-
116-
Err(Error::CoseCertUntrusted)
117-
}
118-
Err(e) => {
119-
log_item!("Cose_Sign1", "signing certificate untrusted", "verify_cose")
120-
.validation_status(SIGNING_CREDENTIAL_UNTRUSTED)
121-
.failure_no_throw(validation_log, &e);
122-
123-
// TO REVIEW: Mixed message: Are we using CoseCertUntrusted in log or &e from above?
124-
// validation_log.log(log_item, Error::CoseCertUntrusted)?;
125-
Err(e.into())
126-
}
127-
}
128-
}
129-
13069
// ---- TEMPORARY MARKER: Above this line will not move to c2pa-crypto
13170

13271
fn get_sign_cert(sign1: &coset::CoseSign1) -> Result<Vec<u8>> {
@@ -207,11 +146,6 @@ fn extract_serial_from_cert(cert: &X509Certificate) -> BigUint {
207146
cert.serial.clone()
208147
}
209148

210-
fn tst_info_to_timestamp(tst_info: &TstInfo) -> i64 {
211-
let dt: chrono::DateTime<chrono::Utc> = tst_info.gen_time.clone().into();
212-
dt.timestamp()
213-
}
214-
215149
/// Asynchronously validate a COSE_SIGN1 byte vector and verify against expected data
216150
/// cose_bytes - byte array containing the raw COSE_SIGN1 data
217151
/// data: data that was used to create the cose_bytes, these must match
@@ -267,31 +201,11 @@ pub(crate) async fn verify_cose_async(
267201
.verify_profile_async(&sign1, &tst_info_res, validation_log)
268202
.await?;
269203

270-
// verify cert matches requested algorithm
271-
if cert_check {
272-
// is the certificate trusted
273-
#[cfg(target_arch = "wasm32")]
274-
check_trust_async(
275-
ctp,
276-
&certs[1..],
277-
der_bytes,
278-
tst_info_res.as_ref().ok().map(tst_info_to_timestamp),
279-
validation_log,
280-
)
204+
// TO REVIEW: Do we need the async case on non-WASM platforms?
205+
verifier
206+
.verify_trust_async(&sign1, &tst_info_res, validation_log)
281207
.await?;
282208

283-
#[cfg(not(target_arch = "wasm32"))]
284-
check_trust(
285-
ctp,
286-
&certs[1..],
287-
der_bytes,
288-
tst_info_res.as_ref().ok().map(tst_info_to_timestamp),
289-
validation_log,
290-
)?;
291-
292-
// todo: check TSA certs against trust list
293-
}
294-
295209
// check signature format
296210
if let Err(_e) = check_sig(&sign1.signature, alg) {
297211
log_item!("Cose_Sign1", "unsupported signature format", "verify_cose")
@@ -449,19 +363,7 @@ pub(crate) fn verify_cose(
449363
};
450364

451365
verifier.verify_profile(&sign1, &tst_info_res, validation_log)?;
452-
453-
if cert_check {
454-
// is the certificate trusted
455-
check_trust(
456-
ctp,
457-
&certs[1..],
458-
der_bytes,
459-
tst_info_res.as_ref().ok().map(tst_info_to_timestamp),
460-
validation_log,
461-
)?;
462-
463-
// todo: check TSA certs against trust list
464-
}
366+
verifier.verify_trust(&sign1, &tst_info_res, validation_log)?;
465367

466368
// check signature format
467369
if let Err(e) = check_sig(&sign1.signature, alg) {

sdk/src/error.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ impl From<CoseError> for Error {
355355
CoseError::CborParsingError(_) => Self::CoseTimeStampGeneration,
356356
CoseError::TimeStampError(e) => e.into(),
357357
CoseError::CertificateProfileError(e) => e.into(),
358+
CoseError::CertificateTrustError(e) => e.into(),
358359
CoseError::InternalError(e) => Self::InternalError(e),
359360
}
360361
}

sdk/src/store.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3595,7 +3595,7 @@ pub mod tests {
35953595

35963596
use std::io::Write;
35973597

3598-
use c2pa_crypto::{time_stamp::TimeStampError, SigningAlg};
3598+
use c2pa_crypto::SigningAlg;
35993599
use c2pa_status_tracker::StatusTracker;
36003600
use memchr::memmem;
36013601
use serde::Serialize;
@@ -4895,7 +4895,9 @@ pub mod tests {
48954895
// replace the title that is inside the claim data - should cause signature to not match
48964896
let report = patch_and_report("C.jpg", b"C.jpg", b"X.jpg");
48974897
assert!(!report.logged_items().is_empty());
4898-
assert!(report.has_error(TimeStampError::InvalidData));
4898+
assert!(report.has_error(Error::TimeStampError(
4899+
c2pa_crypto::time_stamp::TimeStampError::InvalidData
4900+
)));
48994901
assert!(report.has_status(validation_status::TIMESTAMP_MISMATCH));
49004902
}
49014903

0 commit comments

Comments
 (0)