diff options
Diffstat (limited to 'crypto/heimdal/lib/hx509/cms.c')
-rw-r--r-- | crypto/heimdal/lib/hx509/cms.c | 579 |
1 files changed, 399 insertions, 180 deletions
diff --git a/crypto/heimdal/lib/hx509/cms.c b/crypto/heimdal/lib/hx509/cms.c index 80bcaac..4e0a2e0 100644 --- a/crypto/heimdal/lib/hx509/cms.c +++ b/crypto/heimdal/lib/hx509/cms.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan + * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -32,13 +32,12 @@ */ #include "hx_locl.h" -RCSID("$Id: cms.c 22327 2007-12-15 04:49:37Z lha $"); /** * @page page_cms CMS/PKCS7 message functions. * * CMS is defined in RFC 3369 and is an continuation of the RSA Labs - * standard PKCS7. The basic messages in CMS is + * standard PKCS7. The basic messages in CMS is * * - SignedData * Data signed with private key (RSA, DSA, ECDSA) or secret @@ -68,7 +67,7 @@ RCSID("$Id: cms.c 22327 2007-12-15 04:49:37Z lha $"); * der_free_octet_string(). * * @return Returns an hx509 error code. - * + * * @ingroup hx509_cms */ @@ -122,7 +121,7 @@ hx509_cms_wrap_ContentInfo(const heim_oid *oid, * diffrence between no data and the zero length data. * * @return Returns an hx509 error code. - * + * * @ingroup hx509_cms */ @@ -260,6 +259,7 @@ static int find_CMSIdentifier(hx509_context context, CMSIdentifier *client, hx509_certs certs, + time_t time_now, hx509_cert *signer_cert, int match) { @@ -292,7 +292,10 @@ find_CMSIdentifier(hx509_context context, q.match |= match; q.match |= HX509_QUERY_MATCH_TIME; - q.timenow = time(NULL); + if (time_now) + q.timenow = time_now; + else + q.timenow = time(NULL); ret = hx509_certs_find(context, certs, &q, &cert); if (ret == HX509_CERT_NOT_FOUND) { @@ -333,6 +336,7 @@ find_CMSIdentifier(hx509_context context, * @param length length of the data that data point to. * @param encryptedContent in case of detached signature, this * contains the actual encrypted data, othersize its should be NULL. + * @param time_now set the current time, if zero the library uses now as the date. * @param contentType output type oid, should be freed with der_free_oid(). * @param content the data, free with der_free_octet_string(). * @@ -346,6 +350,7 @@ hx509_cms_unenvelope(hx509_context context, const void *data, size_t length, const heim_octet_string *encryptedContent, + time_t time_now, heim_oid *contentType, heim_octet_string *content) { @@ -357,7 +362,8 @@ hx509_cms_unenvelope(hx509_context context, heim_octet_string *params, params_data; heim_octet_string ivec; size_t size; - int ret, i, matched = 0, findflags = 0; + int ret, matched = 0, findflags = 0; + size_t i; memset(&key, 0, sizeof(key)); @@ -407,7 +413,8 @@ hx509_cms_unenvelope(hx509_context context, ri = &ed.recipientInfos.val[i]; - ret = find_CMSIdentifier(context, &ri->rid, certs, &cert, + ret = find_CMSIdentifier(context, &ri->rid, certs, + time_now, &cert, HX509_QUERY_PRIVATE_KEY|findflags); if (ret) continue; @@ -466,7 +473,10 @@ hx509_cms_unenvelope(hx509_context context, ret = hx509_crypto_init(context, NULL, &ai->algorithm, &crypto); if (ret) goto out; - + + if (flags & HX509_CMS_UE_ALLOW_WEAK) + hx509_crypto_allow_weak(crypto); + if (params) { ret = hx509_crypto_set_params(context, crypto, params, &ivec); if (ret) { @@ -483,7 +493,7 @@ hx509_cms_unenvelope(hx509_context context, "of EnvelopedData"); goto out; } - + ret = hx509_crypto_decrypt(crypto, enccontent->data, enccontent->length, @@ -520,7 +530,10 @@ out: * used to RSA. * * @param context A hx509 context. - * @param flags flags to control the behavior, no flags today + * @param flags flags to control the behavior. + * - HX509_CMS_EV_NO_KU_CHECK - Dont check KU on certificate + * - HX509_CMS_EV_ALLOW_WEAK - Allow weak crytpo + * - HX509_CMS_EV_ID_NAME - prefer issuer name and serial number * @param cert Certificate to encrypt the EnvelopedData encryption key * with. * @param data pointer the data to encrypt. @@ -548,9 +561,9 @@ hx509_cms_envelope_1(hx509_context context, heim_octet_string ivec; heim_octet_string key; hx509_crypto crypto = NULL; + int ret, cmsidflag; EnvelopedData ed; size_t size; - int ret; memset(&ivec, 0, sizeof(ivec)); memset(&key, 0, sizeof(key)); @@ -558,16 +571,21 @@ hx509_cms_envelope_1(hx509_context context, memset(content, 0, sizeof(*content)); if (encryption_type == NULL) - encryption_type = oid_id_aes_256_cbc(); + encryption_type = &asn1_oid_id_aes_256_cbc; - ret = _hx509_check_key_usage(context, cert, 1 << 2, TRUE); - if (ret) - goto out; + if ((flags & HX509_CMS_EV_NO_KU_CHECK) == 0) { + ret = _hx509_check_key_usage(context, cert, 1 << 2, TRUE); + if (ret) + goto out; + } ret = hx509_crypto_init(context, NULL, encryption_type, &crypto); if (ret) goto out; + if (flags & HX509_CMS_EV_ALLOW_WEAK) + hx509_crypto_allow_weak(crypto); + ret = hx509_crypto_set_random_key(crypto, &key); if (ret) { hx509_set_error_string(context, 0, ret, @@ -602,7 +620,7 @@ hx509_cms_envelope_1(hx509_context context, "Failed to set crypto oid " "for EnvelopedData"); goto out; - } + } ALLOC(enc_alg->parameters, 1); if (enc_alg->parameters == NULL) { ret = ENOMEM; @@ -632,8 +650,15 @@ hx509_cms_envelope_1(hx509_context context, ri = &ed.recipientInfos.val[0]; - ri->version = 0; - ret = fill_CMSIdentifier(cert, CMS_ID_SKI, &ri->rid); + if (flags & HX509_CMS_EV_ID_NAME) { + ri->version = 0; + cmsidflag = CMS_ID_NAME; + } else { + ri->version = 2; + cmsidflag = CMS_ID_SKI; + } + + ret = fill_CMSIdentifier(cert, cmsidflag, &ri->rid); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to set CMS identifier info " @@ -641,7 +666,7 @@ hx509_cms_envelope_1(hx509_context context, goto out; } - ret = _hx509_cert_public_encrypt(context, + ret = hx509_cert_public_encrypt(context, &key, cert, &ri->keyEncryptionAlgorithm.algorithm, &ri->encryptedKey); @@ -694,7 +719,8 @@ out: static int any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs) { - int ret, i; + int ret; + size_t i; if (sd->certificates == NULL) return 0; @@ -702,8 +728,8 @@ any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs) for (i = 0; i < sd->certificates->len; i++) { hx509_cert c; - ret = hx509_cert_init_data(context, - sd->certificates->val[i].data, + ret = hx509_cert_init_data(context, + sd->certificates->val[i].data, sd->certificates->val[i].length, &c); if (ret) @@ -720,7 +746,7 @@ any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs) static const Attribute * find_attribute(const CMSAttributes *attr, const heim_oid *oid) { - int i; + size_t i; for (i = 0; i < attr->len; i++) if (der_heim_oid_cmp(&attr->val[i].type, oid) == 0) return &attr->val[i]; @@ -731,12 +757,16 @@ find_attribute(const CMSAttributes *attr, const heim_oid *oid) * Decode SignedData and verify that the signature is correct. * * @param context A hx509 context. - * @param ctx a hx509 version context - * @param data + * @param ctx a hx509 verify context. + * @param flags to control the behaivor of the function. + * - HX509_CMS_VS_NO_KU_CHECK - Don't check KeyUsage + * - HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH - allow oid mismatch + * - HX509_CMS_VS_ALLOW_ZERO_SIGNER - no signer, see below. + * @param data pointer to CMS SignedData encoded data. * @param length length of the data that data point to. - * @param signedContent + * @param signedContent external data used for signature. * @param pool certificate pool to build certificates paths. - * @param contentType free with der_free_oid() + * @param contentType free with der_free_oid(). * @param content the output of the function, free with * der_free_octet_string(). * @param signer_certs list of the cerficates used to sign this @@ -748,6 +778,7 @@ find_attribute(const CMSAttributes *attr, const heim_oid *oid) int hx509_cms_verify_signed(hx509_context context, hx509_verify_ctx ctx, + unsigned int flags, const void *data, size_t length, const heim_octet_string *signedContent, @@ -761,7 +792,8 @@ hx509_cms_verify_signed(hx509_context context, hx509_certs certs = NULL; SignedData sd; size_t size; - int ret, i, found_valid_sig; + int ret, found_valid_sig; + size_t i; *signer_certs = NULL; content->data = NULL; @@ -790,8 +822,15 @@ hx509_cms_verify_signed(hx509_context context, "Both external and internal SignedData"); goto out; } + if (sd.encapContentInfo.eContent) - signedContent = sd.encapContentInfo.eContent; + ret = der_copy_octet_string(sd.encapContentInfo.eContent, content); + else + ret = der_copy_octet_string(signedContent, content); + if (ret) { + hx509_set_error_string(context, 0, ret, "malloc: out of memory"); + goto out; + } ret = hx509_certs_init(context, "MEMORY:cms-cert-buffer", 0, NULL, &certs); @@ -816,7 +855,7 @@ hx509_cms_verify_signed(hx509_context context, } for (found_valid_sig = 0, i = 0; i < sd.signerInfos.len; i++) { - heim_octet_string *signed_data; + heim_octet_string signed_data; const heim_oid *match_oid; heim_oid decode_oid; @@ -831,14 +870,29 @@ hx509_cms_verify_signed(hx509_context context, continue; } - ret = find_CMSIdentifier(context, &signer_info->sid, certs, &cert, + ret = find_CMSIdentifier(context, &signer_info->sid, certs, + _hx509_verify_get_time(ctx), &cert, HX509_QUERY_KU_DIGITALSIGNATURE); - if (ret) - continue; + if (ret) { + /** + * If HX509_CMS_VS_NO_KU_CHECK is set, allow more liberal + * search for matching certificates by not considering + * KeyUsage bits on the certificates. + */ + if ((flags & HX509_CMS_VS_NO_KU_CHECK) == 0) + continue; + + ret = find_CMSIdentifier(context, &signer_info->sid, certs, + _hx509_verify_get_time(ctx), &cert, + 0); + if (ret) + continue; + + } if (signer_info->signedAttrs) { const Attribute *attr; - + CMSAttributes sa; heim_octet_string os; @@ -846,7 +900,7 @@ hx509_cms_verify_signed(hx509_context context, sa.len = signer_info->signedAttrs->len; /* verify that sigature exists */ - attr = find_attribute(&sa, oid_id_pkcs9_messageDigest()); + attr = find_attribute(&sa, &asn1_oid_id_pkcs9_messageDigest); if (attr == NULL) { ret = HX509_CRYPTO_SIGNATURE_MISSING; hx509_set_error_string(context, 0, ret, @@ -862,7 +916,7 @@ hx509_cms_verify_signed(hx509_context context, "messageDigest (signature)"); goto next_sigature; } - + ret = decode_MessageDigest(attr->value.val[0].data, attr->value.val[0].length, &os, @@ -877,7 +931,7 @@ hx509_cms_verify_signed(hx509_context context, ret = _hx509_verify_signature(context, NULL, &signer_info->digestAlgorithm, - signedContent, + content, &os); der_free_octet_string(&os); if (ret) { @@ -890,9 +944,9 @@ hx509_cms_verify_signed(hx509_context context, * Fetch content oid inside signedAttrs or set it to * id-pkcs7-data. */ - attr = find_attribute(&sa, oid_id_pkcs9_contentType()); + attr = find_attribute(&sa, &asn1_oid_id_pkcs9_contentType); if (attr == NULL) { - match_oid = oid_id_pkcs7_data(); + match_oid = &asn1_oid_id_pkcs7_data; } else { if (attr->value.len != 1) { ret = HX509_CMS_DATA_OID_MISMATCH; @@ -914,36 +968,36 @@ hx509_cms_verify_signed(hx509_context context, match_oid = &decode_oid; } - ALLOC(signed_data, 1); - if (signed_data == NULL) { - if (match_oid == &decode_oid) - der_free_oid(&decode_oid); - ret = ENOMEM; - hx509_clear_error_string(context); - goto next_sigature; - } - ASN1_MALLOC_ENCODE(CMSAttributes, - signed_data->data, - signed_data->length, + signed_data.data, + signed_data.length, &sa, &size, ret); if (ret) { if (match_oid == &decode_oid) der_free_oid(&decode_oid); - free(signed_data); hx509_clear_error_string(context); goto next_sigature; } - if (size != signed_data->length) + if (size != signed_data.length) _hx509_abort("internal ASN.1 encoder error"); } else { - signed_data = rk_UNCONST(signedContent); - match_oid = oid_id_pkcs7_data(); + signed_data.data = content->data; + signed_data.length = content->length; + match_oid = &asn1_oid_id_pkcs7_data; } - if (der_heim_oid_cmp(match_oid, &sd.encapContentInfo.eContentType)) { + /** + * If HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH, allow + * encapContentInfo mismatch with the oid in signedAttributes + * (or if no signedAttributes where use, pkcs7-data oid). + * This is only needed to work with broken CMS implementations + * that doesn't follow CMS signedAttributes rules. + */ + + if (der_heim_oid_cmp(match_oid, &sd.encapContentInfo.eContentType) && + (flags & HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH) == 0) { ret = HX509_CMS_DATA_OID_MISMATCH; hx509_set_error_string(context, 0, ret, "Oid in message mismatch from the expected"); @@ -955,23 +1009,28 @@ hx509_cms_verify_signed(hx509_context context, ret = hx509_verify_signature(context, cert, &signer_info->signatureAlgorithm, - signed_data, + &signed_data, &signer_info->signature); if (ret) hx509_set_error_string(context, HX509_ERROR_APPEND, ret, - "Failed to verify sigature in " + "Failed to verify signature in " "CMS SignedData"); } - if (signed_data != signedContent) { - der_free_octet_string(signed_data); - free(signed_data); - } + if (signer_info->signedAttrs) + free(signed_data.data); if (ret) goto next_sigature; - ret = hx509_verify_path(context, ctx, cert, certs); - if (ret) - goto next_sigature; + /** + * If HX509_CMS_VS_NO_VALIDATE flags is set, do not verify the + * signing certificates and leave that up to the caller. + */ + + if ((flags & HX509_CMS_VS_NO_VALIDATE) == 0) { + ret = hx509_verify_path(context, ctx, cert, certs); + if (ret) + goto next_sigature; + } ret = hx509_certs_add(context, *signer_certs, cert); if (ret) @@ -984,7 +1043,18 @@ hx509_cms_verify_signed(hx509_context context, hx509_cert_free(cert); cert = NULL; } - if (found_valid_sig == 0) { + /** + * If HX509_CMS_VS_ALLOW_ZERO_SIGNER is set, allow empty + * SignerInfo (no signatures). If SignedData have no signatures, + * the function will return 0 with signer_certs set to NULL. Zero + * signers is allowed by the standard, but since its only useful + * in corner cases, it make into a flag that the caller have to + * turn on. + */ + if (sd.signerInfos.len == 0 && (flags & HX509_CMS_VS_ALLOW_ZERO_SIGNER)) { + if (*signer_certs) + hx509_certs_free(signer_certs); + } else if (found_valid_sig == 0) { if (ret == 0) { ret = HX509_CMS_SIGNER_NOT_FOUND; hx509_set_error_string(context, 0, ret, @@ -999,20 +1069,13 @@ hx509_cms_verify_signed(hx509_context context, goto out; } - content->data = malloc(signedContent->length); - if (content->data == NULL) { - hx509_clear_error_string(context); - ret = ENOMEM; - goto out; - } - content->length = signedContent->length; - memcpy(content->data, signedContent->data, content->length); - out: free_SignedData(&sd); if (certs) hx509_certs_free(&certs); if (ret) { + if (content->data) + der_free_octet_string(content); if (*signer_certs) hx509_certs_free(signer_certs); der_free_oid(contentType); @@ -1053,7 +1116,7 @@ add_one_attribute(Attribute **attr, return 0; } - + /** * Decode SignedData and verify that the signature is correct. * @@ -1089,26 +1152,56 @@ hx509_cms_create_signed_1(hx509_context context, hx509_certs pool, heim_octet_string *signed_data) { - AlgorithmIdentifier digest; - hx509_name name; - SignerInfo *signer_info; - heim_octet_string buf, content, sigdata = { 0, NULL }; + hx509_certs certs; + int ret = 0; + + signed_data->data = NULL; + signed_data->length = 0; + + ret = hx509_certs_init(context, "MEMORY:certs", 0, NULL, &certs); + if (ret) + return ret; + ret = hx509_certs_add(context, certs, cert); + if (ret) + goto out; + + ret = hx509_cms_create_signed(context, flags, eContentType, data, length, + digest_alg, certs, peer, anchors, pool, + signed_data); + + out: + hx509_certs_free(&certs); + return ret; +} + +struct sigctx { SignedData sd; - int ret; + const AlgorithmIdentifier *digest_alg; + const heim_oid *eContentType; + heim_octet_string content; + hx509_peer_info peer; + int cmsidflag; + int leafonly; + hx509_certs certs; + hx509_certs anchors; + hx509_certs pool; +}; + +static int +sig_process(hx509_context context, void *ctx, hx509_cert cert) +{ + struct sigctx *sigctx = ctx; + heim_octet_string buf, sigdata = { 0, NULL }; + SignerInfo *signer_info = NULL; + AlgorithmIdentifier digest; size_t size; + void *ptr; + int ret; + SignedData *sd = &sigctx->sd; hx509_path path; - int cmsidflag = CMS_ID_SKI; - memset(&sd, 0, sizeof(sd)); - memset(&name, 0, sizeof(name)); - memset(&path, 0, sizeof(path)); memset(&digest, 0, sizeof(digest)); - - content.data = rk_UNCONST(data); - content.length = length; - - if (flags & HX509_CMS_SIGATURE_ID_NAME) - cmsidflag = CMS_ID_NAME; + memset(&path, 0, sizeof(path)); if (_hx509_cert_private_key(cert) == NULL) { hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING, @@ -1116,64 +1209,45 @@ hx509_cms_create_signed_1(hx509_context context, return HX509_PRIVATE_KEY_MISSING; } - if (digest_alg == NULL) { - ret = hx509_crypto_select(context, HX509_SELECT_DIGEST, - _hx509_cert_private_key(cert), peer, &digest); - } else { - ret = copy_AlgorithmIdentifier(digest_alg, &digest); + if (sigctx->digest_alg) { + ret = copy_AlgorithmIdentifier(sigctx->digest_alg, &digest); if (ret) hx509_clear_error_string(context); + } else { + ret = hx509_crypto_select(context, HX509_SELECT_DIGEST, + _hx509_cert_private_key(cert), + sigctx->peer, &digest); } if (ret) goto out; - sd.version = CMSVersion_v3; - - if (eContentType == NULL) - eContentType = oid_id_pkcs7_data(); - - der_copy_oid(eContentType, &sd.encapContentInfo.eContentType); - - /* */ - if ((flags & HX509_CMS_SIGATURE_DETACHED) == 0) { - ALLOC(sd.encapContentInfo.eContent, 1); - if (sd.encapContentInfo.eContent == NULL) { - hx509_clear_error_string(context); - ret = ENOMEM; - goto out; - } - - sd.encapContentInfo.eContent->data = malloc(length); - if (sd.encapContentInfo.eContent->data == NULL) { - hx509_clear_error_string(context); - ret = ENOMEM; - goto out; - } - memcpy(sd.encapContentInfo.eContent->data, data, length); - sd.encapContentInfo.eContent->length = length; - } + /* + * Allocate on more signerInfo and do the signature processing + */ - ALLOC_SEQ(&sd.signerInfos, 1); - if (sd.signerInfos.val == NULL) { - hx509_clear_error_string(context); + ptr = realloc(sd->signerInfos.val, + (sd->signerInfos.len + 1) * sizeof(sd->signerInfos.val[0])); + if (ptr == NULL) { ret = ENOMEM; goto out; } + sd->signerInfos.val = ptr; - signer_info = &sd.signerInfos.val[0]; + signer_info = &sd->signerInfos.val[sd->signerInfos.len]; + + memset(signer_info, 0, sizeof(*signer_info)); signer_info->version = 1; - ret = fill_CMSIdentifier(cert, cmsidflag, &signer_info->sid); + ret = fill_CMSIdentifier(cert, sigctx->cmsidflag, &signer_info->sid); if (ret) { hx509_clear_error_string(context); goto out; - } + } signer_info->signedAttrs = NULL; signer_info->unsignedAttrs = NULL; - ret = copy_AlgorithmIdentifier(&digest, &signer_info->digestAlgorithm); if (ret) { hx509_clear_error_string(context); @@ -1184,8 +1258,8 @@ hx509_cms_create_signed_1(hx509_context context, * If it isn't pkcs7-data send signedAttributes */ - if (der_heim_oid_cmp(eContentType, oid_id_pkcs7_data()) != 0) { - CMSAttributes sa; + if (der_heim_oid_cmp(sigctx->eContentType, &asn1_oid_id_pkcs7_data) != 0) { + CMSAttributes sa; heim_octet_string sig; ALLOC(signer_info->signedAttrs, 1); @@ -1197,7 +1271,7 @@ hx509_cms_create_signed_1(hx509_context context, ret = _hx509_create_signature(context, NULL, &digest, - &content, + &sigctx->content, NULL, &sig); if (ret) @@ -1219,9 +1293,10 @@ hx509_cms_create_signed_1(hx509_context context, ret = add_one_attribute(&signer_info->signedAttrs->val, &signer_info->signedAttrs->len, - oid_id_pkcs9_messageDigest(), + &asn1_oid_id_pkcs9_messageDigest, &buf); if (ret) { + free(buf.data); hx509_clear_error_string(context); goto out; } @@ -1230,7 +1305,7 @@ hx509_cms_create_signed_1(hx509_context context, ASN1_MALLOC_ENCODE(ContentType, buf.data, buf.length, - eContentType, + sigctx->eContentType, &size, ret); if (ret) @@ -1240,16 +1315,17 @@ hx509_cms_create_signed_1(hx509_context context, ret = add_one_attribute(&signer_info->signedAttrs->val, &signer_info->signedAttrs->len, - oid_id_pkcs9_contentType(), + &asn1_oid_id_pkcs9_contentType, &buf); if (ret) { + free(buf.data); hx509_clear_error_string(context); goto out; } sa.val = signer_info->signedAttrs->val; sa.len = signer_info->signedAttrs->len; - + ASN1_MALLOC_ENCODE(CMSAttributes, sigdata.data, sigdata.length, @@ -1263,16 +1339,15 @@ hx509_cms_create_signed_1(hx509_context context, if (size != sigdata.length) _hx509_abort("internal ASN.1 encoder error"); } else { - sigdata.data = content.data; - sigdata.length = content.length; + sigdata.data = sigctx->content.data; + sigdata.length = sigctx->content.length; } - { AlgorithmIdentifier sigalg; ret = hx509_crypto_select(context, HX509_SELECT_PUBLIC_SIG, - _hx509_cert_private_key(cert), peer, + _hx509_cert_private_key(cert), sigctx->peer, &sigalg); if (ret) goto out; @@ -1288,64 +1363,211 @@ hx509_cms_create_signed_1(hx509_context context, goto out; } - ALLOC_SEQ(&sd.digestAlgorithms, 1); - if (sd.digestAlgorithms.val == NULL) { - ret = ENOMEM; - hx509_clear_error_string(context); - goto out; - } - - ret = copy_AlgorithmIdentifier(&digest, &sd.digestAlgorithms.val[0]); - if (ret) { - hx509_clear_error_string(context); - goto out; - } + sigctx->sd.signerInfos.len++; + signer_info = NULL; /* * Provide best effort path */ - if (pool) { - _hx509_calculate_path(context, - HX509_CALCULATE_PATH_NO_ANCHOR, - time(NULL), - anchors, - 0, - cert, - pool, - &path); - } else - _hx509_path_append(context, &path, cert); + if (sigctx->certs) { + unsigned int i; + + if (sigctx->pool && sigctx->leafonly == 0) { + _hx509_calculate_path(context, + HX509_CALCULATE_PATH_NO_ANCHOR, + time(NULL), + sigctx->anchors, + 0, + cert, + sigctx->pool, + &path); + } else + _hx509_path_append(context, &path, cert); + + for (i = 0; i < path.len; i++) { + /* XXX remove dups */ + ret = hx509_certs_add(context, sigctx->certs, path.val[i]); + if (ret) { + hx509_clear_error_string(context); + goto out; + } + } + } + out: + if (signer_info) + free_SignerInfo(signer_info); + if (sigdata.data != sigctx->content.data) + der_free_octet_string(&sigdata); + _hx509_path_free(&path); + free_AlgorithmIdentifier(&digest); - if (path.len) { - int i; + return ret; +} + +static int +cert_process(hx509_context context, void *ctx, hx509_cert cert) +{ + struct sigctx *sigctx = ctx; + const unsigned int i = sigctx->sd.certificates->len; + void *ptr; + int ret; + + ptr = realloc(sigctx->sd.certificates->val, + (i + 1) * sizeof(sigctx->sd.certificates->val[0])); + if (ptr == NULL) + return ENOMEM; + sigctx->sd.certificates->val = ptr; - ALLOC(sd.certificates, 1); - if (sd.certificates == NULL) { + ret = hx509_cert_binary(context, cert, + &sigctx->sd.certificates->val[i]); + if (ret == 0) + sigctx->sd.certificates->len++; + + return ret; +} + +static int +cmp_AlgorithmIdentifier(const AlgorithmIdentifier *p, const AlgorithmIdentifier *q) +{ + return der_heim_oid_cmp(&p->algorithm, &q->algorithm); +} + +int +hx509_cms_create_signed(hx509_context context, + int flags, + const heim_oid *eContentType, + const void *data, size_t length, + const AlgorithmIdentifier *digest_alg, + hx509_certs certs, + hx509_peer_info peer, + hx509_certs anchors, + hx509_certs pool, + heim_octet_string *signed_data) +{ + unsigned int i, j; + hx509_name name; + int ret; + size_t size; + struct sigctx sigctx; + + memset(&sigctx, 0, sizeof(sigctx)); + memset(&name, 0, sizeof(name)); + + if (eContentType == NULL) + eContentType = &asn1_oid_id_pkcs7_data; + + sigctx.digest_alg = digest_alg; + sigctx.content.data = rk_UNCONST(data); + sigctx.content.length = length; + sigctx.eContentType = eContentType; + sigctx.peer = peer; + /** + * Use HX509_CMS_SIGNATURE_ID_NAME to preferred use of issuer name + * and serial number if possible. Otherwise subject key identifier + * will preferred. + */ + if (flags & HX509_CMS_SIGNATURE_ID_NAME) + sigctx.cmsidflag = CMS_ID_NAME; + else + sigctx.cmsidflag = CMS_ID_SKI; + + /** + * Use HX509_CMS_SIGNATURE_LEAF_ONLY to only request leaf + * certificates to be added to the SignedData. + */ + sigctx.leafonly = (flags & HX509_CMS_SIGNATURE_LEAF_ONLY) ? 1 : 0; + + /** + * Use HX509_CMS_NO_CERTS to make the SignedData contain no + * certificates, overrides HX509_CMS_SIGNATURE_LEAF_ONLY. + */ + + if ((flags & HX509_CMS_SIGNATURE_NO_CERTS) == 0) { + ret = hx509_certs_init(context, "MEMORY:certs", 0, NULL, &sigctx.certs); + if (ret) + return ret; + } + + sigctx.anchors = anchors; + sigctx.pool = pool; + + sigctx.sd.version = CMSVersion_v3; + + der_copy_oid(eContentType, &sigctx.sd.encapContentInfo.eContentType); + + /** + * Use HX509_CMS_SIGNATURE_DETACHED to create detached signatures. + */ + if ((flags & HX509_CMS_SIGNATURE_DETACHED) == 0) { + ALLOC(sigctx.sd.encapContentInfo.eContent, 1); + if (sigctx.sd.encapContentInfo.eContent == NULL) { hx509_clear_error_string(context); ret = ENOMEM; goto out; } - ALLOC_SEQ(sd.certificates, path.len); - if (sd.certificates->val == NULL) { + + sigctx.sd.encapContentInfo.eContent->data = malloc(length); + if (sigctx.sd.encapContentInfo.eContent->data == NULL) { hx509_clear_error_string(context); ret = ENOMEM; goto out; } + memcpy(sigctx.sd.encapContentInfo.eContent->data, data, length); + sigctx.sd.encapContentInfo.eContent->length = length; + } - for (i = 0; i < path.len; i++) { - ret = hx509_cert_binary(context, path.val[i], - &sd.certificates->val[i]); - if (ret) { - hx509_clear_error_string(context); - goto out; + /** + * Use HX509_CMS_SIGNATURE_NO_SIGNER to create no sigInfo (no + * signatures). + */ + if ((flags & HX509_CMS_SIGNATURE_NO_SIGNER) == 0) { + ret = hx509_certs_iter_f(context, certs, sig_process, &sigctx); + if (ret) + goto out; + } + + if (sigctx.sd.signerInfos.len) { + + /* + * For each signerInfo, collect all different digest types. + */ + for (i = 0; i < sigctx.sd.signerInfos.len; i++) { + AlgorithmIdentifier *di = + &sigctx.sd.signerInfos.val[i].digestAlgorithm; + + for (j = 0; j < sigctx.sd.digestAlgorithms.len; j++) + if (cmp_AlgorithmIdentifier(di, &sigctx.sd.digestAlgorithms.val[j]) == 0) + break; + if (j == sigctx.sd.digestAlgorithms.len) { + ret = add_DigestAlgorithmIdentifiers(&sigctx.sd.digestAlgorithms, di); + if (ret) { + hx509_clear_error_string(context); + goto out; + } } } } + /* + * Add certs we think are needed, build as part of sig_process + */ + if (sigctx.certs) { + ALLOC(sigctx.sd.certificates, 1); + if (sigctx.sd.certificates == NULL) { + hx509_clear_error_string(context); + ret = ENOMEM; + goto out; + } + + ret = hx509_certs_iter_f(context, sigctx.certs, cert_process, &sigctx); + if (ret) + goto out; + } + ASN1_MALLOC_ENCODE(SignedData, signed_data->data, signed_data->length, - &sd, &size, ret); + &sigctx.sd, &size, ret); if (ret) { hx509_clear_error_string(context); goto out; @@ -1354,11 +1576,8 @@ hx509_cms_create_signed_1(hx509_context context, _hx509_abort("internal ASN.1 encoder error"); out: - if (sigdata.data != content.data) - der_free_octet_string(&sigdata); - free_AlgorithmIdentifier(&digest); - _hx509_path_free(&path); - free_SignedData(&sd); + hx509_certs_free(&sigctx.certs); + free_SignedData(&sigctx.sd); return ret; } |