From 772a878182ebb8d86c22dd80f5c7cd3ce118732d Mon Sep 17 00:00:00 2001 From: olszomal Date: Fri, 5 Jun 2020 12:17:19 +0200 Subject: [PATCH] CMS structure support --- osslsigncode.c | 282 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 230 insertions(+), 52 deletions(-) diff --git a/osslsigncode.c b/osslsigncode.c index 8957da6..a29ac05 100644 --- a/osslsigncode.c +++ b/osslsigncode.c @@ -111,6 +111,7 @@ typedef unsigned char u_char; #include #include #include /* X509_PURPOSE */ +#include #include #include #include @@ -1678,6 +1679,43 @@ static int print_time(const ASN1_TIME *time) return 1; /* OK */ } +static time_t ASN1_GetTimeT(ASN1_TIME *time) +{ + struct tm t; + const char *str; + size_t i = 0; + + str = (const char*) time->data; + memset(&t, 0, sizeof(t)); + if (time->type == V_ASN1_UTCTIME) { + /* two digit year */ + t.tm_year = (str[i++] - '0') * 10; + t.tm_year += (str[i++] - '0'); + if (t.tm_year < 70) + t.tm_year += 100; + } else if (time->type == V_ASN1_GENERALIZEDTIME) { + /* four digit year */ + t.tm_year = (str[i++] - '0') * 1000; + t.tm_year+= (str[i++] - '0') * 100; + t.tm_year+= (str[i++] - '0') * 10; + t.tm_year+= (str[i++] - '0'); + t.tm_year -= 1900; + } + t.tm_mon = (str[i++] - '0') * 10; + t.tm_mon += (str[i++] - '0') - 1; /* -1 since January is 0 not 1 */ + t.tm_mday = (str[i++] - '0') * 10; + t.tm_mday+= (str[i++] - '0'); + t.tm_hour = (str[i++] - '0') * 10; + t.tm_hour+= (str[i++] - '0'); + t.tm_min = (str[i++] - '0') * 10; + t.tm_min += (str[i++] - '0'); + t.tm_sec = (str[i++] - '0') * 10; + t.tm_sec += (str[i++] - '0'); + + /* Note: we did not adjust the time based on time zone information */ + return mktime(&t); +} + static int print_cert(X509 *cert, int i) { char *subject, *issuer, *serial; @@ -1713,8 +1751,10 @@ static int find_signer(PKCS7 *p7, char *leafhash, int *leafok) * search only internal certificates if it was requested */ signers = PKCS7_get0_signers(p7, NULL, 0); - if (!signers || sk_X509_num(signers) != 1) + if (!signers || sk_X509_num(signers) != 1) { + fprintf(stderr, "PKCS7_get0_signers error\n"); return 0; /* FAILED */ + } printf("Signer's certificate:\n"); cert = sk_X509_value(signers, 0); if ((cert == NULL) || (!print_cert(cert, 0))) @@ -1831,10 +1871,11 @@ static int set_store_time(X509_STORE *store, time_t time) return 1; /* OK */ } -static ASN1_UTCTIME *print_timestamp(PKCS7_SIGNER_INFO *si) +static time_t pkcs7_print_timestamp(PKCS7_SIGNER_INFO *si) { ASN1_INTEGER *version; - ASN1_UTCTIME *timestamp_time = NULL; + ASN1_UTCTIME *asn1_time = NULL; + time_t posix_time; int md_nid; char *issuer, *serial; BIGNUM *serialbn; @@ -1844,8 +1885,9 @@ static ASN1_UTCTIME *print_timestamp(PKCS7_SIGNER_INFO *si) printf("Version: %ld\nHash Algorithm: %s\n", ASN1_INTEGER_get(version), (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid)); printf("The signature is timestamped: "); - timestamp_time = get_signing_time(si); - print_time(timestamp_time); + asn1_time = get_signing_time(si); + print_time(asn1_time); + posix_time = ASN1_GetTimeT(asn1_time); issuer = X509_NAME_oneline(si->issuer_and_serial->issuer, NULL, 0); serialbn = ASN1_INTEGER_to_BN(si->issuer_and_serial->serial, NULL); serial = BN_bn2hex(serialbn); @@ -1853,10 +1895,49 @@ static ASN1_UTCTIME *print_timestamp(PKCS7_SIGNER_INFO *si) OPENSSL_free(issuer); BN_free(serialbn); OPENSSL_free(serial); - return timestamp_time; /* OK */ + return posix_time; /* OK */ } -static PKCS7 *find_countersignature(PKCS7_SIGNED *p7_signed, const unsigned char *data, int length, ASN1_UTCTIME **timestamp_time) +static time_t cms_print_timestamp(CMS_ContentInfo *cms, CMS_SignerInfo *si) +{ + ASN1_GENERALIZEDTIME *asn1_time = NULL; + time_t posix_time; + int md_nid; + ASN1_OCTET_STRING **pos; + const unsigned char *p = NULL; + TimeStampToken *token = NULL; + ASN1_INTEGER *serialno; + char *issuer_name, *serial; + BIGNUM *serialbn; + X509_ALGOR *pdig; + X509_NAME *issuer; + + CMS_SignerInfo_get0_algs(si, NULL, NULL, &pdig, NULL); + md_nid = OBJ_obj2nid(pdig->algorithm); + printf("Hash Algorithm: %s\n", (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid)); + pos = CMS_get0_content(cms); + if (pos != NULL && *pos != NULL) { + p = (*pos)->data; + token = d2i_TimeStampToken(NULL, &p, (*pos)->length); + asn1_time = token->time; + } + printf("The signature is timestamped: "); + print_time(asn1_time); + posix_time = ASN1_GetTimeT(asn1_time); + CMS_SignerInfo_get0_signer_id(si, NULL, &issuer, &serialno); + issuer_name = X509_NAME_oneline(issuer, NULL, 0); + serialbn = ASN1_INTEGER_to_BN(serialno, NULL); + serial = BN_bn2hex(serialbn); + printf("Timestamp Verified by:\n\t\tIssuer : %s\n\t\tSerial : %s\n", issuer_name, serial); + TimeStampToken_free(token); + OPENSSL_free(issuer_name); + BN_free(serialbn); + OPENSSL_free(serial); + return posix_time; /* OK */ +} + +static PKCS7 *find_countersignature(PKCS7_SIGNED *p7_signed, const unsigned char *data, + int length, time_t *timestamp_time) { PKCS7_SIGNER_INFO *si, *countersignature; PKCS7 *p7 = NULL, *content = NULL; @@ -1897,27 +1978,34 @@ static PKCS7 *find_countersignature(PKCS7_SIGNED *p7_signed, const unsigned char PKCS7_free(content); return NULL; /* FAILED */ } - *timestamp_time = print_timestamp(countersignature); + *timestamp_time = pkcs7_print_timestamp(countersignature); return p7; /* OK */ } -static PKCS7 *find_rfc3161(const unsigned char *data, int length, ASN1_UTCTIME **timestamp_time) +static CMS_ContentInfo *find_rfc3161(const unsigned char *data, int length, + time_t *timestamp_time) { - PKCS7 *p7; - PKCS7_SIGNER_INFO *si; + CMS_ContentInfo *cms; + BIO *in; + STACK_OF(CMS_SignerInfo) *sinfos; + CMS_SignerInfo *si; - p7 = d2i_PKCS7(NULL, &data, length); - if (p7 == NULL) + in = BIO_new(BIO_s_mem()); + OPENSSL_assert(BIO_write(in, data, length) == length); + cms = d2i_CMS_bio(in, NULL); + BIO_free(in); + if (cms == NULL) return NULL; /* FAILED */ - si = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0); + sinfos = CMS_get0_SignerInfos(cms); + si = sk_CMS_SignerInfo_value(sinfos, 0); if (si == NULL) return NULL; /* FAILED */ - *timestamp_time = print_timestamp(si); - return p7; /* OK */ + *timestamp_time = cms_print_timestamp(cms, si); + return cms; /* OK */ } static int pkcs7_print_attributes(PKCS7_SIGNED *p7_signed, PKCS7 **tmstamp_p7, - ASN1_UTCTIME **timestamp_time, int verbose) + CMS_ContentInfo **tmstamp_cms, time_t *timestamp_time, int verbose) { PKCS7_SIGNER_INFO *si; STACK_OF(X509_ATTRIBUTE) *unauth_attr; @@ -1962,8 +2050,8 @@ static int pkcs7_print_attributes(PKCS7_SIGNED *p7_signed, PKCS7 **tmstamp_p7, value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL); if (value == NULL) return 0; /* FAILED */ - *tmstamp_p7 = find_rfc3161(value->data, value->length, timestamp_time); - if (*tmstamp_p7 == NULL) { + *tmstamp_cms = find_rfc3161(value->data, value->length, timestamp_time); + if (*tmstamp_cms == NULL) { printf("Error: RFC3161 Timestamp could not be decoded correctly\n"); ERR_print_errors_fp(stdout); return 0; /* FAILED */ @@ -1994,10 +2082,10 @@ static int pkcs7_print_attributes(PKCS7_SIGNED *p7_signed, PKCS7 **tmstamp_p7, * compare the hash provided from the TSTInfo object against the hash computed * from the signature created by the signing certificate's private key */ -static int TST_verify(PKCS7 *tmstamp_p7, PKCS7_SIGNER_INFO *si) +static int pkcs7_TST_verify(PKCS7 *timestamp, PKCS7_SIGNER_INFO *si) { ASN1_OCTET_STRING *object, *hash; - TimeStampToken *token; + TimeStampToken *token = NULL; const unsigned char *p = NULL; unsigned char mdbuf[EVP_MAX_MD_SIZE]; char hexbuf[EVP_MAX_MD_SIZE*2+1]; @@ -2006,7 +2094,7 @@ static int TST_verify(PKCS7 *tmstamp_p7, PKCS7_SIGNER_INFO *si) int md_nid; /* get id_smime_ct_TSTInfo object for RFC3161 Timestamp */ - object = tmstamp_p7->d.sign->contents->d.other->value.octet_string; + object = timestamp->d.sign->contents->d.other->value.octet_string; p = object->data; token = d2i_TimeStampToken(NULL, &p, object->length); if (token) { @@ -2030,10 +2118,57 @@ static int TST_verify(PKCS7 *tmstamp_p7, PKCS7_SIGNER_INFO *si) tohex(hash->data, hexbuf, hash->length); fprintf(stderr, "\tReceived message digest : %s\n" , hexbuf); printf("File's message digest verification: failed\n"); + TimeStampToken_free(token); return 0; /* FAILED */ } /* else Computed and received message digests matched */ + TimeStampToken_free(token); } /* else Countersignature Timestamp */ - TimeStampToken_free(token); + return 1; /* OK */ +} + +static int cms_TST_verify(CMS_ContentInfo *timestamp, PKCS7_SIGNER_INFO *si) +{ + ASN1_OCTET_STRING *hash, **pos; + TimeStampToken *token = NULL; + const unsigned char *p = NULL; + unsigned char mdbuf[EVP_MAX_MD_SIZE]; + char hexbuf[EVP_MAX_MD_SIZE*2+1]; + const EVP_MD *md; + EVP_MD_CTX *mdctx; + int md_nid; + + pos = CMS_get0_content(timestamp); + if (pos != NULL && *pos != NULL) { + p = (*pos)->data; + token = d2i_TimeStampToken(NULL, &p, (*pos)->length); + } else + return 0; /* FAILED */ + if (token) { + /* compute a hash from the encrypted message digest value of the file */ + md_nid = OBJ_obj2nid(token->messageImprint->digestAlgorithm->algorithm); + md = EVP_get_digestbynid(md_nid); + mdctx = EVP_MD_CTX_new(); + EVP_DigestInit(mdctx, md); + EVP_DigestUpdate(mdctx, si->enc_digest->data, si->enc_digest->length); + EVP_DigestFinal(mdctx, mdbuf, NULL); + EVP_MD_CTX_free(mdctx); + + /* compare the provided hash against the computed hash */ + hash = token->messageImprint->digest; + /* hash->length == EVP_MD_size(md) */ + if (memcmp(mdbuf, hash->data, hash->length)) { + tohex(mdbuf, hexbuf, EVP_MD_size(md)); + fprintf(stderr, "Hash value mismatch:\n\tMessage digest algorithm: %s\n", + (md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid)); + fprintf(stderr, "\tComputed message digest : %s\n", hexbuf); + tohex(hash->data, hexbuf, hash->length); + fprintf(stderr, "\tReceived message digest : %s\n" , hexbuf); + printf("File's message digest verification: failed\n"); + TimeStampToken_free(token); + return 0; /* FAILED */ + } /* else Computed and received message digests matched */ + TimeStampToken_free(token); + } return 1; /* OK */ } @@ -2095,21 +2230,27 @@ static int pkcs7_set_nested_signature(PKCS7 *p7, PKCS7 *p7nest, time_t signing_t return 1; } -static int verify_timestamp(PKCS7 *p7, PKCS7 *tmstamp_p7, GLOBAL_OPTIONS *options) +static int pkcs7_verify_timestamp(PKCS7 *p7, PKCS7 *tmstamp_p7, GLOBAL_OPTIONS *options) { X509_STORE *store = NULL; PKCS7_SIGNER_INFO *si; int ret = 1, verok = 0; + STACK_OF(X509) *signers; store = X509_STORE_new(); - if (load_file_lookup(store, options->untrusted, X509_PURPOSE_TIMESTAMP_SIGN)) + if (load_file_lookup(store, options->untrusted, X509_PURPOSE_ANY)) printf("TSA's certificates file: %s\n", options->untrusted); else { printf("Use the \"-untrusted\" option to add the CA cert bundle to verify timestamp server.\n"); - ret = 0; /* FAILED */ + return 0; /* FAILED */ + } + verok = PKCS7_verify(tmstamp_p7, NULL, store, 0, NULL, 0); + /* check extended key usage flag XKU_TIMESTAMP */ + signers = PKCS7_get0_signers(tmstamp_p7, NULL, 0); + if (!(X509_get_extended_key_usage(sk_X509_value(signers, 0)) & XKU_TIMESTAMP)) { + fprintf(stderr, "Unsupported Signer's certificate purpose XKU_TIMESTAMP\n"); + verok = 0; /* FAILED */ } - if (ret) - verok = PKCS7_verify(tmstamp_p7, NULL, store, 0, NULL, 0); printf("\nTimestamp Server Signature verification: %s\n", verok ? "ok" : "failed"); if (!verok) { ERR_print_errors_fp(stdout); @@ -2117,22 +2258,60 @@ static int verify_timestamp(PKCS7 *p7, PKCS7 *tmstamp_p7, GLOBAL_OPTIONS *option } /* verify the hash provided from the trusted timestamp */ si = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0); - verok = TST_verify(tmstamp_p7, si); + verok = pkcs7_TST_verify(tmstamp_p7, si); if (!verok) { ERR_print_errors_fp(stdout); ret = 0; /* FAILED */ } + sk_X509_free(signers); X509_STORE_free(store); return ret; } -static int verify_authenticode(PKCS7 *p7, ASN1_UTCTIME *timestamp_time, GLOBAL_OPTIONS *options) +static int cms_verify_timestamp(PKCS7 *p7, CMS_ContentInfo *tmstamp_cms, GLOBAL_OPTIONS *options) +{ + X509_STORE *store = NULL; + int ret = 1, verok = 0; + PKCS7_SIGNER_INFO *si; + STACK_OF(X509) *signers; + + store = X509_STORE_new(); + if (load_file_lookup(store, options->untrusted, X509_PURPOSE_ANY)) + printf("TSA's certificates file: %s\n", options->untrusted); + else { + printf("Use the \"-untrusted\" option to add the CA cert bundle to verify timestamp server.\n"); + return 0; /* FAILED */ + } + verok = CMS_verify(tmstamp_cms, NULL, store, 0, NULL, 0); + /* check extended key usage flag XKU_TIMESTAMP */ + signers = CMS_get0_signers(tmstamp_cms); + if (!(X509_get_extended_key_usage(sk_X509_value(signers, 0)) & XKU_TIMESTAMP)) { + fprintf(stderr, "Unsupported Signer's certificate purpose XKU_TIMESTAMP\n"); + verok = 0; /* FAILED */ + } + printf("\nTimestamp Server Signature verification: %s\n", verok ? "ok" : "failed"); + if (!verok) { + ERR_print_errors_fp(stdout); + ret = 0; /* FAILED */ + } + /* verify the hash provided from the trusted timestamp */ + si = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0); + verok = cms_TST_verify(tmstamp_cms, si); + if (!verok) { + ERR_print_errors_fp(stdout); + ret = 0; /* FAILED */ + } + sk_X509_free(signers); + X509_STORE_free(store); + return ret; +} + +static int verify_authenticode(PKCS7 *p7, time_t timestamp_time, GLOBAL_OPTIONS *options) { X509_STORE *store = NULL; int ret = 0, verok = 0; size_t seqhdrlen; BIO *bio = NULL; - int day, sec; STACK_OF(X509) *signers; seqhdrlen = asn1_simple_hdr_len(p7->d.sign->contents->d.other->value.sequence->data, @@ -2145,26 +2324,18 @@ static int verify_authenticode(PKCS7 *p7, ASN1_UTCTIME *timestamp_time, GLOBAL_O fprintf(stderr, "Failed to add store lookup file\n"); ret = 1; /* FAILED */ } - if (timestamp_time) { - ASN1_TIME *ptime; - time_t time = INVALID_TIME; - - ptime = ASN1_TIME_set(NULL, 0); - if (!ASN1_TIME_diff(&day, &sec, ptime, timestamp_time)) - ret = 1; /* FAILED */ - time = 86400*day+sec; - if (!set_store_time(store, time)) { - fprintf(stderr, "Failed to set store time\n"); - ret = 1; /* FAILED */ - } - ASN1_TIME_free(ptime); + if (timestamp_time != INVALID_TIME && !set_store_time(store, timestamp_time)) { + fprintf(stderr, "Failed to set store time\n"); + ret = 1; /* FAILED */ } /* check extended key usage flag XKU_CODE_SIGN */ signers = PKCS7_get0_signers(p7, NULL, 0); - if (!signers || sk_X509_num(signers) != 1) + if (!signers || sk_X509_num(signers) != 1) { + fprintf(stderr, "PKCS7_get0_signers error\n"); ret = 1; /* FAILED */ + } if (!(X509_get_extended_key_usage(sk_X509_value(signers, 0)) & XKU_CODE_SIGN)) { - fprintf(stderr, "Unsupported Signer's certificate purpose\n"); + fprintf(stderr, "Unsupported Signer's certificate purpose XKU_CODE_SIGN\n"); ret = 1; /* FAILED */ } if (!ret) { @@ -2193,14 +2364,15 @@ static int verify_authenticode(PKCS7 *p7, ASN1_UTCTIME *timestamp_time, GLOBAL_O static int verify_pkcs7(PKCS7 *p7, GLOBAL_OPTIONS *options) { PKCS7 *tmstamp_p7 = NULL; - ASN1_UTCTIME *timestamp_time = NULL; + CMS_ContentInfo *tmstamp_cms = NULL; int ret = 0, leafok = 0; + time_t timestamp_time = INVALID_TIME;; if (!find_signer(p7, options->leafhash, &leafok)) printf("Find signers error\n"); if (!print_certs(p7)) printf("Print certs error\n"); - if (!pkcs7_print_attributes(p7->d.sign, &tmstamp_p7, ×tamp_time, options->verbose)) + if (!pkcs7_print_attributes(p7->d.sign, &tmstamp_p7, &tmstamp_cms, ×tamp_time, options->verbose)) printf("Print attributes error\n"); if (options->leafhash != NULL) { printf("Leaf hash match: %s\n", leafok ? "ok" : "failed"); @@ -2210,15 +2382,21 @@ static int verify_pkcs7(PKCS7 *p7, GLOBAL_OPTIONS *options) printf("\nCAfile: %s\n", options->cafile); if (options->crlfile) printf("CRLfile: %s\n", options->crlfile); - if (!tmstamp_p7) + if (tmstamp_p7) { + if (!pkcs7_verify_timestamp(p7, tmstamp_p7, options)) + timestamp_time = INVALID_TIME; + } else if (tmstamp_cms) { + if (!cms_verify_timestamp(p7, tmstamp_cms, options)) + timestamp_time = INVALID_TIME; + } else printf("\nTimestamp is not available\n"); - else if (!verify_timestamp(p7, tmstamp_p7, options)) - timestamp_time = NULL; - ret |= verify_authenticode(p7, timestamp_time, options); if (tmstamp_p7) { PKCS7_free(tmstamp_p7); tmstamp_p7 = NULL; + } else if (tmstamp_cms) { + CMS_ContentInfo_free(tmstamp_cms); + tmstamp_cms = NULL; } return ret; }