Enable verification that a catalog file was signed by a valid certificate.

Simplify setting and signing a content blob.
Clean up a support library.
This commit is contained in:
olszomal
2023-11-03 09:31:22 +01:00
committed by Michał Trojnara
parent 33253afb5e
commit 900ffed596
8 changed files with 337 additions and 284 deletions

6
appx.c
View File

@ -587,12 +587,16 @@ static PKCS7 *appx_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata)
printf("Creating a new signature failed\n"); printf("Creating a new signature failed\n");
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!add_indirect_data_object(p7, hashes, ctx)) { if (!add_indirect_data_object(p7)) {
printf("Adding SPC_INDIRECT_DATA_OBJID failed\n"); printf("Adding SPC_INDIRECT_DATA_OBJID failed\n");
BIO_free_all(hashes); BIO_free_all(hashes);
PKCS7_free(p7); PKCS7_free(p7);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!sign_spc_indirect_data_content(p7, hashes, ctx)) {
printf("Failed to set signed content\n");
return NULL; /* FAILED */
}
BIO_free_all(hashes); BIO_free_all(hashes);
} }
return p7; /* OK */ return p7; /* OK */

6
cab.c
View File

@ -498,11 +498,15 @@ static PKCS7 *cab_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata)
PKCS7_free(p7); PKCS7_free(p7);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!add_indirect_data_object(p7, hash, ctx)) { if (!add_indirect_data_object(p7)) {
printf("Adding SPC_INDIRECT_DATA_OBJID failed\n"); printf("Adding SPC_INDIRECT_DATA_OBJID failed\n");
PKCS7_free(p7); PKCS7_free(p7);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!sign_spc_indirect_data_content(p7, hash, ctx)) {
printf("Failed to set signed content\n");
return NULL; /* FAILED */
}
} }
if (ctx->options->nest) if (ctx->options->nest)
ctx->options->prevsig = cursig; ctx->options->prevsig = cursig;

135
cat.c
View File

@ -19,10 +19,13 @@ struct cat_ctx_st {
uint32_t sigpos; uint32_t sigpos;
uint32_t siglen; uint32_t siglen;
uint32_t fileend; uint32_t fileend;
PKCS7 *p7;
}; };
/* FILE_FORMAT method prototypes */ /* FILE_FORMAT method prototypes */
static FILE_FORMAT_CTX *cat_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *outdata); static FILE_FORMAT_CTX *cat_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *outdata);
static int cat_check_file(FILE_FORMAT_CTX *ctx, int detached);
static int cat_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7);
static PKCS7 *cat_pkcs7_extract(FILE_FORMAT_CTX *ctx); static PKCS7 *cat_pkcs7_extract(FILE_FORMAT_CTX *ctx);
static PKCS7 *cat_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata); static PKCS7 *cat_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata);
static int cat_append_pkcs7(FILE_FORMAT_CTX *ctx, BIO *outdata, PKCS7 *p7); static int cat_append_pkcs7(FILE_FORMAT_CTX *ctx, BIO *outdata, PKCS7 *p7);
@ -31,6 +34,8 @@ static void cat_ctx_cleanup(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata);
FILE_FORMAT file_format_cat = { FILE_FORMAT file_format_cat = {
.ctx_new = cat_ctx_new, .ctx_new = cat_ctx_new,
.check_file = cat_check_file,
.verify_digests = cat_verify_digests,
.pkcs7_extract = cat_pkcs7_extract, .pkcs7_extract = cat_pkcs7_extract,
.pkcs7_prepare = cat_pkcs7_prepare, .pkcs7_prepare = cat_pkcs7_prepare,
.append_pkcs7 = cat_append_pkcs7, .append_pkcs7 = cat_append_pkcs7,
@ -40,6 +45,8 @@ FILE_FORMAT file_format_cat = {
/* Prototypes */ /* Prototypes */
static CAT_CTX *cat_ctx_get(char *indata, uint32_t filesize); static CAT_CTX *cat_ctx_get(char *indata, uint32_t filesize);
static int cat_add_ms_ctl_object(PKCS7 *p7);
static int cat_sign_ms_ctl_content(PKCS7 *p7, PKCS7 *contents);
/* /*
* FILE_FORMAT method definitions * FILE_FORMAT method definitions
@ -58,17 +65,12 @@ static FILE_FORMAT_CTX *cat_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *out
CAT_CTX *cat_ctx; CAT_CTX *cat_ctx;
uint32_t filesize; uint32_t filesize;
/* squash unused parameter warnings */
(void)outdata;
(void)hash;
if (options->cmd == CMD_REMOVE || options->cmd==CMD_ATTACH) { if (options->cmd == CMD_REMOVE || options->cmd==CMD_ATTACH) {
printf("Unsupported command\n"); printf("Unsupported command\n");
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (options->cmd == CMD_VERIFY) { if (options->cmd == CMD_VERIFY) {
printf("Use -catalog option\n"); printf("Warning: Use -catalog option to verify that a file, listed in catalog file, is signed\n\n");
return NULL; /* FAILED */
} }
filesize = get_file_size(options->infile); filesize = get_file_size(options->infile);
if (filesize == 0) if (filesize == 0)
@ -109,6 +111,45 @@ static FILE_FORMAT_CTX *cat_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *out
return ctx; return ctx;
} }
static int cat_check_file(FILE_FORMAT_CTX *ctx, int detached)
{
STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
PKCS7_SIGNER_INFO *si;
if (!ctx) {
printf("Init error\n\n");
return 0; /* FAILED */
}
if (detached) {
printf("CAT format does not support detached PKCS#7 signature\n\n");
return 0; /* FAILED */
}
signer_info = PKCS7_get_signer_info(ctx->cat_ctx->p7);
if (!signer_info) {
printf("Failed catalog file\n\n");
return 0; /* FAILED */
}
si = sk_PKCS7_SIGNER_INFO_value(signer_info, 0);
if (!si) {
printf("No signature found\n\n");
return 0; /* FAILED */
}
return 1; /* OK */
}
/*
* ContentInfo value is the inner content of pkcs7-signedData.
* An extra verification is not necessary when a content type data
* is the inner content of the signed-data type.
*/
static int cat_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
{
/* squash unused parameter warnings */
(void)ctx;
(void)p7;
return 1; /* OK */
}
/* /*
* Extract existing signature in DER format. * Extract existing signature in DER format.
* [in] ctx: structure holds input and output data * [in] ctx: structure holds input and output data
@ -116,7 +157,7 @@ static FILE_FORMAT_CTX *cat_ctx_new(GLOBAL_OPTIONS *options, BIO *hash, BIO *out
*/ */
static PKCS7 *cat_pkcs7_extract(FILE_FORMAT_CTX *ctx) static PKCS7 *cat_pkcs7_extract(FILE_FORMAT_CTX *ctx)
{ {
return pkcs7_get(ctx->options->indata, ctx->cat_ctx->sigpos, ctx->cat_ctx->siglen); return PKCS7_dup(ctx->cat_ctx->p7);
} }
/* /*
@ -128,35 +169,32 @@ static PKCS7 *cat_pkcs7_extract(FILE_FORMAT_CTX *ctx)
*/ */
static PKCS7 *cat_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata) static PKCS7 *cat_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata)
{ {
PKCS7 *cursig = NULL, *p7 = NULL; PKCS7 *p7 = NULL;
/* squash unused parameter warnings */ /* squash unused parameter warnings */
(void)outdata; (void)outdata;
(void)hash; (void)hash;
/* Obtain an existing signature */ /* Obtain an existing signature */
cursig = pkcs7_get(ctx->options->indata, ctx->cat_ctx->sigpos, ctx->cat_ctx->siglen);
if (!cursig) {
printf("Unable to extract existing signature\n");
return NULL; /* FAILED */
}
if (ctx->options->cmd == CMD_ADD || ctx->options->cmd == CMD_ATTACH) { if (ctx->options->cmd == CMD_ADD || ctx->options->cmd == CMD_ATTACH) {
p7 = cursig; p7 = PKCS7_dup(ctx->cat_ctx->p7);
} else if (ctx->options->cmd == CMD_SIGN) { } else if (ctx->options->cmd == CMD_SIGN) {
/* Create a new signature */ /* Create a new signature */
p7 = pkcs7_create(ctx); p7 = pkcs7_create(ctx);
if (!p7) { if (!p7) {
printf("Creating a new signature failed\n"); printf("Creating a new signature failed\n");
PKCS7_free(cursig);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!add_ms_ctl_object(p7, cursig)) { if (!cat_add_ms_ctl_object(p7)) {
printf("Adding MS_CTL_OBJID failed\n"); printf("Adding MS_CTL_OBJID failed\n");
PKCS7_free(p7); PKCS7_free(p7);
PKCS7_free(cursig);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
PKCS7_free(cursig); if (!cat_sign_ms_ctl_content(p7, ctx->cat_ctx->p7->d.sign->contents)) {
printf("Failed to set signed content\n");
PKCS7_free(p7);
return 0; /* FAILED */
}
} }
return p7; /* OK */ return p7; /* OK */
} }
@ -224,6 +262,7 @@ static void cat_ctx_cleanup(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata)
} }
} }
unmap_file(ctx->options->indata, ctx->cat_ctx->fileend); unmap_file(ctx->options->indata, ctx->cat_ctx->fileend);
PKCS7_free(ctx->cat_ctx->p7);
OPENSSL_free(ctx->cat_ctx); OPENSSL_free(ctx->cat_ctx);
OPENSSL_free(ctx); OPENSSL_free(ctx);
} }
@ -233,7 +272,7 @@ static void cat_ctx_cleanup(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata)
*/ */
/* /*
* Verify mapped CAT file TODO and create CAT format specific structure. * Verify mapped CAT file and create CAT format specific structure.
* [in] indata: mapped CAT file (unused) * [in] indata: mapped CAT file (unused)
* [in] filesize: size of CAT file * [in] filesize: size of CAT file
* [returns] pointer to CAT format specific structure * [returns] pointer to CAT format specific structure
@ -241,17 +280,69 @@ static void cat_ctx_cleanup(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata)
static CAT_CTX *cat_ctx_get(char *indata, uint32_t filesize) static CAT_CTX *cat_ctx_get(char *indata, uint32_t filesize)
{ {
CAT_CTX *cat_ctx; CAT_CTX *cat_ctx;
PKCS7 *p7 = pkcs7_get(indata, 0, filesize);
/* squash the unused parameter warning */ if (!p7)
(void)indata; return NULL; /* FAILED */
cat_ctx = OPENSSL_zalloc(sizeof(CAT_CTX)); cat_ctx = OPENSSL_zalloc(sizeof(CAT_CTX));
cat_ctx->p7 = p7;
cat_ctx->sigpos = 0; cat_ctx->sigpos = 0;
cat_ctx->siglen = filesize; cat_ctx->siglen = filesize;
cat_ctx->fileend = filesize; cat_ctx->fileend = filesize;
return cat_ctx; /* OK */ return cat_ctx; /* OK */
} }
/*
* Add "1.3.6.1.4.1.311.10.1" MS_CTL_OBJID signed attribute
* [in, out] p7: new PKCS#7 signature
* [returns] 0 on error or 1 on success
*/
static int cat_add_ms_ctl_object(PKCS7 *p7)
{
STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
PKCS7_SIGNER_INFO *si;
signer_info = PKCS7_get_signer_info(p7);
if (!signer_info)
return 0; /* FAILED */
si = sk_PKCS7_SIGNER_INFO_value(signer_info, 0);
if (!si)
return 0; /* FAILED */
if (!PKCS7_add_signed_attribute(si, NID_pkcs9_contentType,
V_ASN1_OBJECT, OBJ_txt2obj(MS_CTL_OBJID, 1)))
return 0; /* FAILED */
return 1; /* OK */
}
/*
* Sign the MS CTL blob.
* Certificate Trust List (CTL) is a list of file names or thumbprints.
* All the items in this list are authenticated (approved) by the signing entity.
* [in, out] p7: new PKCS#7 signature
* [in] contents: Certificate Trust List (CTL)
* [returns] 0 on error or 1 on success
*/
static int cat_sign_ms_ctl_content(PKCS7 *p7, PKCS7 *contents)
{
u_char *content;
int seqhdrlen, content_length;
seqhdrlen = asn1_simple_hdr_len(contents->d.other->value.sequence->data,
contents->d.other->value.sequence->length);
content = contents->d.other->value.sequence->data + seqhdrlen;
content_length = contents->d.other->value.sequence->length - seqhdrlen;
if (!pkcs7_sign_content(p7, content, content_length)) {
printf("Failed to sign content\n");
return 0; /* FAILED */
}
if (!PKCS7_set_content(p7, PKCS7_dup(contents))) {
printf("PKCS7_set_content failed\n");
return 0; /* FAILED */
}
return 1; /* OK */
}
/* /*
Local Variables: Local Variables:
c-basic-offset: 4 c-basic-offset: 4

436
helpers.c
View File

@ -9,12 +9,12 @@
#include "helpers.h" #include "helpers.h"
/* Prototypes */ /* Prototypes */
static int pkcs7_set_content_blob(PKCS7 *sig, PKCS7 *cursig);
static SpcSpOpusInfo *spc_sp_opus_info_create(FILE_FORMAT_CTX *ctx); 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_get(u_char **blob, int *len, FILE_FORMAT_CTX *ctx);
static int pkcs7_set_spc_indirect_data_content(PKCS7 *p7, BIO *hash, u_char *buf, 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_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 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);
static int X509_compare(const X509 *const *a, const X509 *const *b);
/* /*
* Common functions * Common functions
@ -108,60 +108,6 @@ void unmap_file(char *indata, const size_t size)
#endif /* WIN32 */ #endif /* WIN32 */
} }
/*
* [in, out] si: PKCS7_SIGNER_INFO structure
* [in] ctx: FILE_FORMAT_CTX structure
* [returns] 0 on error or 1 on success
*/
static int pkcs7_signer_info_add_spc_sp_opus_info(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx)
{
SpcSpOpusInfo *opus;
ASN1_STRING *astr;
int len;
u_char *p = NULL;
opus = spc_sp_opus_info_create(ctx);
if ((len = i2d_SpcSpOpusInfo(opus, NULL)) <= 0
|| (p = OPENSSL_malloc((size_t)len)) == NULL) {
SpcSpOpusInfo_free(opus);
return 0; /* FAILED */
}
i2d_SpcSpOpusInfo(opus, &p);
p -= len;
astr = ASN1_STRING_new();
ASN1_STRING_set(astr, p, len);
OPENSSL_free(p);
SpcSpOpusInfo_free(opus);
return PKCS7_add_signed_attribute(si, OBJ_txt2nid(SPC_SP_OPUS_INFO_OBJID),
V_ASN1_SEQUENCE, astr);
}
/*
* [in, out] si: PKCS7_SIGNER_INFO structure
* [in] ctx: structure holds input and output data
* [returns] 0 on error or 1 on success
*/
static int pkcs7_signer_info_add_purpose(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx)
{
static const u_char purpose_ind[] = {
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
0x01, 0x82, 0x37, 0x02, 0x01, 0x15
};
static const u_char purpose_comm[] = {
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
0x01, 0x82, 0x37, 0x02, 0x01, 0x16
};
ASN1_STRING *purpose = ASN1_STRING_new();
if (ctx->options->comm) {
ASN1_STRING_set(purpose, purpose_comm, sizeof purpose_comm);
} else {
ASN1_STRING_set(purpose, purpose_ind, sizeof purpose_ind);
}
return PKCS7_add_signed_attribute(si, OBJ_txt2nid(SPC_STATEMENT_TYPE_OBJID),
V_ASN1_SEQUENCE, purpose);
}
/* /*
* Add a custom, non-trusted time to the PKCS7 structure to prevent OpenSSL * Add a custom, non-trusted time to the PKCS7 structure to prevent OpenSSL
* adding the _current_ time. This allows to create a deterministic signature * adding the _current_ time. This allows to create a deterministic signature
@ -216,81 +162,6 @@ PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx)
return p7; return p7;
} }
/*
* X.690-compliant certificate comparison function
* Windows requires catalog files to use PKCS#7
* content ordering specified in X.690 section 11.6
* https://support.microsoft.com/en-us/topic/october-13-2020-kb4580358-security-only-update-d3f6eb3c-d7c4-a9cb-0de6-759386bf7113
* This algorithm is different from X509_cmp()
* [in] a_ptr, b_ptr: pointers to X509 certificates
* [returns] certificates order
*/
static int X509_compare(const X509 *const *a, const X509 *const *b)
{
u_char *a_data, *b_data, *a_tmp, *b_tmp;
size_t a_len, b_len;
int ret;
a_len = (size_t)i2d_X509(*a, NULL);
a_tmp = a_data = OPENSSL_malloc(a_len);
i2d_X509(*a, &a_tmp);
b_len = (size_t)i2d_X509(*b, NULL);
b_tmp = b_data = OPENSSL_malloc(b_len);
i2d_X509(*b, &b_tmp);
ret = memcmp(a_data, b_data, MIN(a_len, b_len));
OPENSSL_free(a_data);
OPENSSL_free(b_data);
if (ret == 0 && a_len != b_len) /* identical up to the length of the shorter DER */
ret = a_len < b_len ? -1 : 1; /* shorter is smaller */
return ret;
}
/*
* Create certificate chain sorted in ascending order by their DER encoding.
* [in] ctx: structure holds input and output data
* [in] signer: signer's certificate number in the certificate chain
* [returns] sorted certificate chain
*/
static STACK_OF(X509) *X509_chain_get_sorted(FILE_FORMAT_CTX *ctx, int signer)
{
int i;
STACK_OF(X509) *chain = sk_X509_new(X509_compare);
/* add the signer's certificate */
if (ctx->options->cert != NULL && !sk_X509_push(chain, ctx->options->cert)) {
sk_X509_free(chain);
return NULL;
}
if (signer != -1 && !sk_X509_push(chain, sk_X509_value(ctx->options->certs, signer))) {
sk_X509_free(chain);
return NULL;
}
/* add the certificate chain */
for (i=0; i<sk_X509_num(ctx->options->certs); i++) {
if (i == signer)
continue;
if (!sk_X509_push(chain, sk_X509_value(ctx->options->certs, i))) {
sk_X509_free(chain);
return NULL;
}
}
/* add all cross certificates */
if (ctx->options->xcerts) {
for (i=0; i<sk_X509_num(ctx->options->xcerts); i++) {
if (!sk_X509_push(chain, sk_X509_value(ctx->options->xcerts, i))) {
sk_X509_free(chain);
return NULL;
}
}
}
/* sort certificate chain using the supplied comparison function */
sk_X509_sort(chain);
return chain;
}
/* /*
* Allocate, set type, add content and return a new PKCS#7 signature * Allocate, set type, add content and return a new PKCS#7 signature
* [in] ctx: structure holds input and output data * [in] ctx: structure holds input and output data
@ -305,7 +176,7 @@ PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx)
p7 = PKCS7_new(); p7 = PKCS7_new();
PKCS7_set_type(p7, NID_pkcs7_signed); PKCS7_set_type(p7, NID_pkcs7_signed);
PKCS7_content_new(p7, NID_pkcs7_data);
if (ctx->options->cert != NULL) { if (ctx->options->cert != NULL) {
/* /*
* the private key and corresponding certificate are parsed from the PKCS12 * the private key and corresponding certificate are parsed from the PKCS12
@ -335,17 +206,14 @@ PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx)
} }
} }
pkcs7_signer_info_add_signing_time(si, ctx); pkcs7_signer_info_add_signing_time(si, ctx);
if (!pkcs7_signer_info_add_purpose(si, ctx)) {
if (!pkcs7_signer_info_add_purpose(si, ctx))
return NULL; /* FAILED */ return NULL; /* FAILED */
}
if ((ctx->options->desc || ctx->options->url) && if ((ctx->options->desc || ctx->options->url) &&
!pkcs7_signer_info_add_spc_sp_opus_info(si, ctx)) { !pkcs7_signer_info_add_spc_sp_opus_info(si, ctx)) {
printf("Couldn't allocate memory for opus info\n"); printf("Couldn't allocate memory for opus info\n");
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
PKCS7_content_new(p7, NID_pkcs7_data);
/* create X509 chain sorted in ascending order by their DER encoding */ /* create X509 chain sorted in ascending order by their DER encoding */
chain = X509_chain_get_sorted(ctx, signer); chain = X509_chain_get_sorted(ctx, signer);
if (chain == NULL) { if (chain == NULL) {
@ -366,12 +234,12 @@ PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx)
} }
/* /*
* PE, MSI, CAB and APPX file specific
* Add "1.3.6.1.4.1.311.2.1.4" SPC_INDIRECT_DATA_OBJID signed attribute
* [in, out] p7: new PKCS#7 signature * [in, out] p7: new PKCS#7 signature
* [in] hash: message digest BIO
* [in] ctx: structure holds input and output data
* [returns] 0 on error or 1 on success * [returns] 0 on error or 1 on success
*/ */
int add_indirect_data_object(PKCS7 *p7, BIO *hash, FILE_FORMAT_CTX *ctx) int add_indirect_data_object(PKCS7 *p7)
{ {
STACK_OF(PKCS7_SIGNER_INFO) *signer_info; STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
PKCS7_SIGNER_INFO *si; PKCS7_SIGNER_INFO *si;
@ -385,67 +253,85 @@ int add_indirect_data_object(PKCS7 *p7, BIO *hash, FILE_FORMAT_CTX *ctx)
if (!PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, if (!PKCS7_add_signed_attribute(si, NID_pkcs9_contentType,
V_ASN1_OBJECT, OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1))) V_ASN1_OBJECT, OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1)))
return 0; /* FAILED */ return 0; /* FAILED */
if (!pkcs7_set_data_content(p7, hash, ctx)) { return 1; /* OK */
printf("Signing failed\n"); }
/*
* PE, MSI, CAB and APPX format specific
* Sign the MS Authenticode spcIndirectDataContent blob.
* 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
* [returns] 0 on error or 1 on success
*/
int sign_spc_indirect_data_content(PKCS7 *p7, BIO *hash, FILE_FORMAT_CTX *ctx)
{
u_char mdbuf[5 * EVP_MAX_MD_SIZE + 24];
int mdlen, seqhdrlen, hashlen;
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 */
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);
return 0; /* FAILED */
}
td7 = PKCS7_new();
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);
if (!PKCS7_set_content(p7, td7)) {
printf("PKCS7_set_content failed\n");
PKCS7_free(td7);
return 0; /* FAILED */ return 0; /* FAILED */
} }
return 1; /* OK */ return 1; /* OK */
} }
/* /*
* Signs the data and place the signature in p7
* [in, out] p7: new PKCS#7 signature * [in, out] p7: new PKCS#7 signature
* [in] cursig: current PKCS#7 signature * [in] data: content data
* [returns] 0 on error or 1 on success * [in] len: content length
*/ */
int add_ms_ctl_object(PKCS7 *p7, PKCS7 *cursig) int pkcs7_sign_content(PKCS7 *p7, u_char *data, int len)
{ {
STACK_OF(PKCS7_SIGNER_INFO) *signer_info; BIO *p7bio;
PKCS7_SIGNER_INFO *si;
signer_info = PKCS7_get_signer_info(p7); if ((p7bio = PKCS7_dataInit(p7, NULL)) == NULL) {
if (!signer_info)
return 0; /* FAILED */
si = sk_PKCS7_SIGNER_INFO_value(signer_info, 0);
if (!si)
return 0; /* FAILED */
if (!PKCS7_add_signed_attribute(si, NID_pkcs9_contentType,
V_ASN1_OBJECT, OBJ_txt2obj(MS_CTL_OBJID, 1)))
return 0; /* FAILED */
if (!pkcs7_set_content_blob(p7, cursig)) {
printf("Signing failed\n");
return 0; /* FAILED */
}
return 1; /* OK */
}
static int pkcs7_set_content_blob(PKCS7 *sig, PKCS7 *cursig)
{
PKCS7 *contents;
u_char *content;
int seqhdrlen, content_length;
BIO *sigbio;
contents = cursig->d.sign->contents;
seqhdrlen = asn1_simple_hdr_len(contents->d.other->value.sequence->data,
contents->d.other->value.sequence->length);
content = contents->d.other->value.sequence->data + seqhdrlen;
content_length = contents->d.other->value.sequence->length - seqhdrlen;
if ((sigbio = PKCS7_dataInit(sig, NULL)) == NULL) {
printf("PKCS7_dataInit failed\n"); printf("PKCS7_dataInit failed\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
BIO_write(sigbio, content, content_length); BIO_write(p7bio, data, len);
(void)BIO_flush(sigbio); (void)BIO_flush(p7bio);
if (!PKCS7_dataFinal(sig, sigbio)) { if (!PKCS7_dataFinal(p7, p7bio)) {
printf("PKCS7_dataFinal failed\n"); printf("PKCS7_dataFinal failed\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
BIO_free_all(sigbio); BIO_free_all(p7bio);
if (!PKCS7_set_content(sig, PKCS7_dup(contents))) {
printf("PKCS7_set_content failed\n");
return 0; /* FAILED */
}
return 1; /* OK */ return 1; /* OK */
} }
@ -525,32 +411,6 @@ int is_content_type(PKCS7 *p7, const char *objid)
return ret; return ret;
} }
/*
* [out] p7: new PKCS#7 signature
* [in] hash: message digest BIO
* [in] ctx: structure holds input and output data
* [returns] 0 on error or 1 on success
*/
int pkcs7_set_data_content(PKCS7 *p7, BIO *hash, FILE_FORMAT_CTX *ctx)
{
u_char *p = NULL;
int len = 0;
u_char *buf;
if (!spc_indirect_data_content_get(&p, &len, ctx))
return 0; /* FAILED */
buf = OPENSSL_malloc(SIZE_64K);
memcpy(buf, p, (size_t)len);
OPENSSL_free(p);
if (!pkcs7_set_spc_indirect_data_content(p7, hash, buf, len, ctx)) {
OPENSSL_free(buf);
return 0; /* FAILED */
}
OPENSSL_free(buf);
return 1; /* OK */
}
/* /*
* PE and CAB format specific * PE and CAB format specific
* [in] none * [in] none
@ -673,56 +533,132 @@ static int spc_indirect_data_content_get(u_char **blob, int *len, FILE_FORMAT_CT
} }
/* /*
* Replace the data part with the MS Authenticode spcIndirectDataContent blob * [in, out] si: PKCS7_SIGNER_INFO structure
* [out] p7: new PKCS#7 signature
* [in] hash: message digest BIO
* [in] blob: SpcIndirectDataContent data
* [in] len: SpcIndirectDataContent data length
* [in] ctx: FILE_FORMAT_CTX structure * [in] ctx: FILE_FORMAT_CTX structure
* [returns] 0 on error or 1 on success * [returns] 0 on error or 1 on success
*/ */
static int pkcs7_set_spc_indirect_data_content(PKCS7 *p7, BIO *hash, u_char *buf, int len, FILE_FORMAT_CTX *ctx) static int pkcs7_signer_info_add_spc_sp_opus_info(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx)
{ {
u_char mdbuf[5 * EVP_MAX_MD_SIZE + 24]; SpcSpOpusInfo *opus;
int mdlen, seqhdrlen, hashlen; ASN1_STRING *astr;
BIO *bio; int len;
PKCS7 *td7; u_char *p = NULL;
hashlen = ctx->format->hash_length_get(ctx); opus = spc_sp_opus_info_create(ctx);
if (hashlen > EVP_MAX_MD_SIZE) { if ((len = i2d_SpcSpOpusInfo(opus, NULL)) <= 0
/* APPX format specific */ || (p = OPENSSL_malloc((size_t)len)) == NULL) {
mdlen = BIO_read(hash, (char*)mdbuf, hashlen); SpcSpOpusInfo_free(opus);
return 0; /* FAILED */
}
i2d_SpcSpOpusInfo(opus, &p);
p -= len;
astr = ASN1_STRING_new();
ASN1_STRING_set(astr, p, len);
OPENSSL_free(p);
SpcSpOpusInfo_free(opus);
return PKCS7_add_signed_attribute(si, OBJ_txt2nid(SPC_SP_OPUS_INFO_OBJID),
V_ASN1_SEQUENCE, astr);
}
/*
* [in, out] si: PKCS7_SIGNER_INFO structure
* [in] ctx: structure holds input and output data
* [returns] 0 on error or 1 on success
*/
static int pkcs7_signer_info_add_purpose(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx)
{
static const u_char purpose_ind[] = {
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
0x01, 0x82, 0x37, 0x02, 0x01, 0x15
};
static const u_char purpose_comm[] = {
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
0x01, 0x82, 0x37, 0x02, 0x01, 0x16
};
ASN1_STRING *purpose = ASN1_STRING_new();
if (ctx->options->comm) {
ASN1_STRING_set(purpose, purpose_comm, sizeof purpose_comm);
} else { } else {
mdlen = BIO_gets(hash, (char*)mdbuf, EVP_MAX_MD_SIZE); ASN1_STRING_set(purpose, purpose_ind, sizeof purpose_ind);
} }
memcpy(buf + len, mdbuf, (size_t)mdlen); return PKCS7_add_signed_attribute(si, OBJ_txt2nid(SPC_STATEMENT_TYPE_OBJID),
seqhdrlen = asn1_simple_hdr_len(buf, len); V_ASN1_SEQUENCE, purpose);
}
if ((bio = PKCS7_dataInit(p7, NULL)) == NULL) { /*
printf("PKCS7_dataInit failed\n"); * Create certificate chain sorted in ascending order by their DER encoding.
return 0; /* FAILED */ * [in] ctx: structure holds input and output data
} * [in] signer: signer's certificate number in the certificate chain
BIO_write(bio, buf + seqhdrlen, len - seqhdrlen + mdlen); * [returns] sorted certificate chain
(void)BIO_flush(bio); */
static STACK_OF(X509) *X509_chain_get_sorted(FILE_FORMAT_CTX *ctx, int signer)
{
int i;
STACK_OF(X509) *chain = sk_X509_new(X509_compare);
if (!PKCS7_dataFinal(p7, bio)) { /* add the signer's certificate */
printf("PKCS7_dataFinal failed\n"); if (ctx->options->cert != NULL && !sk_X509_push(chain, ctx->options->cert)) {
return 0; /* FAILED */ sk_X509_free(chain);
return NULL;
} }
BIO_free_all(bio); if (signer != -1 && !sk_X509_push(chain, sk_X509_value(ctx->options->certs, signer))) {
sk_X509_free(chain);
return NULL;
}
/* add the certificate chain */
for (i=0; i<sk_X509_num(ctx->options->certs); i++) {
if (i == signer)
continue;
if (!sk_X509_push(chain, sk_X509_value(ctx->options->certs, i))) {
sk_X509_free(chain);
return NULL;
}
}
/* add all cross certificates */
if (ctx->options->xcerts) {
for (i=0; i<sk_X509_num(ctx->options->xcerts); i++) {
if (!sk_X509_push(chain, sk_X509_value(ctx->options->xcerts, i))) {
sk_X509_free(chain);
return NULL;
}
}
}
/* sort certificate chain using the supplied comparison function */
sk_X509_sort(chain);
return chain;
}
td7 = PKCS7_new(); /*
td7->type = OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1); * X.690-compliant certificate comparison function
td7->d.other = ASN1_TYPE_new(); * Windows requires catalog files to use PKCS#7
td7->d.other->type = V_ASN1_SEQUENCE; * content ordering specified in X.690 section 11.6
td7->d.other->value.sequence = ASN1_STRING_new(); * https://support.microsoft.com/en-us/topic/october-13-2020-kb4580358-security-only-update-d3f6eb3c-d7c4-a9cb-0de6-759386bf7113
ASN1_STRING_set(td7->d.other->value.sequence, buf, len + mdlen); * This algorithm is different from X509_cmp()
if (!PKCS7_set_content(p7, td7)) { * [in] a_ptr, b_ptr: pointers to X509 certificates
PKCS7_free(td7); * [returns] certificates order
printf("PKCS7_set_content failed\n"); */
return 0; /* FAILED */ static int X509_compare(const X509 *const *a, const X509 *const *b)
} {
return 1; /* OK */ u_char *a_data, *b_data, *a_tmp, *b_tmp;
size_t a_len, b_len;
int ret;
a_len = (size_t)i2d_X509(*a, NULL);
a_tmp = a_data = OPENSSL_malloc(a_len);
i2d_X509(*a, &a_tmp);
b_len = (size_t)i2d_X509(*b, NULL);
b_tmp = b_data = OPENSSL_malloc(b_len);
i2d_X509(*b, &b_tmp);
ret = memcmp(a_data, b_data, MIN(a_len, b_len));
OPENSSL_free(a_data);
OPENSSL_free(b_data);
if (ret == 0 && a_len != b_len) /* identical up to the length of the shorter DER */
ret = a_len < b_len ? -1 : 1; /* shorter is smaller */
return ret;
} }
/* /*

View File

@ -12,14 +12,13 @@ void unmap_file(char *indata, const size_t size);
int pkcs7_signer_info_add_signing_time(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx); 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_get_sigfile(FILE_FORMAT_CTX *ctx);
PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx); PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx);
void add_content_type(PKCS7 *p7); int add_indirect_data_object(PKCS7 *p7);
int add_indirect_data_object(PKCS7 *p7, BIO *hash, FILE_FORMAT_CTX *ctx); int sign_spc_indirect_data_content(PKCS7 *p7, BIO *hash, FILE_FORMAT_CTX *ctx);
int add_ms_ctl_object(PKCS7 *p7, PKCS7 *cursig); int pkcs7_sign_content(PKCS7 *p7, u_char *data, int len);
int asn1_simple_hdr_len(const u_char *p, 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); 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); void print_hash(const char *descript1, const char *descript2, const u_char *hashbuf, int length);
int is_content_type(PKCS7 *p7, const char *objid); int is_content_type(PKCS7 *p7, const char *objid);
int pkcs7_set_data_content(PKCS7 *sig, BIO *hash, FILE_FORMAT_CTX *ctx);
SpcLink *spc_link_obsolete_get(void); SpcLink *spc_link_obsolete_get(void);
PKCS7 *pkcs7_get(char *indata, uint32_t sigpos, uint32_t siglen); PKCS7 *pkcs7_get(char *indata, uint32_t sigpos, uint32_t siglen);
int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype); int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype);

6
msi.c
View File

@ -668,11 +668,15 @@ static PKCS7 *msi_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata)
printf("Creating a new signature failed\n"); printf("Creating a new signature failed\n");
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!add_indirect_data_object(p7, hash, ctx)) { if (!add_indirect_data_object(p7)) {
printf("Adding SPC_INDIRECT_DATA_OBJID failed\n"); printf("Adding SPC_INDIRECT_DATA_OBJID failed\n");
PKCS7_free(p7); PKCS7_free(p7);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!sign_spc_indirect_data_content(p7, hash, ctx)) {
printf("Failed to set signed content\n");
return NULL; /* FAILED */
}
} }
if (ctx->options->nest) if (ctx->options->nest)
ctx->options->prevsig = cursig; ctx->options->prevsig = cursig;

View File

@ -1236,7 +1236,7 @@ static int trusted_cert(X509 *cert, int error) {
BIO_gets(bhash, (char *)mdbuf, EVP_MD_size(md)); BIO_gets(bhash, (char *)mdbuf, EVP_MD_size(md));
BIO_free_all(bhash); BIO_free_all(bhash);
hex = OPENSSL_buf2hexstr(mdbuf, (size_t)EVP_MD_size(md)); hex = OPENSSL_buf2hexstr(mdbuf, (long)EVP_MD_size(md));
if (!hex) { if (!hex) {
return 0; /* FAILED */ return 0; /* FAILED */
} }
@ -1746,6 +1746,13 @@ static int verify_authenticode(FILE_FORMAT_CTX *ctx, PKCS7 *p7, time_t time, X50
p7->d.sign->contents->d.other->value.sequence->length); p7->d.sign->contents->d.other->value.sequence->length);
} }
printf("\nSigning Certificate Chain:\n"); printf("\nSigning Certificate Chain:\n");
/*
* In the PKCS7_verify() function, the BIO *indata parameter refers to
* the signed data if the content is detached from p7.
* Otherwise, indata should be NULL, and then the signed data must be in p7.
* The OpenSSL error workaround is to put the inner content into BIO *indata parameter
* https://github.com/openssl/openssl/pull/22575
*/
if (!PKCS7_verify(p7, NULL, store, bio, NULL, 0)) { if (!PKCS7_verify(p7, NULL, store, bio, NULL, 0)) {
printf("\nPKCS7_verify error\n"); printf("\nPKCS7_verify error\n");
X509_STORE_free(store); X509_STORE_free(store);
@ -2304,21 +2311,25 @@ static int verify_member(FILE_FORMAT_CTX *ctx, CatalogAuthAttr *attribute)
ASN1_TYPE_free(content); ASN1_TYPE_free(content);
if (mdtype == -1) { if (mdtype == -1) {
printf("Failed to extract current message digest\n\n"); printf("Failed to extract current message digest\n\n");
SpcIndirectDataContent_free(idc);
return 1; /* FAILED */ return 1; /* FAILED */
} }
if (!ctx->format->digest_calc) { if (!ctx->format->digest_calc) {
printf("Unsupported method: digest_calc\n"); printf("Unsupported method: digest_calc\n");
SpcIndirectDataContent_free(idc);
return 1; /* FAILED */ return 1; /* FAILED */
} }
md = EVP_get_digestbynid(mdtype); md = EVP_get_digestbynid(mdtype);
cmdbuf = ctx->format->digest_calc(ctx, md); cmdbuf = ctx->format->digest_calc(ctx, md);
if (!cmdbuf) { if (!cmdbuf) {
printf("Failed to compute a message digest value\n\n"); printf("Failed to compute a message digest value\n\n");
return 1; /* Failed */ SpcIndirectDataContent_free(idc);
return 1; /* FAILED */
} }
mdlen = EVP_MD_size(EVP_get_digestbynid(mdtype)); mdlen = EVP_MD_size(EVP_get_digestbynid(mdtype));
if (memcmp(mdbuf, cmdbuf, (size_t)mdlen)) { if (memcmp(mdbuf, cmdbuf, (size_t)mdlen)) {
OPENSSL_free(cmdbuf); OPENSSL_free(cmdbuf);
SpcIndirectDataContent_free(idc);
return 1; /* FAILED */ return 1; /* FAILED */
} else { } else {
printf("Message digest algorithm : %s\n", OBJ_nid2sn(mdtype)); printf("Message digest algorithm : %s\n", OBJ_nid2sn(mdtype));

6
pe.c
View File

@ -462,11 +462,15 @@ static PKCS7 *pe_pkcs7_prepare(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata)
printf("Creating a new signature failed\n"); printf("Creating a new signature failed\n");
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!add_indirect_data_object(p7, hash, ctx)) { if (!add_indirect_data_object(p7)) {
printf("Adding SPC_INDIRECT_DATA_OBJID failed\n"); printf("Adding SPC_INDIRECT_DATA_OBJID failed\n");
PKCS7_free(p7); PKCS7_free(p7);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!sign_spc_indirect_data_content(p7, hash, ctx)) {
printf("Failed to set signed content\n");
return NULL; /* FAILED */
}
} }
if (ctx->options->nest) if (ctx->options->nest)
ctx->options->prevsig = cursig; ctx->options->prevsig = cursig;