/* * Copyright (C) 2012 Jeremy Kerr * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * In addition, as a special exception, the copyright holders give * permission to link the code of portions of this program with the OpenSSL * library under certain conditions as described in each individual source file, * and distribute linked combinations including the two. * * You must obey the GNU General Public License in all respects for all * of the code used other than OpenSSL. If you modify file(s) with this * exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do * so, delete this exception statement from your version. If you delete * this exception statement from all source files in the program, then * also delete it here. */ #include #include #include #include #include #include #include #include #include "idc.h" typedef struct idc_type_value { ASN1_OBJECT *type; ASN1_TYPE *value; } IDC_TYPE_VALUE; ASN1_SEQUENCE(IDC_TYPE_VALUE) = { ASN1_SIMPLE(IDC_TYPE_VALUE, type, ASN1_OBJECT), ASN1_OPT(IDC_TYPE_VALUE, value, ASN1_ANY), } ASN1_SEQUENCE_END(IDC_TYPE_VALUE); IMPLEMENT_ASN1_FUNCTIONS(IDC_TYPE_VALUE); typedef struct idc_string { int type; union { ASN1_BMPSTRING *unicode; ASN1_IA5STRING *ascii; } value; } IDC_STRING; ASN1_CHOICE(IDC_STRING) = { ASN1_IMP(IDC_STRING, value.unicode, ASN1_BMPSTRING, 0), ASN1_IMP(IDC_STRING, value.ascii, ASN1_IA5STRING, 1), } ASN1_CHOICE_END(IDC_STRING); IMPLEMENT_ASN1_FUNCTIONS(IDC_STRING); typedef struct idc_link { int type; union { ASN1_NULL *url; ASN1_NULL *moniker; IDC_STRING *file; } value; } IDC_LINK; ASN1_CHOICE(IDC_LINK) = { ASN1_IMP(IDC_LINK, value.url, ASN1_NULL, 0), ASN1_IMP(IDC_LINK, value.moniker, ASN1_NULL, 1), ASN1_EXP(IDC_LINK, value.file, IDC_STRING, 2), } ASN1_CHOICE_END(IDC_LINK); IMPLEMENT_ASN1_FUNCTIONS(IDC_LINK); typedef struct idc_pe_image_data { ASN1_BIT_STRING *flags; IDC_LINK *file; } IDC_PEID; ASN1_SEQUENCE(IDC_PEID) = { ASN1_SIMPLE(IDC_PEID, flags, ASN1_BIT_STRING), ASN1_EXP(IDC_PEID, file, IDC_LINK, 0), } ASN1_SEQUENCE_END(IDC_PEID); IMPLEMENT_ASN1_FUNCTIONS(IDC_PEID); typedef struct idc_digest { X509_ALGOR *alg; ASN1_OCTET_STRING *digest; } IDC_DIGEST; ASN1_SEQUENCE(IDC_DIGEST) = { ASN1_SIMPLE(IDC_DIGEST, alg, X509_ALGOR), ASN1_SIMPLE(IDC_DIGEST, digest, ASN1_OCTET_STRING), } ASN1_SEQUENCE_END(IDC_DIGEST) IMPLEMENT_ASN1_FUNCTIONS(IDC_DIGEST) typedef struct idc { IDC_TYPE_VALUE *data; IDC_DIGEST *digest; } IDC; ASN1_SEQUENCE(IDC) = { ASN1_SIMPLE(IDC, data, IDC_TYPE_VALUE), ASN1_SIMPLE(IDC, digest, IDC_DIGEST), } ASN1_SEQUENCE_END(IDC) IMPLEMENT_ASN1_FUNCTIONS(IDC) static int type_set_sequence(void *ctx, ASN1_TYPE *type, void *s, const ASN1_ITEM *it) { uint8_t *seq_data, *tmp; ASN1_OCTET_STRING *os; ASN1_STRING *seq = s; int len; os = ASN1_STRING_new(); len = ASN1_item_i2d((ASN1_VALUE *)seq, NULL, it); tmp = seq_data = talloc_array(ctx, uint8_t, len); ASN1_item_i2d((ASN1_VALUE *)seq, &tmp, it); ASN1_STRING_set(os, seq_data, len); ASN1_TYPE_set(type, V_ASN1_SEQUENCE, os); return 0; } const char obsolete[] = { 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x62, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x65, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e }; const char *sha256_str(const uint8_t *hash) { static char s[SHA256_DIGEST_LENGTH * 2 + 1]; int i; for (i = 0; i < SHA256_DIGEST_LENGTH; i++) snprintf(s + i * 2, 3, "%02x", hash[i]); return s; } int IDC_set(PKCS7 *p7, PKCS7_SIGNER_INFO *si, struct image *image) { uint8_t *buf, *tmp, sha[SHA256_DIGEST_LENGTH]; int idc_nid, peid_nid, len, rc; IDC_PEID *peid; ASN1_STRING *s; ASN1_TYPE *t; BIO *sigbio; IDC *idc; idc_nid = OBJ_create("1.3.6.1.4.1.311.2.1.4", "spcIndirectDataContext", "Indirect Data Context"); peid_nid = OBJ_create("1.3.6.1.4.1.311.2.1.15", "spcPEImageData", "PE Image Data"); image_hash_sha256(image, sha); idc = IDC_new(); peid = IDC_PEID_new(); peid->file = IDC_LINK_new(); peid->file->type = 2; peid->file->value.file = IDC_STRING_new(); peid->file->value.file->type = 0; peid->file->value.file->value.unicode = ASN1_STRING_new(); ASN1_STRING_set(peid->file->value.file->value.unicode, obsolete, sizeof(obsolete)); idc->data->type = OBJ_nid2obj(peid_nid); idc->data->value = ASN1_TYPE_new(); type_set_sequence(image, idc->data->value, peid, &IDC_PEID_it); idc->digest->alg->parameter = ASN1_TYPE_new(); idc->digest->alg->algorithm = OBJ_nid2obj(NID_sha256); idc->digest->alg->parameter->type = V_ASN1_NULL; ASN1_OCTET_STRING_set(idc->digest->digest, sha, sizeof(sha)); len = i2d_IDC(idc, NULL); tmp = buf = talloc_array(image, uint8_t, len); i2d_IDC(idc, &tmp); /* Add the contentType authenticated attribute */ PKCS7_add_signed_attribute(si, NID_pkcs9_contentType, V_ASN1_OBJECT, OBJ_nid2obj(idc_nid)); /* Because the PKCS7 lib has a hard time dealing with non-standard * data types, we create a temporary BIO to hold the signed data, so * that the top-level PKCS7 object calculates the correct hash... */ sigbio = PKCS7_dataInit(p7, NULL); BIO_write(sigbio, buf+2, len-2); /* ... then we finalise the p7 content, which does the actual * signing ... */ rc = PKCS7_dataFinal(p7, sigbio); if (!rc) { fprintf(stderr, "dataFinal failed\n"); ERR_print_errors_fp(stderr); return -1; } /* ... and we replace the content with the actual IDC ASN type. */ t = ASN1_TYPE_new(); s = ASN1_STRING_new(); ASN1_STRING_set(s, buf, len); ASN1_TYPE_set(t, V_ASN1_SEQUENCE, s); PKCS7_set0_type_other(p7->d.sign->contents, idc_nid, t); return 0; } struct idc *IDC_get(PKCS7 *p7, BIO *bio) { const unsigned char *buf, *idcbuf; ASN1_STRING *str; IDC *idc; /* extract the idc from the signed PKCS7 'other' data */ str = p7->d.sign->contents->d.other->value.asn1_string; idcbuf = buf = ASN1_STRING_data(str); idc = d2i_IDC(NULL, &buf, ASN1_STRING_length(str)); /* If we were passed a BIO, write the idc data, minus type and length, * to the BIO. This can be used to PKCS7_verify the idc */ if (bio) { uint32_t idclen; uint8_t tmp; tmp = idcbuf[1]; if (!(tmp & 0x80)) { idclen = tmp & 0x7f; idcbuf += 2; } else if ((tmp & 0x82) == 0x82) { idclen = (idcbuf[2] << 8) + idcbuf[3]; idcbuf += 4; } else { fprintf(stderr, "Invalid ASN.1 data in " "IndirectDataContext?\n"); return NULL; } BIO_write(bio, idcbuf, idclen); } return idc; } int IDC_check_hash(struct idc *idc, struct image *image) { unsigned char sha[SHA256_DIGEST_LENGTH]; const unsigned char *buf; ASN1_STRING *str; image_hash_sha256(image, sha); /* check hash algorithm sanity */ if (OBJ_cmp(idc->digest->alg->algorithm, OBJ_nid2obj(NID_sha256))) { fprintf(stderr, "Invalid algorithm type\n"); return -1; } str = idc->digest->digest; if (ASN1_STRING_length(str) != sizeof(sha)) { fprintf(stderr, "Invalid algorithm length\n"); return -1; } /* check hash against the one we calculated from the image */ buf = ASN1_STRING_data(str); if (memcmp(buf, sha, sizeof(sha))) { fprintf(stderr, "Hash doesn't match image\n"); fprintf(stderr, " got: %s\n", sha256_str(buf)); fprintf(stderr, " expecting: %s\n", sha256_str(sha)); return -1; } return 0; }