11
11
// specific language governing permissions and limitations under
12
12
// each license.
13
13
14
+ use std:: io:: Cursor ;
15
+
16
+ use asn1_rs:: FromDer ;
14
17
use async_generic:: async_generic;
15
18
use c2pa_status_tracker:: {
16
19
log_item,
17
20
validation_codes:: {
18
- SIGNING_CREDENTIAL_TRUSTED , SIGNING_CREDENTIAL_UNTRUSTED , TIMESTAMP_MISMATCH ,
19
- TIMESTAMP_OUTSIDE_VALIDITY ,
21
+ ALGORITHM_UNSUPPORTED , SIGNING_CREDENTIAL_INVALID , SIGNING_CREDENTIAL_TRUSTED ,
22
+ SIGNING_CREDENTIAL_UNTRUSTED , TIMESTAMP_MISMATCH , TIMESTAMP_OUTSIDE_VALIDITY ,
20
23
} ,
21
24
StatusTracker ,
22
25
} ;
23
26
use coset:: CoseSign1 ;
27
+ use x509_parser:: prelude:: X509Certificate ;
24
28
25
29
use crate :: {
26
30
asn1:: rfc3161:: TstInfo ,
27
31
cose:: {
28
- cert_chain_from_sign1, check_certificate_profile, CertificateTrustError ,
32
+ cert_chain_from_sign1, check_certificate_profile, parse_cose_sign1, signing_alg_from_sign1,
33
+ validate_cose_tst_info, validate_cose_tst_info_async, CertificateTrustError ,
29
34
CertificateTrustPolicy , CoseError ,
30
35
} ,
36
+ p1363:: parse_ec_der_sig,
37
+ raw_signature:: { async_validator_for_signing_alg, validator_for_signing_alg} ,
31
38
time_stamp:: TimeStampError ,
39
+ SigningAlg , ValidationInfo ,
32
40
} ;
33
41
34
42
/// A `Verifier` reads a COSE signature and reports on its validity.
@@ -52,6 +60,109 @@ pub enum Verifier<'a> {
52
60
}
53
61
54
62
impl Verifier < ' _ > {
63
+ /// Verify a COSE signature according to the configured policies.
64
+ #[ async_generic]
65
+ pub fn verify_signature (
66
+ & self ,
67
+ cose_sign1 : & [ u8 ] ,
68
+ data : & [ u8 ] ,
69
+ additional_data : & [ u8 ] ,
70
+ validation_log : & mut impl StatusTracker ,
71
+ ) -> Result < ValidationInfo , CoseError > {
72
+ let mut sign1 = parse_cose_sign1 ( cose_sign1, data, validation_log) ?;
73
+
74
+ let Ok ( alg) = signing_alg_from_sign1 ( & sign1) else {
75
+ log_item ! (
76
+ "Cose_Sign1" ,
77
+ "unsupported or missing Cose algorithm" ,
78
+ "verify_cose"
79
+ )
80
+ . validation_status ( ALGORITHM_UNSUPPORTED )
81
+ . failure_no_throw ( validation_log, CoseError :: UnsupportedSigningAlgorithm ) ;
82
+
83
+ return Err ( CoseError :: UnsupportedSigningAlgorithm ) ;
84
+ } ;
85
+
86
+ let tst_info_res = if _sync {
87
+ validate_cose_tst_info ( & sign1, data)
88
+ } else {
89
+ validate_cose_tst_info_async ( & sign1, data) . await
90
+ } ;
91
+
92
+ if _sync {
93
+ self . verify_profile ( & sign1, & tst_info_res, validation_log) ?;
94
+ self . verify_trust ( & sign1, & tst_info_res, validation_log) ?;
95
+ } else {
96
+ self . verify_profile_async ( & sign1, & tst_info_res, validation_log)
97
+ . await ?;
98
+ self . verify_trust_async ( & sign1, & tst_info_res, validation_log)
99
+ . await ?;
100
+ }
101
+
102
+ match alg {
103
+ SigningAlg :: Es256 | SigningAlg :: Es384 | SigningAlg :: Es512 => {
104
+ if parse_ec_der_sig ( & sign1. signature ) . is_ok ( ) {
105
+ // Should have been in P1363 format, not DER.
106
+ log_item ! ( "Cose_Sign1" , "unsupported signature format" , "verify_cose" )
107
+ . validation_status ( SIGNING_CREDENTIAL_INVALID )
108
+ . failure_no_throw ( validation_log, CoseError :: InvalidEcdsaSignature ) ;
109
+
110
+ // validation_log.log(log_item, CoseError::InvalidEcdsaSignature)?;
111
+ return Err ( CoseError :: InvalidEcdsaSignature ) ;
112
+ }
113
+ }
114
+ _ => ( ) ,
115
+ }
116
+
117
+ // Reconstruct payload and additional data as it should have been at time of
118
+ // signing.
119
+ sign1. payload = Some ( data. to_vec ( ) ) ;
120
+ let tbs = sign1. tbs_data ( additional_data) ;
121
+
122
+ let certs = cert_chain_from_sign1 ( & sign1) ?;
123
+ let end_entity_cert_der = & certs[ 0 ] ;
124
+
125
+ let ( _rem, sign_cert) = X509Certificate :: from_der ( end_entity_cert_der)
126
+ . map_err ( |_| CoseError :: CborParsingError ( "invalid X509 certificate" . to_string ( ) ) ) ?;
127
+ let pk = sign_cert. public_key ( ) ;
128
+ let pk_der = pk. raw ;
129
+
130
+ if _sync {
131
+ let Some ( validator) = validator_for_signing_alg ( alg) else {
132
+ return Err ( CoseError :: UnsupportedSigningAlgorithm ) ;
133
+ } ;
134
+
135
+ validator. validate ( & sign1. signature , & tbs, pk_der) ?;
136
+ } else {
137
+ let Some ( validator) = async_validator_for_signing_alg ( alg) else {
138
+ return Err ( CoseError :: UnsupportedSigningAlgorithm ) ;
139
+ } ;
140
+
141
+ validator
142
+ . validate_async ( & sign1. signature , & tbs, pk_der)
143
+ . await ?;
144
+ }
145
+
146
+ let subject = sign_cert
147
+ . subject ( )
148
+ . iter_organization ( )
149
+ . map ( |attr| attr. as_str ( ) )
150
+ . last ( )
151
+ . ok_or ( CoseError :: MissingSigningCertificateChain ) ?
152
+ . map ( |attr| attr. to_string ( ) )
153
+ . map_err ( |_| CoseError :: MissingSigningCertificateChain ) ?;
154
+
155
+ Ok ( ValidationInfo {
156
+ alg : Some ( alg) ,
157
+ date : tst_info_res. map ( |t| t. gen_time . into ( ) ) . ok ( ) ,
158
+ cert_serial_number : Some ( sign_cert. serial . clone ( ) ) ,
159
+ issuer_org : Some ( subject) ,
160
+ validated : true ,
161
+ cert_chain : dump_cert_chain ( & certs) ?,
162
+ revocation_status : Some ( true ) ,
163
+ } )
164
+ }
165
+
55
166
/// Verify certificate profile if so configured.
56
167
///
57
168
/// TO DO: This might not need to be public after refactoring.
@@ -194,3 +305,20 @@ impl Verifier<'_> {
194
305
}
195
306
}
196
307
}
308
+
309
+ fn dump_cert_chain ( certs : & [ Vec < u8 > ] ) -> Result < Vec < u8 > , CoseError > {
310
+ let mut out_buf: Vec < u8 > = Vec :: new ( ) ;
311
+ let mut writer = Cursor :: new ( out_buf) ;
312
+
313
+ for der_bytes in certs {
314
+ let c = x509_certificate:: X509Certificate :: from_der ( der_bytes)
315
+ . map_err ( |_e| CoseError :: CborParsingError ( "invalid X509 certificate" . to_string ( ) ) ) ?;
316
+
317
+ c. write_pem ( & mut writer) . map_err ( |_| {
318
+ CoseError :: InternalError ( "I/O error constructing cert_chain dump" . to_string ( ) )
319
+ } ) ?;
320
+ }
321
+
322
+ out_buf = writer. into_inner ( ) ;
323
+ Ok ( out_buf)
324
+ }
0 commit comments