@@ -15,7 +15,7 @@ use crate::error::ErrorStack;
15
15
use crate :: pkey:: { HasPrivate , PKeyRef } ;
16
16
use crate :: stack:: StackRef ;
17
17
use crate :: symm:: Cipher ;
18
- use crate :: x509:: { X509Ref , X509 } ;
18
+ use crate :: x509:: { store :: X509StoreRef , X509Ref , X509 } ;
19
19
use crate :: { cvt, cvt_p} ;
20
20
use openssl_macros:: corresponds;
21
21
@@ -227,14 +227,65 @@ impl CmsContentInfo {
227
227
Ok ( CmsContentInfo :: from_ptr ( cms) )
228
228
}
229
229
}
230
+
231
+ /// Verify this CmsContentInfo's signature,
232
+ /// This will search the 'certs' list for the signing certificate.
233
+ /// Additional certificates, needed for building the certificate chain, may be
234
+ /// given in 'store' as well as additional CRLs.
235
+ /// A detached signature may be passed in `detached_data`. The signed content
236
+ /// without signature, will be copied into output_data if it is present.
237
+ ///
238
+ #[ corresponds( CMS_verify ) ]
239
+ pub fn verify (
240
+ & mut self ,
241
+ certs : Option < & StackRef < X509 > > ,
242
+ store : Option < & X509StoreRef > ,
243
+ detached_data : Option < & [ u8 ] > ,
244
+ output_data : Option < & mut Vec < u8 > > ,
245
+ flags : CMSOptions ,
246
+ ) -> Result < ( ) , ErrorStack > {
247
+ unsafe {
248
+ let certs_ptr = certs. map_or ( ptr:: null_mut ( ) , |p| p. as_ptr ( ) ) ;
249
+ let store_ptr = store. map_or ( ptr:: null_mut ( ) , |p| p. as_ptr ( ) ) ;
250
+ let detached_data_bio = match detached_data {
251
+ Some ( data) => Some ( MemBioSlice :: new ( data) ?) ,
252
+ None => None ,
253
+ } ;
254
+ let detached_data_bio_ptr = detached_data_bio
255
+ . as_ref ( )
256
+ . map_or ( ptr:: null_mut ( ) , |p| p. as_ptr ( ) ) ;
257
+ let out_bio = MemBio :: new ( ) ?;
258
+
259
+ cvt ( ffi:: CMS_verify (
260
+ self . as_ptr ( ) ,
261
+ certs_ptr,
262
+ store_ptr,
263
+ detached_data_bio_ptr,
264
+ out_bio. as_ptr ( ) ,
265
+ flags. bits ( ) ,
266
+ ) ) ?;
267
+
268
+ if let Some ( data) = output_data {
269
+ data. clear ( ) ;
270
+ data. extend_from_slice ( out_bio. get_buf ( ) ) ;
271
+ } ;
272
+
273
+ Ok ( ( ) )
274
+ }
275
+ }
230
276
}
231
277
232
278
#[ cfg( test) ]
233
279
mod test {
234
280
use super :: * ;
281
+
235
282
use crate :: pkcs12:: Pkcs12 ;
283
+ use crate :: pkey:: PKey ;
236
284
use crate :: stack:: Stack ;
237
- use crate :: x509:: X509 ;
285
+ use crate :: x509:: {
286
+ store:: { X509Store , X509StoreBuilder } ,
287
+ X509 ,
288
+ } ;
238
289
239
290
#[ test]
240
291
fn cms_encrypt_decrypt ( ) {
@@ -317,4 +368,119 @@ mod test {
317
368
assert_eq ! ( input, decrypt_without_cert_check) ;
318
369
}
319
370
}
371
+
372
+ fn cms_sign_verify_generic_helper ( is_detached : bool ) {
373
+ // load cert with private key
374
+ let cert_bytes = include_bytes ! ( "../test/cert.pem" ) ;
375
+ let cert = X509 :: from_pem ( cert_bytes) . expect ( "failed to load cert.pem" ) ;
376
+
377
+ let key_bytes = include_bytes ! ( "../test/key.pem" ) ;
378
+ let key = PKey :: private_key_from_pem ( key_bytes) . expect ( "failed to load key.pem" ) ;
379
+
380
+ let root_bytes = include_bytes ! ( "../test/root-ca.pem" ) ;
381
+ let root = X509 :: from_pem ( root_bytes) . expect ( "failed to load root-ca.pem" ) ;
382
+
383
+ // sign cms message using public key cert
384
+ let data = b"Hello world!" ;
385
+
386
+ let ( opt, ext_data) : ( CMSOptions , Option < & [ u8 ] > ) = if is_detached {
387
+ ( CMSOptions :: DETACHED | CMSOptions :: BINARY , Some ( data) )
388
+ } else {
389
+ ( CMSOptions :: empty ( ) , None )
390
+ } ;
391
+
392
+ let mut cms = CmsContentInfo :: sign ( Some ( & cert) , Some ( & key) , None , Some ( data) , opt)
393
+ . expect ( "failed to CMS sign a message" ) ;
394
+
395
+ // check CMS signature length
396
+ let pem_cms = cms
397
+ . to_pem ( )
398
+ . expect ( "failed to pack CmsContentInfo into PEM" ) ;
399
+ assert ! ( !pem_cms. is_empty( ) ) ;
400
+
401
+ // verify CMS signature
402
+ let mut builder = X509StoreBuilder :: new ( ) . expect ( "failed to create X509StoreBuilder" ) ;
403
+ builder
404
+ . add_cert ( root)
405
+ . expect ( "failed to add root-ca into X509StoreBuilder" ) ;
406
+ let store: X509Store = builder. build ( ) ;
407
+ let mut out_data: Vec < u8 > = Vec :: new ( ) ;
408
+ let res = cms. verify (
409
+ None ,
410
+ Some ( & store) ,
411
+ ext_data,
412
+ Some ( & mut out_data) ,
413
+ CMSOptions :: empty ( ) ,
414
+ ) ;
415
+
416
+ // check verification result - valid signature
417
+ res. unwrap ( ) ;
418
+ assert_eq ! ( data. to_vec( ) , out_data) ;
419
+ }
420
+
421
+ #[ test]
422
+ fn cms_sign_verify_ok ( ) {
423
+ cms_sign_verify_generic_helper ( false ) ;
424
+ }
425
+
426
+ #[ test]
427
+ fn cms_sign_verify_detached_ok ( ) {
428
+ cms_sign_verify_generic_helper ( true ) ;
429
+ }
430
+
431
+ #[ test]
432
+ fn cms_sign_verify_error ( ) {
433
+ #[ cfg( ossl300) ]
434
+ let _provider = crate :: provider:: Provider :: try_load ( None , "legacy" , true ) . unwrap ( ) ;
435
+
436
+ // load cert with private key
437
+ let priv_cert_bytes = include_bytes ! ( "../test/cms.p12" ) ;
438
+ let priv_cert = Pkcs12 :: from_der ( priv_cert_bytes) . expect ( "failed to load priv cert" ) ;
439
+ let priv_cert = priv_cert
440
+ . parse2 ( "mypass" )
441
+ . expect ( "failed to parse priv cert" ) ;
442
+
443
+ // sign cms message using public key cert
444
+ let data = b"Hello world!" ;
445
+ let mut cms = CmsContentInfo :: sign (
446
+ Some ( & priv_cert. cert . unwrap ( ) ) ,
447
+ Some ( & priv_cert. pkey . unwrap ( ) ) ,
448
+ None ,
449
+ Some ( data) ,
450
+ CMSOptions :: empty ( ) ,
451
+ )
452
+ . expect ( "failed to CMS sign a message" ) ;
453
+
454
+ // check CMS signature length
455
+ let pem_cms = cms
456
+ . to_pem ( )
457
+ . expect ( "failed to pack CmsContentInfo into PEM" ) ;
458
+ assert ! ( !pem_cms. is_empty( ) ) ;
459
+
460
+ let empty_store = X509StoreBuilder :: new ( )
461
+ . expect ( "failed to create X509StoreBuilder" )
462
+ . build ( ) ;
463
+
464
+ // verify CMS signature
465
+ let res = cms. verify (
466
+ None ,
467
+ Some ( & empty_store) ,
468
+ Some ( data) ,
469
+ None ,
470
+ CMSOptions :: empty ( ) ,
471
+ ) ;
472
+
473
+ // check verification result - this is an invalid signature
474
+ // defined in openssl crypto/cms/cms.h
475
+ const CMS_R_CERTIFICATE_VERIFY_ERROR : i32 = 100 ;
476
+ match res {
477
+ Err ( es) => {
478
+ let error_array = es. errors ( ) ;
479
+ assert_eq ! ( 1 , error_array. len( ) ) ;
480
+ let code = error_array[ 0 ] . code ( ) ;
481
+ assert_eq ! ( ffi:: ERR_GET_REASON ( code) , CMS_R_CERTIFICATE_VERIFY_ERROR ) ;
482
+ }
483
+ _ => panic ! ( "expected CMS verification error, got Ok()" ) ,
484
+ }
485
+ }
320
486
}
0 commit comments