From 64e1bba96ba0ec72e8b9efd2e462586805661b00 Mon Sep 17 00:00:00 2001 From: olszomal Date: Tue, 19 Dec 2023 12:59:28 +0100 Subject: [PATCH] Add a new command extract-data to extract a PKCS#7 data content to be signed --- NEWS.md | 1 + appx.c | 55 +++++++++++++- cab.c | 48 +++++++++++-- cat.c | 47 ++++-------- helpers.c | 191 +++++++++++++++++++++++++++++++++++-------------- helpers.h | 9 ++- msi.c | 69 +++++++++++++++--- osslsigncode.c | 85 ++++++++++++---------- osslsigncode.h | 2 + pe.c | 152 ++++++++++++++++++++++++++------------- 10 files changed, 467 insertions(+), 192 deletions(-) diff --git a/NEWS.md b/NEWS.md index 28b8d66..65831be 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,7 @@ - added CAT file verification and listing each member of the CAT file by using the "-verbose" option +- added new command "extract-data" to extract a PKCS#7 data content to be signed ### 2.7 (2023.09.19) diff --git a/appx.c b/appx.c index 2af4f7a..097023c 100644 --- a/appx.c +++ b/appx.c @@ -235,6 +235,7 @@ struct appx_ctx_st { static FILE_FORMAT_CTX *appx_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *outdata); static const EVP_MD *appx_md_get(FILE_FORMAT_CTX *ctx); static ASN1_OBJECT *appx_spc_sip_info_get(u_char **p, int *plen, FILE_FORMAT_CTX *ctx); +static PKCS7 *appx_pkcs7_contents_get(FILE_FORMAT_CTX *ctx, BIO *hash, const EVP_MD *md); static int appx_hash_length_get(FILE_FORMAT_CTX *ctx); static int appx_check_file(FILE_FORMAT_CTX *ctx, int detached); static int appx_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7); @@ -249,6 +250,7 @@ FILE_FORMAT file_format_appx = { .ctx_new = appx_ctx_new, .md_get = appx_md_get, .data_blob_get = appx_spc_sip_info_get, + .pkcs7_contents_get = appx_pkcs7_contents_get, .hash_length_get = appx_hash_length_get, .check_file = appx_check_file, .verify_digests = appx_verify_digests, @@ -349,7 +351,8 @@ static FILE_FORMAT_CTX *appx_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *ou if (options->nest) /* I've not tried using set_nested_signature as signtool won't do this */ printf("Warning: APPX files do not support nesting (multiple signature)\n"); - if (options->cmd == CMD_SIGN || options->cmd==CMD_ATTACH || options->cmd==CMD_ADD) { + if (options->cmd == CMD_SIGN || options->cmd==CMD_ATTACH + || options->cmd==CMD_ADD || options->cmd == CMD_EXTRACT_DATA) { printf("Warning: Ignore -h option, use the hash algorithm specified in AppxBlockMap.xml\n"); } if (options->pagehash == 1) @@ -406,6 +409,41 @@ static ASN1_OBJECT *appx_spc_sip_info_get(u_char **p, int *plen, FILE_FORMAT_CTX return dtype; /* OK */ } +/* + * Allocate and return a data content to be signed. + * [in] ctx: structure holds input and output data + * [in] hash: message digest BIO + * [in] md: message digest algorithm + * [returns] data content + */ +static PKCS7 *appx_pkcs7_contents_get(FILE_FORMAT_CTX *ctx, BIO *hash, const EVP_MD *md) +{ + ASN1_OCTET_STRING *content; + ZIP_CENTRAL_DIRECTORY_ENTRY *entry; + BIO *bhash; + + /* squash unused parameter warnings */ + (void)md; + (void)hash; + + /* Create and append a new signature content types entry */ + entry = zipGetCDEntryByName(ctx->appx_ctx->zip, CONTENT_TYPES_FILENAME); + if (!entry) { + printf("Not a valid .appx file: content types file missing\n"); + return NULL; /* FAILED */ + } + if (!appx_append_ct_signature_entry(ctx->appx_ctx->zip, entry)) { + return NULL; /* FAILED */ + } + bhash = appx_calculate_hashes(ctx); + if (!bhash) { + return NULL; /* FAILED */ + } + content = spc_indirect_data_content_get(bhash, ctx); + BIO_free_all(bhash); + return pkcs7_set_content(content); +} + /* * Get concatenated hashes length. * [in] ctx: structure holds input and output data @@ -596,6 +634,7 @@ static PKCS7 *appx_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) return NULL; /* FAILED */ } } else if (ctx->options->cmd == CMD_SIGN) { + ASN1_OCTET_STRING *content; /* Create hash blob from concatenated APPX hashes */ BIO *hashes = appx_calculate_hashes(ctx); if (!hashes) { @@ -605,19 +644,29 @@ static PKCS7 *appx_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) p7 = pkcs7_create(ctx); if (!p7) { printf("Creating a new signature failed\n"); + BIO_free_all(hashes); return NULL; /* FAILED */ } if (!add_indirect_data_object(p7)) { printf("Adding SPC_INDIRECT_DATA_OBJID failed\n"); + PKCS7_free(p7); BIO_free_all(hashes); + return NULL; /* FAILED */ + } + content = spc_indirect_data_content_get(hashes, ctx); + BIO_free_all(hashes); + if (!content) { + printf("Failed to get spcIndirectDataContent\n"); PKCS7_free(p7); return NULL; /* FAILED */ } - if (!sign_spc_indirect_data_content(p7, hashes, ctx)) { + if (!sign_spc_indirect_data_content(p7, content)) { printf("Failed to set signed content\n"); + PKCS7_free(p7); + ASN1_OCTET_STRING_free(content); return NULL; /* FAILED */ } - BIO_free_all(hashes); + ASN1_OCTET_STRING_free(content); } return p7; /* OK */ } diff --git a/cab.c b/cab.c index 80ef219..3ee7e5e 100644 --- a/cab.c +++ b/cab.c @@ -43,6 +43,7 @@ struct cab_ctx_st { /* FILE_FORMAT method prototypes */ static FILE_FORMAT_CTX *cab_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *outdata); static ASN1_OBJECT *cab_obsolete_link_get(u_char **p, int *plen, FILE_FORMAT_CTX *ctx); +static PKCS7 *cab_pkcs7_contents_get(FILE_FORMAT_CTX *ctx, BIO *hash, const EVP_MD *md); static int cab_hash_length_get(FILE_FORMAT_CTX *ctx); static int cab_check_file(FILE_FORMAT_CTX *ctx, int detached); static u_char *cab_digest_calc(FILE_FORMAT_CTX *ctx, const EVP_MD *md); @@ -58,6 +59,7 @@ static void cab_ctx_cleanup(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata); FILE_FORMAT file_format_cab = { .ctx_new = cab_ctx_new, .data_blob_get = cab_obsolete_link_get, + .pkcs7_contents_get = cab_pkcs7_contents_get, .hash_length_get = cab_hash_length_get, .check_file = cab_check_file, .digest_calc = cab_digest_calc, @@ -151,6 +153,32 @@ static ASN1_OBJECT *cab_obsolete_link_get(u_char **p, int *plen, FILE_FORMAT_CTX return dtype; /* OK */ } +/* + * Allocate and return a data content to be signed. + * [in] ctx: structure holds input and output data + * [in] hash: message digest BIO + * [in] md: message digest algorithm + * [returns] data content + */ +static PKCS7 *cab_pkcs7_contents_get(FILE_FORMAT_CTX *ctx, BIO *hash, const EVP_MD *md) +{ + ASN1_OCTET_STRING *content; + + /* squash the unused parameter warning, use initialized message digest BIO */ + (void)md; + + /* Strip current signature and modify header */ + if (ctx->cab_ctx->header_size == 20) { + if (!cab_modify_header(ctx, hash, NULL)) + return NULL; /* FAILED */ + } else { + if (!cab_add_header(ctx, hash, NULL)) + return NULL; /* FAILED */ + } + content = spc_indirect_data_content_get(hash, ctx); + return pkcs7_set_content(content); +} + /* * [in] ctx: structure holds input and output data * [returns] the size of the message digest when passed an EVP_MD structure (the size of the hash) @@ -363,11 +391,14 @@ static int cab_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7) */ static PKCS7 *cab_pkcs7_extract(FILE_FORMAT_CTX *ctx) { + const u_char *blob; + if (ctx->cab_ctx->sigpos == 0 || ctx->cab_ctx->siglen == 0 || ctx->cab_ctx->sigpos > ctx->cab_ctx->fileend) { return NULL; /* FAILED */ } - return pkcs7_get(ctx->options->indata, ctx->cab_ctx->sigpos, ctx->cab_ctx->siglen); + blob = (u_char *)ctx->options->indata + ctx->cab_ctx->sigpos; + return d2i_PKCS7(NULL, &blob, ctx->cab_ctx->siglen); } /* @@ -470,7 +501,7 @@ static PKCS7 *cab_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) if ((ctx->options->cmd == CMD_SIGN && ctx->options->nest) || (ctx->options->cmd == CMD_ATTACH && ctx->options->nest) || ctx->options->cmd == CMD_ADD) { - cursig = pkcs7_get(ctx->options->indata, ctx->cab_ctx->sigpos, ctx->cab_ctx->siglen); + cursig = cab_pkcs7_extract(ctx); if (!cursig) { printf("Unable to extract existing signature\n"); return NULL; /* FAILED */ @@ -487,6 +518,7 @@ static PKCS7 *cab_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) return NULL; /* FAILED */ } } else if (ctx->options->cmd == CMD_SIGN) { + ASN1_OCTET_STRING *content; /* Create a new PKCS#7 signature */ p7 = pkcs7_create(ctx); if (!p7) { @@ -503,10 +535,18 @@ static PKCS7 *cab_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) PKCS7_free(p7); return NULL; /* FAILED */ } - if (!sign_spc_indirect_data_content(p7, hash, ctx)) { - printf("Failed to set signed content\n"); + content = spc_indirect_data_content_get(hash, ctx); + if (!content) { + printf("Failed to get spcIndirectDataContent\n"); return NULL; /* FAILED */ } + if (!sign_spc_indirect_data_content(p7, content)) { + printf("Failed to set signed content\n"); + PKCS7_free(p7); + ASN1_OCTET_STRING_free(content); + return NULL; /* FAILED */ + } + ASN1_OCTET_STRING_free(content); } if (ctx->options->nest) ctx->options->prevsig = cursig; diff --git a/cat.c b/cat.c index 8df9ac0..160492c 100644 --- a/cat.c +++ b/cat.c @@ -10,11 +10,6 @@ #include "osslsigncode.h" #include "helpers.h" -const u_char pkcs7_signed_data[] = { - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x07, 0x02, -}; - typedef struct { ASN1_BMPSTRING *tag; ASN1_INTEGER *flags; @@ -86,7 +81,7 @@ static FILE_FORMAT_CTX *cat_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *out CAT_CTX *cat_ctx; uint32_t filesize; - if (options->cmd == CMD_REMOVE || options->cmd==CMD_ATTACH) { + if (options->cmd == CMD_REMOVE || options->cmd==CMD_ATTACH || options->cmd == CMD_EXTRACT_DATA) { printf("Unsupported command\n"); return NULL; /* FAILED */ } @@ -98,12 +93,6 @@ static FILE_FORMAT_CTX *cat_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *out if (!options->indata) { return NULL; /* FAILED */ } - /* the maximum size of a supported cat file is (2^24 -1) bytes */ - if (memcmp(options->indata + ((GET_UINT8_LE(options->indata+1) == 0x82) ? 4 : 5), - pkcs7_signed_data, sizeof pkcs7_signed_data)) { - unmap_file(options->indata, filesize); - return NULL; /* FAILED */ - } cat_ctx = cat_ctx_get(options->indata, filesize); if (!cat_ctx) { unmap_file(options->indata, filesize); @@ -200,7 +189,7 @@ static PKCS7 *cat_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) /* Obtain an existing signature */ if (ctx->options->cmd == CMD_ADD || ctx->options->cmd == CMD_ATTACH) { - p7 = PKCS7_dup(ctx->cat_ctx->p7); + p7 = cat_pkcs7_extract(ctx); } else if (ctx->options->cmd == CMD_SIGN) { /* Create a new signature */ p7 = pkcs7_create(ctx); @@ -224,29 +213,14 @@ static PKCS7 *cat_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) /* * Append signature to the outfile. - * [in, out] ctx: structure holds input and output data (unused) + * [in, out] ctx: structure holds input and output data * [out] outdata: outdata file BIO * [in] p7: PKCS#7 signature * [returns] 1 on error or 0 on success */ static int cat_append_pkcs7(FILE_FORMAT_CTX *ctx, BIO *outdata, PKCS7 *p7) { - u_char *p = NULL; - int len; /* signature length */ - - /* squash the unused parameter warning */ - (void)ctx; - - if (((len = i2d_PKCS7(p7, NULL)) <= 0) - || (p = OPENSSL_malloc((size_t)len)) == NULL) { - printf("i2d_PKCS memory allocation failed: %d\n", len); - return 1; /* FAILED */ - } - i2d_PKCS7(p7, &p); - p -= len; - i2d_PKCS7_bio(outdata, p7); - OPENSSL_free(p); - return 0; /* OK */ + return data_write_pkcs7(ctx, outdata, p7); } /* @@ -288,18 +262,23 @@ static void cat_ctx_cleanup(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) */ /* - * Verify mapped CAT file and create CAT format specific structure. - * [in] indata: mapped CAT file (unused) - * [in] filesize: size of CAT file + * Verify mapped PKCS#7 (CAT) file and create CAT format specific structure. + * [in] indata: mapped file + * [in] filesize: size of file * [returns] pointer to CAT format specific structure */ static CAT_CTX *cat_ctx_get(char *indata, uint32_t filesize) { CAT_CTX *cat_ctx; - PKCS7 *p7 = pkcs7_get(indata, 0, filesize); + PKCS7 *p7; + p7 = pkcs7_read_data(indata, filesize); if (!p7) return NULL; /* FAILED */ + if (!PKCS7_type_is_signed(p7)) { + PKCS7_free(p7); + return NULL; /* FAILED */ + } cat_ctx = OPENSSL_zalloc(sizeof(CAT_CTX)); cat_ctx->p7 = p7; cat_ctx->sigpos = 0; diff --git a/helpers.c b/helpers.c index 2cc45e5..2ad6d88 100644 --- a/helpers.c +++ b/helpers.c @@ -10,7 +10,7 @@ /* Prototypes */ static SpcSpOpusInfo *spc_sp_opus_info_create(FILE_FORMAT_CTX *ctx); -static int spc_indirect_data_content_get(u_char **blob, int *len, FILE_FORMAT_CTX *ctx); +static int spc_indirect_data_content_create(u_char **blob, int *len, FILE_FORMAT_CTX *ctx); static int pkcs7_signer_info_add_spc_sp_opus_info(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx); static int pkcs7_signer_info_add_purpose(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx); static STACK_OF(X509) *X509_chain_get_sorted(FILE_FORMAT_CTX *ctx, int signer); @@ -138,8 +138,6 @@ PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx) PKCS7 *p7 = NULL; uint32_t filesize; char *indata; - BIO *bio; - const char pemhdr[] = "-----BEGIN PKCS7-----"; filesize = get_file_size(ctx->options->sigfile); if (!filesize) { @@ -150,18 +148,58 @@ PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx) printf("Failed to open file: %s\n", ctx->options->sigfile); return NULL; /* FAILED */ } - bio = BIO_new_mem_buf(indata, (int)filesize); - if (filesize >= sizeof pemhdr && !memcmp(indata, pemhdr, sizeof pemhdr - 1)) { + p7 = pkcs7_read_data(indata, filesize); + unmap_file(indata, filesize); + return p7; +} + +/* + * Retrieve a decoded PKCS#7 structure + * [in] data: encoded PEM or DER data + * [in] size: data size + * [returns] pointer to PKCS#7 structure + */ +PKCS7 *pkcs7_read_data(char *data, uint32_t size) +{ + PKCS7 *p7 = NULL; + BIO *bio; + const char pemhdr[] = "-----BEGIN PKCS7-----"; + + bio = BIO_new_mem_buf(data, (int)size); + if (size >= sizeof pemhdr && !memcmp(data, pemhdr, sizeof pemhdr - 1)) { /* PEM format */ p7 = PEM_read_bio_PKCS7(bio, NULL, NULL, NULL); } else { /* DER format */ p7 = d2i_PKCS7_bio(bio, NULL); } BIO_free_all(bio); - unmap_file(indata, filesize); return p7; } +/* + * [in, out] ctx: structure holds input and output data + * [out] outdata: BIO outdata file + * [in] p7: PKCS#7 signature + * [returns] 1 on error or 0 on success + */ +int data_write_pkcs7(FILE_FORMAT_CTX *ctx, BIO *outdata, PKCS7 *p7) +{ + int ret; + + (void)BIO_reset(outdata); + if (ctx->options->output_pkcs7) { + /* PEM format */ + ret = !PEM_write_bio_PKCS7(outdata, p7); + } else { + /* default DER format */ + ret = !i2d_PKCS7_bio(outdata, p7); + } + if (ret) { + printf("Unable to write pkcs7 object\n"); + } + return ret; +} + /* * Allocate, set type, add content and return a new PKCS#7 signature * [in] ctx: structure holds input and output data @@ -262,47 +300,32 @@ int add_indirect_data_object(PKCS7 *p7) * The spcIndirectDataContent structure is used in Authenticode signatures * to store the digest and other attributes of the signed file. * [in, out] p7: new PKCS#7 signature - * [in] hash: message digest BIO - * [in] ctx: structure holds input and output data + * [in] content: spcIndirectDataContent * [returns] 0 on error or 1 on success */ -int sign_spc_indirect_data_content(PKCS7 *p7, BIO *hash, FILE_FORMAT_CTX *ctx) +int sign_spc_indirect_data_content(PKCS7 *p7, ASN1_OCTET_STRING *content) { - u_char mdbuf[5 * EVP_MAX_MD_SIZE + 24]; - int mdlen, seqhdrlen, hashlen; + int len, hdrlen; + const u_char *data; PKCS7 *td7; - u_char *p = NULL; - int len = 0; - u_char *buf; - hashlen = ctx->format->hash_length_get(ctx); - if (hashlen > EVP_MAX_MD_SIZE) { - /* APPX format specific */ - mdlen = BIO_read(hash, (char*)mdbuf, hashlen); - } else { - mdlen = BIO_gets(hash, (char*)mdbuf, EVP_MAX_MD_SIZE); - } - if (!spc_indirect_data_content_get(&p, &len, ctx)) - return 0; /* FAILED */ + data = ASN1_STRING_get0_data(content); + len = ASN1_STRING_length(content); + hdrlen = ASN1_object_size(0, len, V_ASN1_SEQUENCE) - len; - buf = OPENSSL_malloc(SIZE_64K); - memcpy(buf, p, (size_t)len); - OPENSSL_free(p); - memcpy(buf + len, mdbuf, (size_t)mdlen); - seqhdrlen = asn1_simple_hdr_len(buf, len); - - if (!pkcs7_sign_content(p7, buf + seqhdrlen, len - seqhdrlen + mdlen)) { - printf("Failed to sign content\n"); - OPENSSL_free(buf); + if (!pkcs7_sign_content(p7, data + hdrlen, len - hdrlen)) { + printf("Failed to sign spcIndirectDataContent\n"); return 0; /* FAILED */ } td7 = PKCS7_new(); + if (!td7) { + return 0; /* FAILED */ + } td7->type = OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1); td7->d.other = ASN1_TYPE_new(); td7->d.other->type = V_ASN1_SEQUENCE; td7->d.other->value.sequence = ASN1_STRING_new(); - ASN1_STRING_set(td7->d.other->value.sequence, buf, len + mdlen); - OPENSSL_free(buf); + ASN1_STRING_set(td7->d.other->value.sequence, data, len); if (!PKCS7_set_content(p7, td7)) { printf("PKCS7_set_content failed\n"); PKCS7_free(td7); @@ -311,13 +334,92 @@ int sign_spc_indirect_data_content(PKCS7 *p7, BIO *hash, FILE_FORMAT_CTX *ctx) return 1; /* OK */ } +/* + * Add encapsulated content to signed PKCS7 structure. + * [in] content: spcIndirectDataContent + * [returns] new PKCS#7 signature with encapsulated content + */ +PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content) +{ + PKCS7 *p7, *td7; + + p7 = PKCS7_new(); + if (!p7) { + return NULL; /* FAILED */ + } + if (!PKCS7_set_type(p7, NID_pkcs7_signed)) { + PKCS7_free(p7); + return NULL; /* FAILED */ + } + if (!PKCS7_content_new(p7, NID_pkcs7_data)) { + PKCS7_free(p7); + return NULL; /* FAILED */ + } + td7 = PKCS7_new(); + if (!td7) { + PKCS7_free(p7); + return NULL; /* FAILED */ + } + td7->type = OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1); + td7->d.other = ASN1_TYPE_new(); + td7->d.other->type = V_ASN1_SEQUENCE; + td7->d.other->value.sequence = content; + if (!PKCS7_set_content(p7, td7)) { + PKCS7_free(td7); + PKCS7_free(p7); + return NULL; /* FAILED */ + } + return p7; +} + +/* + * Return spcIndirectDataContent. + * [in] hash: message digest BIO + * [in] ctx: structure holds input and output data + * [returns] content + */ +ASN1_OCTET_STRING *spc_indirect_data_content_get(BIO *hash, FILE_FORMAT_CTX *ctx) +{ + ASN1_OCTET_STRING *content; + u_char mdbuf[5 * EVP_MAX_MD_SIZE + 24]; + int mdlen, hashlen, len = 0; + u_char *data, *p = NULL; + + content = ASN1_OCTET_STRING_new(); + if (!content) { + return NULL; /* FAILED */ + } + if (!spc_indirect_data_content_create(&p, &len, ctx)) { + ASN1_OCTET_STRING_free(content); + return NULL; /* FAILED */ + } + hashlen = ctx->format->hash_length_get(ctx); + if (hashlen > EVP_MAX_MD_SIZE) { + /* APPX format specific */ + mdlen = BIO_read(hash, (char*)mdbuf, hashlen); + } else { + mdlen = BIO_gets(hash, (char*)mdbuf, EVP_MAX_MD_SIZE); + } + data = OPENSSL_malloc((size_t)(len + mdlen)); + memcpy(data, p, (size_t)len); + OPENSSL_free(p); + memcpy(data + len, mdbuf, (size_t)mdlen); + if (!ASN1_OCTET_STRING_set(content, data, len + mdlen)) { + ASN1_OCTET_STRING_free(content); + OPENSSL_free(data); + return NULL; /* FAILED */ + } + OPENSSL_free(data); + return content; +} + /* * Signs the data and place the signature in p7 * [in, out] p7: new PKCS#7 signature * [in] data: content data * [in] len: content length */ -int pkcs7_sign_content(PKCS7 *p7, u_char *data, int len) +int pkcs7_sign_content(PKCS7 *p7, const u_char *data, int len) { BIO *p7bio; @@ -472,23 +574,6 @@ SpcLink *spc_link_obsolete_get(void) return link; } -/* - * Retrieve a decoded PKCS#7 structure - * [in] indata: mapped file - * [in] sigpos: signature data offset - * [in] siglen: signature data size - * [returns] pointer to PKCS#7 structure - */ -PKCS7 *pkcs7_get(char *indata, uint32_t sigpos, uint32_t siglen) -{ - PKCS7 *p7 = NULL; - const u_char *blob; - - blob = (u_char *)indata + sigpos; - p7 = d2i_PKCS7(NULL, &blob, siglen); - return p7; -} - /* * [in] mdbuf, cmdbuf: message digests * [in] mdtype: message digest algorithm type @@ -539,7 +624,7 @@ static SpcSpOpusInfo *spc_sp_opus_info_create(FILE_FORMAT_CTX *ctx) * [in] ctx: FILE_FORMAT_CTX structure * [returns] 0 on error or 1 on success */ -static int spc_indirect_data_content_get(u_char **blob, int *len, FILE_FORMAT_CTX *ctx) +static int spc_indirect_data_content_create(u_char **blob, int *len, FILE_FORMAT_CTX *ctx) { u_char *p = NULL; int mdtype, hashlen, l = 0; diff --git a/helpers.h b/helpers.h index 32b8c30..983e09c 100644 --- a/helpers.h +++ b/helpers.h @@ -11,10 +11,14 @@ char *map_file(const char *infile, const size_t size); void unmap_file(char *indata, const size_t size); int pkcs7_signer_info_add_signing_time(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx); PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx); +PKCS7 *pkcs7_read_data(char *indata, uint32_t size); +int data_write_pkcs7(FILE_FORMAT_CTX *ctx, BIO *outdata, PKCS7 *p7); PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx); int add_indirect_data_object(PKCS7 *p7); -int sign_spc_indirect_data_content(PKCS7 *p7, BIO *hash, FILE_FORMAT_CTX *ctx); -int pkcs7_sign_content(PKCS7 *p7, u_char *data, int len); +int sign_spc_indirect_data_content(PKCS7 *p7, ASN1_OCTET_STRING *content); +PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content); +ASN1_OCTET_STRING *spc_indirect_data_content_get(BIO *hash, FILE_FORMAT_CTX *ctx); +int pkcs7_sign_content(PKCS7 *p7, const u_char *data, int len); int asn1_simple_hdr_len(const u_char *p, int len); int bio_hash_data(BIO *hash, char *indata, size_t idx, size_t fileend); void print_hash(const char *descript1, const char *descript2, const u_char *hashbuf, int length); @@ -22,7 +26,6 @@ int is_content_type(PKCS7 *p7, const char *objid); MsCtlContent *ms_ctl_content_get(PKCS7 *p7); ASN1_TYPE *catalog_content_get(CatalogAuthAttr *attribute); SpcLink *spc_link_obsolete_get(void); -PKCS7 *pkcs7_get(char *indata, uint32_t sigpos, uint32_t siglen); int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype); /* diff --git a/msi.c b/msi.c index 62a9612..7b1b1ac 100644 --- a/msi.c +++ b/msi.c @@ -215,6 +215,7 @@ struct msi_ctx_st { /* FILE_FORMAT method prototypes */ static FILE_FORMAT_CTX *msi_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *outdata); static ASN1_OBJECT *msi_spc_sip_info_get(u_char **p, int *plen, FILE_FORMAT_CTX *ctx); +static PKCS7 *msi_pkcs7_contents_get(FILE_FORMAT_CTX *ctx, BIO *hash, const EVP_MD *md); static int msi_hash_length_get(FILE_FORMAT_CTX *ctx); static int msi_check_file(FILE_FORMAT_CTX *ctx, int detached); static u_char *msi_digest_calc(FILE_FORMAT_CTX *ctx, const EVP_MD *md); @@ -229,6 +230,7 @@ static void msi_ctx_cleanup(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata); FILE_FORMAT file_format_msi = { .ctx_new = msi_ctx_new, .data_blob_get = msi_spc_sip_info_get, + .pkcs7_contents_get = msi_pkcs7_contents_get, .hash_length_get = msi_hash_length_get, .check_file = msi_check_file, .digest_calc = msi_digest_calc, @@ -251,6 +253,7 @@ static int msi_file_write(MSI_FILE *msi, MSI_DIRENT *dirent, u_char *p_msi, uint static MSI_ENTRY *msi_signatures_get(MSI_DIRENT *dirent, MSI_ENTRY **dse); static int msi_file_read(MSI_FILE *msi, MSI_ENTRY *entry, uint32_t offset, char *buffer, uint32_t len); static int msi_dirent_delete(MSI_DIRENT *dirent, const u_char *name, uint16_t nameLen); +static BIO *msi_digest_calc_bio(FILE_FORMAT_CTX *ctx, BIO *hash); static int msi_calc_MsiDigitalSignatureEx(FILE_FORMAT_CTX *ctx, BIO *hash); static int msi_check_MsiDigitalSignatureEx(FILE_FORMAT_CTX *ctx, MSI_ENTRY *dse, PKCS7 *p7); static int msi_hash_dir(MSI_FILE *msi, MSI_DIRENT *dirent, BIO *hash, int is_root); @@ -348,6 +351,32 @@ static ASN1_OBJECT *msi_spc_sip_info_get(u_char **p, int *plen, FILE_FORMAT_CTX return dtype; /* OK */ } +/* + * Allocate and return a data content to be signed. + * [in] ctx: structure holds input and output data + * [in] hash: message digest BIO + * [in] md: message digest algorithm + * [returns] data content + */ +static PKCS7 *msi_pkcs7_contents_get(FILE_FORMAT_CTX *ctx, BIO *hash, const EVP_MD *md) +{ + ASN1_OCTET_STRING *content; + + /* squash the unused parameter warning, use initialized message digest BIO */ + (void)md; + + if (ctx->options->add_msi_dse && !msi_calc_MsiDigitalSignatureEx(ctx, hash)) { + printf("Unable to calc MsiDigitalSignatureEx\n"); + return NULL; /* FAILED */ + } + if (!msi_hash_dir(ctx->msi_ctx->msi, ctx->msi_ctx->dirent, hash, 1)) { + printf("Unable to msi_handle_dir()\n"); + return NULL; /* FAILED */ + } + content = spc_indirect_data_content_get(hash, ctx); + return pkcs7_set_content(content); +} + /* * [in] ctx: structure holds input and output data * [returns] the size of the message digest when passed an EVP_MD structure (the size of the hash) @@ -616,12 +645,8 @@ static PKCS7 *msi_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) /* squash the unused parameter warning */ (void)outdata; - if (ctx->options->add_msi_dse && !msi_calc_MsiDigitalSignatureEx(ctx, hash)) { - printf("Unable to calc MsiDigitalSignatureEx\n"); - return NULL; /* FAILED */ - } - if (!msi_hash_dir(ctx->msi_ctx->msi, ctx->msi_ctx->dirent, hash, 1)) { - printf("Unable to msi_handle_dir()\n"); + hash = msi_digest_calc_bio(ctx, hash); + if (!hash) { return NULL; /* FAILED */ } /* Obtain a current signature from previously-signed file */ @@ -663,6 +688,7 @@ static PKCS7 *msi_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) return NULL; /* FAILED */ } } else if (ctx->options->cmd == CMD_SIGN) { + ASN1_OCTET_STRING *content; /* Create a new PKCS#7 signature */ p7 = pkcs7_create(ctx); if (!p7) { @@ -674,10 +700,18 @@ static PKCS7 *msi_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) PKCS7_free(p7); return NULL; /* FAILED */ } - if (!sign_spc_indirect_data_content(p7, hash, ctx)) { - printf("Failed to set signed content\n"); + content = spc_indirect_data_content_get(hash, ctx); + if (!content) { + printf("Failed to get spcIndirectDataContent\n"); return NULL; /* FAILED */ } + if (!sign_spc_indirect_data_content(p7, content)) { + printf("Failed to set signed content\n"); + PKCS7_free(p7); + ASN1_OCTET_STRING_free(content); + return NULL; /* FAILED */ + } + ASN1_OCTET_STRING_free(content); } if (ctx->options->nest) ctx->options->prevsig = cursig; @@ -2176,6 +2210,25 @@ out: return ret; } +/* + * Compute a message digest value of a signed or unsigned MSI file. + * [in] ctx: structure holds input and output data + * [in] md: message digest algorithm + * [returns] calculated message digest BIO + */ +static BIO *msi_digest_calc_bio(FILE_FORMAT_CTX *ctx, BIO *hash) +{ + if (ctx->options->add_msi_dse && !msi_calc_MsiDigitalSignatureEx(ctx, hash)) { + printf("Unable to calc MsiDigitalSignatureEx\n"); + return NULL; /* FAILED */ + } + if (!msi_hash_dir(ctx->msi_ctx->msi, ctx->msi_ctx->dirent, hash, 1)) { + printf("Unable to msi_handle_dir()\n"); + return NULL; /* FAILED */ + } + return hash; +} + /* * MsiDigitalSignatureEx is an enhanced signature type that * can be used when signing MSI files. In addition to diff --git a/osslsigncode.c b/osslsigncode.c index c24e9ac..708cef6 100644 --- a/osslsigncode.c +++ b/osslsigncode.c @@ -2597,30 +2597,6 @@ static int verify_signed_file(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options) return ret; } -/* - * [in, out] ctx: structure holds input and output data - * [out] outdata: BIO outdata file - * [in] p7: PKCS#7 signature - * [returns] 1 on error or 0 on success - */ -static int save_extracted_pkcs7(FILE_FORMAT_CTX *ctx, BIO *outdata, PKCS7 *p7) -{ - int ret; - - (void)BIO_reset(outdata); - if (ctx->options->output_pkcs7) { - /* PEM format */ - ret = !PEM_write_bio_PKCS7(outdata, p7); - } else { - /* default DER format */ - ret = !i2d_PKCS7_bio(outdata, p7); - } - if (ret) { - printf("Unable to write pkcs7 object\n"); - } - return ret; -} - /* * [in] options: structure holds the input data * [returns] 1 on error or 0 on success @@ -2700,6 +2676,7 @@ static void usage(const char *argv0, const char *cmd) { const char *cmds_all[] = {"all", NULL}; const char *cmds_sign[] = {"all", "sign", NULL}; + const char *cmds_extract_data[] = {"all", "extract-data", NULL}; const char *cmds_add[] = {"all", "add", NULL}; const char *cmds_attach[] = {"all", "attach-signature", NULL}; const char *cmds_extract[] = {"all", "extract-signature", NULL}; @@ -2738,8 +2715,16 @@ static void usage(const char *argv0, const char *cmd) printf("%12s[ -nest ]\n", ""); printf("%12s[ -verbose ]\n", ""); printf("%12s[ -add-msi-dse ]\n", ""); + printf("%12s[ -pem ]\n", ""); printf("%12s[ -in ] [-out ] \n\n", ""); } + if (on_list(cmd, cmds_extract_data)) { + printf("%1sextract-data [ -pem ]\n", ""); + printf("%12s[ -h {md5,sha1,sha2(56),sha384,sha512} ]\n", ""); + printf("%12s[ -ph ]\n", ""); + printf("%12s[ -add-msi-dse ]\n", ""); + printf("%12s[ -in ] [ -out ] \n\n", ""); + } if (on_list(cmd, cmds_add)) { printf("%1sadd [-addUnauthenticatedBlob]\n", ""); #ifdef ENABLE_CURL @@ -2798,9 +2783,10 @@ static void help_for(const char *argv0, const char *cmd) const char *cmds_extract[] = {"extract-signature", NULL}; const char *cmds_remove[] = {"remove-signature", NULL}; const char *cmds_sign[] = {"sign", NULL}; + const char *cmds_extract_data[] = {"extract-data", NULL}; const char *cmds_verify[] = {"verify", NULL}; const char *cmds_ac[] = {"sign", NULL}; - const char *cmds_add_msi_dse[] = {"add", "attach-signature", "sign", NULL}; + const char *cmds_add_msi_dse[] = {"add", "attach-signature", "sign", "extract-data", NULL}; const char *cmds_addUnauthenticatedBlob[] = {"sign", "add", NULL}; #ifdef PROVIDE_ASKPASS const char *cmds_askpass[] = {"sign", NULL}; @@ -2811,9 +2797,10 @@ static void help_for(const char *argv0, const char *cmd) const char *cmds_comm[] = {"sign", NULL}; const char *cmds_CRLfile[] = {"attach-signature", "verify", NULL}; const char *cmds_CRLfileTSA[] = {"attach-signature", "verify", NULL}; - const char *cmds_h[] = {"add", "attach-signature", "sign", NULL}; + const char *cmds_h[] = {"add", "attach-signature", "sign", "extract-data", NULL}; const char *cmds_i[] = {"sign", NULL}; - const char *cmds_in[] = {"add", "attach-signature", "extract-signature", "remove-signature", "sign", "verify", NULL}; + const char *cmds_in[] = {"add", "attach-signature", "extract-signature", + "remove-signature", "sign", "extract-data", "verify", NULL}; const char *cmds_jp[] = {"sign", NULL}; const char *cmds_key[] = {"sign", NULL}; #if OPENSSL_VERSION_NUMBER>=0x30000000L @@ -2824,13 +2811,14 @@ static void help_for(const char *argv0, const char *cmd) #ifdef ENABLE_CURL const char *cmds_noverifypeer[] = {"add", "sign", NULL}; #endif /* ENABLE_CURL */ - const char *cmds_out[] = {"add", "attach-signature", "extract-signature", "remove-signature", "sign", NULL}; + const char *cmds_out[] = {"add", "attach-signature", "extract-signature", + "remove-signature", "sign", "extract-data", NULL}; #ifdef ENABLE_CURL const char *cmds_p[] = {"add", "sign", NULL}; #endif /* ENABLE_CURL */ const char *cmds_pass[] = {"sign", NULL}; - const char *cmds_pem[] = {"extract-signature", NULL}; - const char *cmds_ph[] = {"sign", NULL}; + const char *cmds_pem[] = {"sign", "extract-data", "extract-signature", NULL}; + const char *cmds_ph[] = {"sign", "extract-data", NULL}; const char *cmds_pkcs11cert[] = {"sign", NULL}; const char *cmds_pkcs11engine[] = {"sign", NULL}; const char *cmds_pkcs11module[] = {"sign", NULL}; @@ -2893,6 +2881,10 @@ static void help_for(const char *argv0, const char *cmd) printf("parameters and to select the signing certificate you wish to use.\n\n"); printf("Options:\n"); } + if (on_list(cmd, cmds_extract_data)) { + printf("\nUse the \"extract-data\" command to extract a data content to be signed.\n\n"); + printf("Options:\n"); + } if (on_list(cmd, cmds_verify)) { printf("\nUse the \"verify\" command to verify embedded signatures.\n"); printf("Verification determines if the signing certificate was issued by a trusted party,\n"); @@ -2957,7 +2949,7 @@ static void help_for(const char *argv0, const char *cmd) if (on_list(cmd, cmds_pass)) printf("%-24s= the private key password\n", "-pass"); if (on_list(cmd, cmds_pem)) - printf("%-24s= output data format PEM to use (default: DER)\n", "-pem"); + printf("%-24s= PKCS#7 output data format PEM to use (default: DER)\n", "-pem"); if (on_list(cmd, cmds_ph)) printf("%-24s= generate page hashes for executable files\n", "-ph"); if (on_list(cmd, cmds_pkcs11cert)) @@ -3579,6 +3571,8 @@ static cmd_type_t get_command(char **argv) return CMD_HELP; } else if (!strcmp(argv[1], "sign")) return CMD_SIGN; + else if (!strcmp(argv[1], "extract-data")) + return CMD_EXTRACT_DATA; else if (!strcmp(argv[1], "extract-signature")) return CMD_EXTRACT; else if (!strcmp(argv[1], "attach-signature")) @@ -3731,7 +3725,8 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options) return 0; /* FAILED */ } options->pkcs12file = *(++argv); - } else if ((cmd == CMD_EXTRACT) && !strcmp(*argv, "-pem")) { + } else if ((cmd == CMD_SIGN || cmd == CMD_EXTRACT || cmd == CMD_EXTRACT_DATA) + && !strcmp(*argv, "-pem")) { options->output_pkcs7 = 1; #ifndef OPENSSL_NO_ENGINE } else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-pkcs11cert")) { @@ -3788,7 +3783,7 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options) options->readpass = *(++argv); } else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-comm")) { options->comm = 1; - } else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-ph")) { + } else if ((cmd == CMD_SIGN || cmd == CMD_EXTRACT_DATA) && !strcmp(*argv, "-ph")) { options->pagehash = 1; } else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-n")) { if (--argc < 1) { @@ -3796,8 +3791,8 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options) return 0; /* FAILED */ } options->desc = *(++argv); - } else if ((cmd == CMD_SIGN|| cmd == CMD_ADD || cmd == CMD_ATTACH) - && !strcmp(*argv, "-h")) { + } else if ((cmd == CMD_SIGN || cmd == CMD_ADD || cmd == CMD_ATTACH + || cmd == CMD_EXTRACT_DATA) && !strcmp(*argv, "-h")) { if (--argc < 1) { usage(argv0, "all"); return 0; /* FAILED */ @@ -3860,7 +3855,8 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options) options->ignore_timestamp = 1; } else if ((cmd == CMD_SIGN || cmd == CMD_ADD || cmd == CMD_VERIFY) && !strcmp(*argv, "-verbose")) { options->verbose = 1; - } else if ((cmd == CMD_SIGN || cmd == CMD_ADD || cmd == CMD_ATTACH) && !strcmp(*argv, "-add-msi-dse")) { + } else if ((cmd == CMD_SIGN || cmd == CMD_EXTRACT_DATA || cmd == CMD_ADD || cmd == CMD_ATTACH) + && !strcmp(*argv, "-add-msi-dse")) { options->add_msi_dse = 1; } else if ((cmd == CMD_VERIFY) && (!strcmp(*argv, "-c") || !strcmp(*argv, "-catalog"))) { if (--argc < 1) { @@ -3938,6 +3934,10 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options) help_for(argv0, "sign"); cmd = CMD_HELP; return 0; /* FAILED */ + } else if ((cmd == CMD_EXTRACT_DATA) && !strcmp(*argv, "--help")) { + help_for(argv0, "extract-data"); + cmd = CMD_HELP; + return 0; /* FAILED */ } else if ((cmd == CMD_VERIFY) && !strcmp(*argv, "--help")) { help_for(argv0, "verify"); cmd = CMD_HELP; @@ -4090,6 +4090,17 @@ int main(int argc, char **argv) if (options.cmd == CMD_VERIFY) { ret = verify_signed_file(ctx, &options); goto skip_signing; + } else if (options.cmd == CMD_EXTRACT_DATA) { + if (!ctx->format->pkcs7_contents_get) { + DO_EXIT_0("Unsupported command: extract-data\n"); + } + p7 = ctx->format->pkcs7_contents_get(ctx, hash, options.md); + if (!p7) { + DO_EXIT_0("Unable to extract pkcs7 contents\n"); + } + ret = data_write_pkcs7(ctx, outdata, p7); + PKCS7_free(p7); + goto skip_signing; } else if (options.cmd == CMD_EXTRACT) { if (!ctx->format->pkcs7_extract) { DO_EXIT_0("Unsupported command: extract-signature\n"); @@ -4098,7 +4109,7 @@ int main(int argc, char **argv) if (!p7) { DO_EXIT_0("Unable to extract existing signature\n"); } - ret = save_extracted_pkcs7(ctx, outdata, p7); + ret = data_write_pkcs7(ctx, outdata, p7); PKCS7_free(p7); goto skip_signing; } else if (options.cmd == CMD_REMOVE) { diff --git a/osslsigncode.h b/osslsigncode.h index e85e3fa..4ec2988 100644 --- a/osslsigncode.h +++ b/osslsigncode.h @@ -221,6 +221,7 @@ typedef enum { CMD_SIGN, CMD_EXTRACT, + CMD_EXTRACT_DATA, CMD_REMOVE, CMD_VERIFY, CMD_ADD, @@ -493,6 +494,7 @@ struct file_format_st { FILE_FORMAT_CTX *(*ctx_new) (GLOBAL_OPTIONS *option, BIO *hash, BIO *outdata); const EVP_MD *(*md_get) (FILE_FORMAT_CTX *ctx); ASN1_OBJECT *(*data_blob_get) (u_char **p, int *plen, FILE_FORMAT_CTX *ctx); + PKCS7 *(*pkcs7_contents_get) (FILE_FORMAT_CTX *ctx, BIO *hash, const EVP_MD *md); int (*hash_length_get) (FILE_FORMAT_CTX *ctx); int (*check_file) (FILE_FORMAT_CTX *ctx, int detached); u_char *(*digest_calc) (FILE_FORMAT_CTX *ctx, const EVP_MD *md); diff --git a/pe.c b/pe.c index 2427f58..103f639 100644 --- a/pe.c +++ b/pe.c @@ -44,6 +44,7 @@ struct pe_ctx_st { /* FILE_FORMAT method prototypes */ static FILE_FORMAT_CTX *pe_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *outdata); static ASN1_OBJECT *pe_spc_image_data_get(u_char **p, int *plen, FILE_FORMAT_CTX *ctx); +static PKCS7 *pe_pkcs7_contents_get(FILE_FORMAT_CTX *ctx, BIO *hash, const EVP_MD *md); static int pe_hash_length_get(FILE_FORMAT_CTX *ctx); static int pe_check_file(FILE_FORMAT_CTX *ctx, int detached); static u_char *pe_digest_calc(FILE_FORMAT_CTX *ctx, const EVP_MD *md); @@ -60,6 +61,7 @@ static void pe_ctx_cleanup(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata); FILE_FORMAT file_format_pe = { .ctx_new = pe_ctx_new, .data_blob_get = pe_spc_image_data_get, + .pkcs7_contents_get = pe_pkcs7_contents_get, .hash_length_get = pe_hash_length_get, .check_file = pe_check_file, .digest_calc = pe_digest_calc, @@ -80,6 +82,7 @@ static PKCS7 *pe_pkcs7_get_file(char *indata, PE_CTX *pe_ctx); static uint32_t pe_calc_checksum(BIO *bio, uint32_t header_size); static uint32_t pe_calc_realchecksum(FILE_FORMAT_CTX *ctx); static int pe_modify_header(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata); +static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md); static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj); static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype); static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int phtype); @@ -170,6 +173,30 @@ static ASN1_OBJECT *pe_spc_image_data_get(u_char **p, int *plen, FILE_FORMAT_CTX return dtype; /* OK */ } +/* + * Allocate and return a data content to be signed. + * [in] ctx: structure holds input and output data + * [in] hash: message digest BIO + * [in] md: message digest algorithm + * [returns] data content + */ +static PKCS7 *pe_pkcs7_contents_get(FILE_FORMAT_CTX *ctx, BIO *hash, const EVP_MD *md) +{ + ASN1_OCTET_STRING *content; + BIO *bhash; + + /* squash the unused parameter warning */ + (void)hash; + + bhash = pe_digest_calc_bio(ctx, md); + if (!bhash) { + return NULL; /* FAILED */ + } + content = spc_indirect_data_content_get(bhash, ctx); + BIO_free_all(bhash); + return pkcs7_set_content(content); +} + /* * [in] ctx: structure holds input and output data * [returns] the size of the message digest when passed an EVP_MD structure (the size of the hash) @@ -236,65 +263,25 @@ static int pe_check_file(FILE_FORMAT_CTX *ctx, int detached) return 1; /* OK */ } -/* Compute a message digest value of a signed or unsigned PE file. +/* + * Returns a message digest value of a signed or unsigned PE file. * [in] ctx: structure holds input and output data * [in] md: message digest algorithm * [returns] pointer to calculated message digest */ static u_char *pe_digest_calc(FILE_FORMAT_CTX *ctx, const EVP_MD *md) { - size_t written; - uint32_t idx = 0, fileend; - u_char *mdbuf = NULL; - BIO *bhash = BIO_new(BIO_f_md()); - - if (!BIO_set_md(bhash, md)) { - printf("Unable to set the message digest of BIO\n"); - BIO_free_all(bhash); - return 0; /* FAILED */ - } - BIO_push(bhash, BIO_new(BIO_s_null())); - if (ctx->pe_ctx->sigpos) - fileend = ctx->pe_ctx->sigpos; - else - fileend = ctx->pe_ctx->fileend; - - /* ctx->pe_ctx->header_size + 88 + 4 + 60 + ctx->pe_ctx->pe32plus * 16 + 8 */ - if (!BIO_write_ex(bhash, ctx->options->indata, ctx->pe_ctx->header_size + 88, &written) - || written != ctx->pe_ctx->header_size + 88) { - BIO_free_all(bhash); - return 0; /* FAILED */ - } - idx += (uint32_t)written + 4; - if (!BIO_write_ex(bhash, ctx->options->indata + idx, - 60 + ctx->pe_ctx->pe32plus * 16, &written) - || written != 60 + ctx->pe_ctx->pe32plus * 16) { - BIO_free_all(bhash); - return 0; /* FAILED */ - } - idx += (uint32_t)written + 8; - if (!bio_hash_data(bhash, ctx->options->indata, idx, fileend)) { - printf("Unable to calculate digest\n"); - BIO_free_all(bhash); - return 0; /* FAILED */ - } - if (!ctx->pe_ctx->sigpos) { - /* pad (with 0's) unsigned PE file to 8 byte boundary */ - int len = 8 - ctx->pe_ctx->fileend % 8; - if (len > 0 && len != 8) { - char *buf = OPENSSL_malloc(8); - memset(buf, 0, (size_t)len); - BIO_write(bhash, buf, len); - OPENSSL_free(buf); - } - } + u_char *mdbuf; + BIO *bhash = pe_digest_calc_bio(ctx, md); + if (!bhash) { + return 0; /* FAILED */ + } mdbuf = OPENSSL_malloc((size_t)EVP_MD_size(md)); BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md)); BIO_free_all(bhash); - return mdbuf; /* OK */ + return mdbuf; /* OK */ } - /* * Calculate message digest and page_hash and compare to values retrieved * from PKCS#7 signedData. @@ -456,6 +443,7 @@ static PKCS7 *pe_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) return NULL; /* FAILED */ } } else if (ctx->options->cmd == CMD_SIGN) { + ASN1_OCTET_STRING *content; /* Create a new PKCS#7 signature */ p7 = pkcs7_create(ctx); if (!p7) { @@ -467,10 +455,18 @@ static PKCS7 *pe_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) PKCS7_free(p7); return NULL; /* FAILED */ } - if (!sign_spc_indirect_data_content(p7, hash, ctx)) { - printf("Failed to set signed content\n"); + content = spc_indirect_data_content_get(hash, ctx); + if (!content) { + printf("Failed to get spcIndirectDataContent\n"); return NULL; /* FAILED */ } + if (!sign_spc_indirect_data_content(p7, content)) { + printf("Failed to set signed content\n"); + PKCS7_free(p7); + ASN1_OCTET_STRING_free(content); + return NULL; /* FAILED */ + } + ASN1_OCTET_STRING_free(content); } if (ctx->options->nest) ctx->options->prevsig = cursig; @@ -833,6 +829,62 @@ static int pe_modify_header(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) return 1; /* OK */ } +/* + * Compute a message digest value of a signed or unsigned PE file. + * [in] ctx: structure holds input and output data + * [in] md: message digest algorithm + * [returns] calculated message digest BIO + */ +static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md) +{ + size_t written; + uint32_t idx = 0, fileend; + BIO *bhash = BIO_new(BIO_f_md()); + + if (!BIO_set_md(bhash, md)) { + printf("Unable to set the message digest of BIO\n"); + BIO_free_all(bhash); + return 0; /* FAILED */ + } + BIO_push(bhash, BIO_new(BIO_s_null())); + if (ctx->pe_ctx->sigpos) + fileend = ctx->pe_ctx->sigpos; + else + fileend = ctx->pe_ctx->fileend; + + /* ctx->pe_ctx->header_size + 88 + 4 + 60 + ctx->pe_ctx->pe32plus * 16 + 8 */ + if (!BIO_write_ex(bhash, ctx->options->indata, ctx->pe_ctx->header_size + 88, &written) + || written != ctx->pe_ctx->header_size + 88) { + BIO_free_all(bhash); + return 0; /* FAILED */ + } + idx += (uint32_t)written + 4; + if (!BIO_write_ex(bhash, ctx->options->indata + idx, + 60 + ctx->pe_ctx->pe32plus * 16, &written) + || written != 60 + ctx->pe_ctx->pe32plus * 16) { + BIO_free_all(bhash); + return 0; /* FAILED */ + } + idx += (uint32_t)written + 8; + if (!bio_hash_data(bhash, ctx->options->indata, idx, fileend)) { + printf("Unable to calculate digest\n"); + BIO_free_all(bhash); + return 0; /* FAILED */ + } + if (!ctx->pe_ctx->sigpos) { + /* pad (with 0's) unsigned PE file to 8 byte boundary */ + int len = 8 - ctx->pe_ctx->fileend % 8; + if (len > 0 && len != 8) { + char *buf = OPENSSL_malloc(8); + memset(buf, 0, (size_t)len); + BIO_write(bhash, buf, len); + OPENSSL_free(buf); + } + } + return bhash; +} + + /* * Page hash support */