iPXE
cms.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /** @file
00027  *
00028  * Cryptographic Message Syntax (PKCS #7)
00029  *
00030  * The format of CMS messages is defined in RFC 5652.
00031  *
00032  */
00033 
00034 #include <stdint.h>
00035 #include <string.h>
00036 #include <time.h>
00037 #include <errno.h>
00038 #include <ipxe/asn1.h>
00039 #include <ipxe/x509.h>
00040 #include <ipxe/malloc.h>
00041 #include <ipxe/uaccess.h>
00042 #include <ipxe/cms.h>
00043 
00044 /* Disambiguate the various error causes */
00045 #define EACCES_NON_SIGNING \
00046         __einfo_error ( EINFO_EACCES_NON_SIGNING )
00047 #define EINFO_EACCES_NON_SIGNING \
00048         __einfo_uniqify ( EINFO_EACCES, 0x01, "Not a signing certificate" )
00049 #define EACCES_NON_CODE_SIGNING \
00050         __einfo_error ( EINFO_EACCES_NON_CODE_SIGNING )
00051 #define EINFO_EACCES_NON_CODE_SIGNING \
00052         __einfo_uniqify ( EINFO_EACCES, 0x02, "Not a code-signing certificate" )
00053 #define EACCES_WRONG_NAME \
00054         __einfo_error ( EINFO_EACCES_WRONG_NAME )
00055 #define EINFO_EACCES_WRONG_NAME \
00056         __einfo_uniqify ( EINFO_EACCES, 0x04, "Incorrect certificate name" )
00057 #define EACCES_NO_SIGNATURES \
00058         __einfo_error ( EINFO_EACCES_NO_SIGNATURES )
00059 #define EINFO_EACCES_NO_SIGNATURES \
00060         __einfo_uniqify ( EINFO_EACCES, 0x05, "No signatures present" )
00061 #define EINVAL_DIGEST \
00062         __einfo_error ( EINFO_EINVAL_DIGEST )
00063 #define EINFO_EINVAL_DIGEST \
00064         __einfo_uniqify ( EINFO_EINVAL, 0x01, "Not a digest algorithm" )
00065 #define EINVAL_PUBKEY \
00066         __einfo_error ( EINFO_EINVAL_PUBKEY )
00067 #define EINFO_EINVAL_PUBKEY \
00068         __einfo_uniqify ( EINFO_EINVAL, 0x02, "Not a public-key algorithm" )
00069 #define ENOTSUP_SIGNEDDATA \
00070         __einfo_error ( EINFO_ENOTSUP_SIGNEDDATA )
00071 #define EINFO_ENOTSUP_SIGNEDDATA \
00072         __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Not a digital signature" )
00073 
00074 /** "pkcs7-signedData" object identifier */
00075 static uint8_t oid_signeddata[] = { ASN1_OID_SIGNEDDATA };
00076 
00077 /** "pkcs7-signedData" object identifier cursor */
00078 static struct asn1_cursor oid_signeddata_cursor =
00079         ASN1_OID_CURSOR ( oid_signeddata );
00080 
00081 /**
00082  * Parse CMS signature content type
00083  *
00084  * @v sig               CMS signature
00085  * @v raw               ASN.1 cursor
00086  * @ret rc              Return status code
00087  */
00088 static int cms_parse_content_type ( struct cms_signature *sig,
00089                                     const struct asn1_cursor *raw ) {
00090         struct asn1_cursor cursor;
00091 
00092         /* Enter contentType */
00093         memcpy ( &cursor, raw, sizeof ( cursor ) );
00094         asn1_enter ( &cursor, ASN1_OID );
00095 
00096         /* Check OID is pkcs7-signedData */
00097         if ( asn1_compare ( &cursor, &oid_signeddata_cursor ) != 0 ) {
00098                 DBGC ( sig, "CMS %p does not contain signedData:\n", sig );
00099                 DBGC_HDA ( sig, 0, raw->data, raw->len );
00100                 return -ENOTSUP_SIGNEDDATA;
00101         }
00102 
00103         DBGC ( sig, "CMS %p contains signedData\n", sig );
00104         return 0;
00105 }
00106 
00107 /**
00108  * Parse CMS signature certificate list
00109  *
00110  * @v sig               CMS signature
00111  * @v raw               ASN.1 cursor
00112  * @ret rc              Return status code
00113  */
00114 static int cms_parse_certificates ( struct cms_signature *sig,
00115                                     const struct asn1_cursor *raw ) {
00116         struct asn1_cursor cursor;
00117         struct x509_certificate *cert;
00118         int rc;
00119 
00120         /* Enter certificates */
00121         memcpy ( &cursor, raw, sizeof ( cursor ) );
00122         asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
00123 
00124         /* Add each certificate */
00125         while ( cursor.len ) {
00126 
00127                 /* Add certificate to chain */
00128                 if ( ( rc = x509_append_raw ( sig->certificates, cursor.data,
00129                                               cursor.len ) ) != 0 ) {
00130                         DBGC ( sig, "CMS %p could not append certificate: %s\n",
00131                                sig, strerror ( rc) );
00132                         DBGC_HDA ( sig, 0, cursor.data, cursor.len );
00133                         return rc;
00134                 }
00135                 cert = x509_last ( sig->certificates );
00136                 DBGC ( sig, "CMS %p found certificate %s\n",
00137                        sig, x509_name ( cert ) );
00138 
00139                 /* Move to next certificate */
00140                 asn1_skip_any ( &cursor );
00141         }
00142 
00143         return 0;
00144 }
00145 
00146 /**
00147  * Identify CMS signature certificate by issuer and serial number
00148  *
00149  * @v sig               CMS signature
00150  * @v issuer            Issuer
00151  * @v serial            Serial number
00152  * @ret cert            X.509 certificate, or NULL if not found
00153  */
00154 static struct x509_certificate *
00155 cms_find_issuer_serial ( struct cms_signature *sig,
00156                          const struct asn1_cursor *issuer,
00157                          const struct asn1_cursor *serial ) {
00158         struct x509_link *link;
00159         struct x509_certificate *cert;
00160 
00161         /* Scan through certificate list */
00162         list_for_each_entry ( link, &sig->certificates->links, list ) {
00163 
00164                 /* Check issuer and serial number */
00165                 cert = link->cert;
00166                 if ( ( asn1_compare ( issuer, &cert->issuer.raw ) == 0 ) &&
00167                      ( asn1_compare ( serial, &cert->serial.raw ) == 0 ) )
00168                         return cert;
00169         }
00170 
00171         return NULL;
00172 }
00173 
00174 /**
00175  * Parse CMS signature signer identifier
00176  *
00177  * @v sig               CMS signature
00178  * @v info              Signer information to fill in
00179  * @v raw               ASN.1 cursor
00180  * @ret rc              Return status code
00181  */
00182 static int cms_parse_signer_identifier ( struct cms_signature *sig,
00183                                          struct cms_signer_info *info,
00184                                          const struct asn1_cursor *raw ) {
00185         struct asn1_cursor cursor;
00186         struct asn1_cursor serial;
00187         struct asn1_cursor issuer;
00188         struct x509_certificate *cert;
00189         int rc;
00190 
00191         /* Enter issuerAndSerialNumber */
00192         memcpy ( &cursor, raw, sizeof ( cursor ) );
00193         asn1_enter ( &cursor, ASN1_SEQUENCE );
00194 
00195         /* Identify issuer */
00196         memcpy ( &issuer, &cursor, sizeof ( issuer ) );
00197         if ( ( rc = asn1_shrink ( &issuer, ASN1_SEQUENCE ) ) != 0 ) {
00198                 DBGC ( sig, "CMS %p/%p could not locate issuer: %s\n",
00199                        sig, info, strerror ( rc ) );
00200                 DBGC_HDA ( sig, 0, raw->data, raw->len );
00201                 return rc;
00202         }
00203         DBGC ( sig, "CMS %p/%p issuer is:\n", sig, info );
00204         DBGC_HDA ( sig, 0, issuer.data, issuer.len );
00205         asn1_skip_any ( &cursor );
00206 
00207         /* Identify serialNumber */
00208         memcpy ( &serial, &cursor, sizeof ( serial ) );
00209         if ( ( rc = asn1_shrink ( &serial, ASN1_INTEGER ) ) != 0 ) {
00210                 DBGC ( sig, "CMS %p/%p could not locate serialNumber: %s\n",
00211                        sig, info, strerror ( rc ) );
00212                 DBGC_HDA ( sig, 0, raw->data, raw->len );
00213                 return rc;
00214         }
00215         DBGC ( sig, "CMS %p/%p serial number is:\n", sig, info );
00216         DBGC_HDA ( sig, 0, serial.data, serial.len );
00217 
00218         /* Identify certificate */
00219         cert = cms_find_issuer_serial ( sig, &issuer, &serial );
00220         if ( ! cert ) {
00221                 DBGC ( sig, "CMS %p/%p could not identify signer's "
00222                        "certificate\n", sig, info );
00223                 return -ENOENT;
00224         }
00225 
00226         /* Append certificate to chain */
00227         if ( ( rc = x509_append ( info->chain, cert ) ) != 0 ) {
00228                 DBGC ( sig, "CMS %p/%p could not append certificate: %s\n",
00229                        sig, info, strerror ( rc ) );
00230                 return rc;
00231         }
00232 
00233         /* Append remaining certificates to chain */
00234         if ( ( rc = x509_auto_append ( info->chain,
00235                                        sig->certificates ) ) != 0 ) {
00236                 DBGC ( sig, "CMS %p/%p could not append certificates: %s\n",
00237                        sig, info, strerror ( rc ) );
00238                 return rc;
00239         }
00240 
00241         return 0;
00242 }
00243 
00244 /**
00245  * Parse CMS signature digest algorithm
00246  *
00247  * @v sig               CMS signature
00248  * @v info              Signer information to fill in
00249  * @v raw               ASN.1 cursor
00250  * @ret rc              Return status code
00251  */
00252 static int cms_parse_digest_algorithm ( struct cms_signature *sig,
00253                                         struct cms_signer_info *info,
00254                                         const struct asn1_cursor *raw ) {
00255         struct asn1_algorithm *algorithm;
00256         int rc;
00257 
00258         /* Identify algorithm */
00259         if ( ( rc = asn1_digest_algorithm ( raw, &algorithm ) ) != 0 ) {
00260                 DBGC ( sig, "CMS %p/%p could not identify digest algorithm: "
00261                        "%s\n", sig, info, strerror ( rc ) );
00262                 DBGC_HDA ( sig, 0, raw->data, raw->len );
00263                 return rc;
00264         }
00265 
00266         /* Record digest algorithm */
00267         info->digest = algorithm->digest;
00268         DBGC ( sig, "CMS %p/%p digest algorithm is %s\n",
00269                sig, info, algorithm->name );
00270 
00271         return 0;
00272 }
00273 
00274 /**
00275  * Parse CMS signature algorithm
00276  *
00277  * @v sig               CMS signature
00278  * @v info              Signer information to fill in
00279  * @v raw               ASN.1 cursor
00280  * @ret rc              Return status code
00281  */
00282 static int cms_parse_signature_algorithm ( struct cms_signature *sig,
00283                                            struct cms_signer_info *info,
00284                                            const struct asn1_cursor *raw ) {
00285         struct asn1_algorithm *algorithm;
00286         int rc;
00287 
00288         /* Identify algorithm */
00289         if ( ( rc = asn1_pubkey_algorithm ( raw, &algorithm ) ) != 0 ) {
00290                 DBGC ( sig, "CMS %p/%p could not identify public-key "
00291                        "algorithm: %s\n", sig, info, strerror ( rc ) );
00292                 DBGC_HDA ( sig, 0, raw->data, raw->len );
00293                 return rc;
00294         }
00295 
00296         /* Record signature algorithm */
00297         info->pubkey = algorithm->pubkey;
00298         DBGC ( sig, "CMS %p/%p public-key algorithm is %s\n",
00299                sig, info, algorithm->name );
00300 
00301         return 0;
00302 }
00303 
00304 /**
00305  * Parse CMS signature value
00306  *
00307  * @v sig               CMS signature
00308  * @v info              Signer information to fill in
00309  * @v raw               ASN.1 cursor
00310  * @ret rc              Return status code
00311  */
00312 static int cms_parse_signature_value ( struct cms_signature *sig,
00313                                        struct cms_signer_info *info,
00314                                        const struct asn1_cursor *raw ) {
00315         struct asn1_cursor cursor;
00316         int rc;
00317 
00318         /* Enter signature */
00319         memcpy ( &cursor, raw, sizeof ( cursor ) );
00320         if ( ( rc = asn1_enter ( &cursor, ASN1_OCTET_STRING ) ) != 0 ) {
00321                 DBGC ( sig, "CMS %p/%p could not locate signature:\n",
00322                        sig, info );
00323                 DBGC_HDA ( sig, 0, raw->data, raw->len );
00324                 return rc;
00325         }
00326 
00327         /* Record signature */
00328         info->signature_len = cursor.len;
00329         info->signature = malloc ( info->signature_len );
00330         if ( ! info->signature )
00331                 return -ENOMEM;
00332         memcpy ( info->signature, cursor.data, info->signature_len );
00333         DBGC ( sig, "CMS %p/%p signature value is:\n", sig, info );
00334         DBGC_HDA ( sig, 0, info->signature, info->signature_len );
00335 
00336         return 0;
00337 }
00338 
00339 /**
00340  * Parse CMS signature signer information
00341  *
00342  * @v sig               CMS signature
00343  * @v info              Signer information to fill in
00344  * @v raw               ASN.1 cursor
00345  * @ret rc              Return status code
00346  */
00347 static int cms_parse_signer_info ( struct cms_signature *sig,
00348                                    struct cms_signer_info *info,
00349                                    const struct asn1_cursor *raw ) {
00350         struct asn1_cursor cursor;
00351         int rc;
00352 
00353         /* Enter signerInfo */
00354         memcpy ( &cursor, raw, sizeof ( cursor ) );
00355         asn1_enter ( &cursor, ASN1_SEQUENCE );
00356 
00357         /* Skip version */
00358         asn1_skip ( &cursor, ASN1_INTEGER );
00359 
00360         /* Parse sid */
00361         if ( ( rc = cms_parse_signer_identifier ( sig, info, &cursor ) ) != 0 )
00362                 return rc;
00363         asn1_skip_any ( &cursor );
00364 
00365         /* Parse digestAlgorithm */
00366         if ( ( rc = cms_parse_digest_algorithm ( sig, info, &cursor ) ) != 0 )
00367                 return rc;
00368         asn1_skip_any ( &cursor );
00369 
00370         /* Skip signedAttrs, if present */
00371         asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
00372 
00373         /* Parse signatureAlgorithm */
00374         if ( ( rc = cms_parse_signature_algorithm ( sig, info, &cursor ) ) != 0)
00375                 return rc;
00376         asn1_skip_any ( &cursor );
00377 
00378         /* Parse signature */
00379         if ( ( rc = cms_parse_signature_value ( sig, info, &cursor ) ) != 0 )
00380                 return rc;
00381 
00382         return 0;
00383 }
00384 
00385 /**
00386  * Parse CMS signature from ASN.1 data
00387  *
00388  * @v sig               CMS signature
00389  * @v raw               ASN.1 cursor
00390  * @ret rc              Return status code
00391  */
00392 static int cms_parse ( struct cms_signature *sig,
00393                        const struct asn1_cursor *raw ) {
00394         struct asn1_cursor cursor;
00395         struct cms_signer_info *info;
00396         int rc;
00397 
00398         /* Enter contentInfo */
00399         memcpy ( &cursor, raw, sizeof ( cursor ) );
00400         asn1_enter ( &cursor, ASN1_SEQUENCE );
00401 
00402         /* Parse contentType */
00403 
00404         if ( ( rc = cms_parse_content_type ( sig, &cursor ) ) != 0 )
00405                 return rc;
00406         asn1_skip_any ( &cursor );
00407 
00408         /* Enter content */
00409         asn1_enter ( &cursor, ASN1_EXPLICIT_TAG ( 0 ) );
00410 
00411         /* Enter signedData */
00412         asn1_enter ( &cursor, ASN1_SEQUENCE );
00413 
00414         /* Skip version */
00415         asn1_skip ( &cursor, ASN1_INTEGER );
00416 
00417         /* Skip digestAlgorithms */
00418         asn1_skip ( &cursor, ASN1_SET );
00419 
00420         /* Skip encapContentInfo */
00421         asn1_skip ( &cursor, ASN1_SEQUENCE );
00422 
00423         /* Parse certificates */
00424         if ( ( rc = cms_parse_certificates ( sig, &cursor ) ) != 0 )
00425                 return rc;
00426         asn1_skip_any ( &cursor );
00427 
00428         /* Skip crls, if present */
00429         asn1_skip_if_exists ( &cursor, ASN1_EXPLICIT_TAG ( 1 ) );
00430 
00431         /* Enter signerInfos */
00432         asn1_enter ( &cursor, ASN1_SET );
00433 
00434         /* Add each signerInfo.  Errors are handled by ensuring that
00435          * cms_put() will always be able to free any allocated memory.
00436          */
00437         while ( cursor.len ) {
00438 
00439                 /* Allocate signer information block */
00440                 info = zalloc ( sizeof ( *info ) );
00441                 if ( ! info )
00442                         return -ENOMEM;
00443                 list_add ( &info->list, &sig->info );
00444 
00445                 /* Allocate certificate chain */
00446                 info->chain = x509_alloc_chain();
00447                 if ( ! info->chain )
00448                         return -ENOMEM;
00449 
00450                 /* Parse signerInfo */
00451                 if ( ( rc = cms_parse_signer_info ( sig, info,
00452                                                     &cursor ) ) != 0 )
00453                         return rc;
00454                 asn1_skip_any ( &cursor );
00455         }
00456 
00457         return 0;
00458 }
00459 
00460 /**
00461  * Free CMS signature
00462  *
00463  * @v refcnt            Reference count
00464  */
00465 static void cms_free ( struct refcnt *refcnt ) {
00466         struct cms_signature *sig =
00467                 container_of ( refcnt, struct cms_signature, refcnt );
00468         struct cms_signer_info *info;
00469         struct cms_signer_info *tmp;
00470 
00471         list_for_each_entry_safe ( info, tmp, &sig->info, list ) {
00472                 list_del ( &info->list );
00473                 x509_chain_put ( info->chain );
00474                 free ( info->signature );
00475                 free ( info );
00476         }
00477         x509_chain_put ( sig->certificates );
00478         free ( sig );
00479 }
00480 
00481 /**
00482  * Create CMS signature
00483  *
00484  * @v data              Raw signature data
00485  * @v len               Length of raw data
00486  * @ret sig             CMS signature
00487  * @ret rc              Return status code
00488  *
00489  * On success, the caller holds a reference to the CMS signature, and
00490  * is responsible for ultimately calling cms_put().
00491  */
00492 int cms_signature ( const void *data, size_t len, struct cms_signature **sig ) {
00493         struct asn1_cursor cursor;
00494         int rc;
00495 
00496         /* Allocate and initialise signature */
00497         *sig = zalloc ( sizeof ( **sig ) );
00498         if ( ! *sig ) {
00499                 rc = -ENOMEM;
00500                 goto err_alloc;
00501         }
00502         ref_init ( &(*sig)->refcnt, cms_free );
00503         INIT_LIST_HEAD ( &(*sig)->info );
00504 
00505         /* Allocate certificate list */
00506         (*sig)->certificates = x509_alloc_chain();
00507         if ( ! (*sig)->certificates ) {
00508                 rc = -ENOMEM;
00509                 goto err_alloc_chain;
00510         }
00511 
00512         /* Initialise cursor */
00513         cursor.data = data;
00514         cursor.len = len;
00515         asn1_shrink_any ( &cursor );
00516 
00517         /* Parse signature */
00518         if ( ( rc = cms_parse ( *sig, &cursor ) ) != 0 )
00519                 goto err_parse;
00520 
00521         return 0;
00522 
00523  err_parse:
00524  err_alloc_chain:
00525         cms_put ( *sig );
00526  err_alloc:
00527         return rc;
00528 }
00529 
00530 /**
00531  * Calculate digest of CMS-signed data
00532  *
00533  * @v sig               CMS signature
00534  * @v info              Signer information
00535  * @v data              Signed data
00536  * @v len               Length of signed data
00537  * @v out               Digest output
00538  */
00539 static void cms_digest ( struct cms_signature *sig,
00540                          struct cms_signer_info *info,
00541                          userptr_t data, size_t len, void *out ) {
00542         struct digest_algorithm *digest = info->digest;
00543         uint8_t ctx[ digest->ctxsize ];
00544         uint8_t block[ digest->blocksize ];
00545         size_t offset = 0;
00546         size_t frag_len;
00547 
00548         /* Initialise digest */
00549         digest_init ( digest, ctx );
00550 
00551         /* Process data one block at a time */
00552         while ( len ) {
00553                 frag_len = len;
00554                 if ( frag_len > sizeof ( block ) )
00555                         frag_len = sizeof ( block );
00556                 copy_from_user ( block, data, offset, frag_len );
00557                 digest_update ( digest, ctx, block, frag_len );
00558                 offset += frag_len;
00559                 len -= frag_len;
00560         }
00561 
00562         /* Finalise digest */
00563         digest_final ( digest, ctx, out );
00564 
00565         DBGC ( sig, "CMS %p/%p digest value:\n", sig, info );
00566         DBGC_HDA ( sig, 0, out, digest->digestsize );
00567 }
00568 
00569 /**
00570  * Verify digest of CMS-signed data
00571  *
00572  * @v sig               CMS signature
00573  * @v info              Signer information
00574  * @v cert              Corresponding certificate
00575  * @v data              Signed data
00576  * @v len               Length of signed data
00577  * @ret rc              Return status code
00578  */
00579 static int cms_verify_digest ( struct cms_signature *sig,
00580                                struct cms_signer_info *info,
00581                                struct x509_certificate *cert,
00582                                userptr_t data, size_t len ) {
00583         struct digest_algorithm *digest = info->digest;
00584         struct pubkey_algorithm *pubkey = info->pubkey;
00585         struct x509_public_key *public_key = &cert->subject.public_key;
00586         uint8_t digest_out[ digest->digestsize ];
00587         uint8_t ctx[ pubkey->ctxsize ];
00588         int rc;
00589 
00590         /* Generate digest */
00591         cms_digest ( sig, info, data, len, digest_out );
00592 
00593         /* Initialise public-key algorithm */
00594         if ( ( rc = pubkey_init ( pubkey, ctx, public_key->raw.data,
00595                                   public_key->raw.len ) ) != 0 ) {
00596                 DBGC ( sig, "CMS %p/%p could not initialise public key: %s\n",
00597                        sig, info, strerror ( rc ) );
00598                 goto err_init;
00599         }
00600 
00601         /* Verify digest */
00602         if ( ( rc = pubkey_verify ( pubkey, ctx, digest, digest_out,
00603                                     info->signature,
00604                                     info->signature_len ) ) != 0 ) {
00605                 DBGC ( sig, "CMS %p/%p signature verification failed: %s\n",
00606                        sig, info, strerror ( rc ) );
00607                 goto err_verify;
00608         }
00609 
00610  err_verify:
00611         pubkey_final ( pubkey, ctx );
00612  err_init:
00613         return rc;
00614 }
00615 
00616 /**
00617  * Verify CMS signature signer information
00618  *
00619  * @v sig               CMS signature
00620  * @v info              Signer information
00621  * @v data              Signed data
00622  * @v len               Length of signed data
00623  * @v time              Time at which to validate certificates
00624  * @v store             Certificate store, or NULL to use default
00625  * @v root              Root certificate list, or NULL to use default
00626  * @ret rc              Return status code
00627  */
00628 static int cms_verify_signer_info ( struct cms_signature *sig,
00629                                     struct cms_signer_info *info,
00630                                     userptr_t data, size_t len,
00631                                     time_t time, struct x509_chain *store,
00632                                     struct x509_root *root ) {
00633         struct x509_certificate *cert;
00634         int rc;
00635 
00636         /* Validate certificate chain */
00637         if ( ( rc = x509_validate_chain ( info->chain, time, store,
00638                                           root ) ) != 0 ) {
00639                 DBGC ( sig, "CMS %p/%p could not validate chain: %s\n",
00640                        sig, info, strerror ( rc ) );
00641                 return rc;
00642         }
00643 
00644         /* Extract code-signing certificate */
00645         cert = x509_first ( info->chain );
00646         assert ( cert != NULL );
00647 
00648         /* Check that certificate can create digital signatures */
00649         if ( ! ( cert->extensions.usage.bits & X509_DIGITAL_SIGNATURE ) ) {
00650                 DBGC ( sig, "CMS %p/%p certificate cannot create signatures\n",
00651                        sig, info );
00652                 return -EACCES_NON_SIGNING;
00653         }
00654 
00655         /* Check that certificate can sign code */
00656         if ( ! ( cert->extensions.ext_usage.bits & X509_CODE_SIGNING ) ) {
00657                 DBGC ( sig, "CMS %p/%p certificate is not code-signing\n",
00658                        sig, info );
00659                 return -EACCES_NON_CODE_SIGNING;
00660         }
00661 
00662         /* Verify digest */
00663         if ( ( rc = cms_verify_digest ( sig, info, cert, data, len ) ) != 0 )
00664                 return rc;
00665 
00666         return 0;
00667 }
00668 
00669 /**
00670  * Verify CMS signature
00671  *
00672  * @v sig               CMS signature
00673  * @v data              Signed data
00674  * @v len               Length of signed data
00675  * @v name              Required common name, or NULL to check all signatures
00676  * @v time              Time at which to validate certificates
00677  * @v store             Certificate store, or NULL to use default
00678  * @v root              Root certificate list, or NULL to use default
00679  * @ret rc              Return status code
00680  */
00681 int cms_verify ( struct cms_signature *sig, userptr_t data, size_t len,
00682                  const char *name, time_t time, struct x509_chain *store,
00683                  struct x509_root *root ) {
00684         struct cms_signer_info *info;
00685         struct x509_certificate *cert;
00686         int count = 0;
00687         int rc;
00688 
00689         /* Verify using all signerInfos */
00690         list_for_each_entry ( info, &sig->info, list ) {
00691                 cert = x509_first ( info->chain );
00692                 if ( name && ( x509_check_name ( cert, name ) != 0 ) )
00693                         continue;
00694                 if ( ( rc = cms_verify_signer_info ( sig, info, data, len, time,
00695                                                      store, root ) ) != 0 )
00696                         return rc;
00697                 count++;
00698         }
00699 
00700         /* Check that we have verified at least one signature */
00701         if ( count == 0 ) {
00702                 if ( name ) {
00703                         DBGC ( sig, "CMS %p had no signatures matching name "
00704                                "%s\n", sig, name );
00705                         return -EACCES_WRONG_NAME;
00706                 } else {
00707                         DBGC ( sig, "CMS %p had no signatures\n", sig );
00708                         return -EACCES_NO_SIGNATURES;
00709                 }
00710         }
00711 
00712         return 0;
00713 }