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; memset(&digest, 0, sizeof(digest)); memset(&path, 0, sizeof(path)); if (_hx509_cert_private_key(cert) == NULL) { hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING, "Private key missing for signing"); return HX509_PRIVATE_KEY_MISSING; } 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; /* * Allocate on more signerInfo and do the signature processing */ 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[sd->signerInfos.len]; memset(signer_info, 0, sizeof(*signer_info)); signer_info->version = 1; 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); goto out; } /* * If it isn't pkcs7-data send signedAttributes */ if (der_heim_oid_cmp(sigctx->eContentType, &asn1_oid_id_pkcs7_data) != 0) { CMSAttributes sa; heim_octet_string sig; ALLOC(signer_info->signedAttrs, 1); if (signer_info->signedAttrs == NULL) { ret = ENOMEM; goto out; } ret = _hx509_create_signature(context, NULL, &digest, &sigctx->content, NULL, &sig); if (ret) goto out; ASN1_MALLOC_ENCODE(MessageDigest, buf.data, buf.length, &sig, &size, ret); der_free_octet_string(&sig); if (ret) { hx509_clear_error_string(context); goto out; } if (size != buf.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_one_attribute(&signer_info->signedAttrs->val, &signer_info->signedAttrs->len, &asn1_oid_id_pkcs9_messageDigest, &buf); if (ret) { free(buf.data); hx509_clear_error_string(context); goto out; } ASN1_MALLOC_ENCODE(ContentType, buf.data, buf.length, sigctx->eContentType, &size, ret); if (ret) goto out; if (size != buf.length) _hx509_abort("internal ASN.1 encoder error"); ret = add_one_attribute(&signer_info->signedAttrs->val, &signer_info->signedAttrs->len, &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, &sa, &size, ret); if (ret) { hx509_clear_error_string(context); goto out; } if (size != sigdata.length) _hx509_abort("internal ASN.1 encoder error"); } else { 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), sigctx->peer, &sigalg); if (ret) goto out; ret = _hx509_create_signature(context, _hx509_cert_private_key(cert), &sigalg, &sigdata, &signer_info->signatureAlgorithm, &signer_info->signature); free_AlgorithmIdentifier(&sigalg); if (ret) goto out; } sigctx->sd.signerInfos.len++; signer_info = NULL; /* * Provide best effort path */ 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); return ret; }
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; } 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; } /** * 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, &sigctx.sd, &size, ret); if (ret) { hx509_clear_error_string(context); goto out; } if (signed_data->length != size) _hx509_abort("internal ASN.1 encoder error"); out: hx509_certs_free(&sigctx.certs); free_SignedData(&sigctx.sd); return ret; }
static int try_decrypt(hx509_context context, struct hx509_collector *collector, const AlgorithmIdentifier *alg, const EVP_CIPHER *c, const void *ivdata, const void *password, size_t passwordlen, const void *cipher, size_t len) { heim_octet_string clear; size_t keylen; void *key; int ret; keylen = EVP_CIPHER_key_length(c); key = malloc(keylen); if (key == NULL) { hx509_clear_error_string(context); return ENOMEM; } ret = EVP_BytesToKey(c, EVP_md5(), ivdata, password, passwordlen, 1, key, NULL); if (ret <= 0) { hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR, "Failed to do string2key for private key"); return HX509_CRYPTO_INTERNAL_ERROR; } clear.data = malloc(len); if (clear.data == NULL) { hx509_set_error_string(context, 0, ENOMEM, "Out of memory to decrypt for private key"); ret = ENOMEM; goto out; } clear.length = len; { EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init(&ctx); EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0); EVP_Cipher(&ctx, clear.data, cipher, len); EVP_CIPHER_CTX_cleanup(&ctx); } ret = _hx509_collector_private_key_add(context, collector, alg, NULL, &clear, NULL); memset(clear.data, 0, clear.length); free(clear.data); out: memset(key, 0, keylen); free(key); return ret; }
static int p11_init(hx509_context context, hx509_certs certs, void **data, int flags, const char *residue, hx509_lock lock) { CK_C_GetFunctionList getFuncs; struct p11_module *p; char *list, *str; int ret; *data = NULL; list = strdup(residue); if (list == NULL) return ENOMEM; p = calloc(1, sizeof(*p)); if (p == NULL) { free(list); return ENOMEM; } p->ref = 1; str = strchr(list, ','); if (str) *str++ = '\0'; while (str) { char *strnext; strnext = strchr(str, ','); if (strnext) *strnext++ = '\0'; #if 0 if (strncasecmp(str, "slot=", 5) == 0) p->selected_slot = atoi(str + 5); #endif str = strnext; } p->dl_handle = dlopen(list, RTLD_NOW); free(list); if (p->dl_handle == NULL) { ret = HX509_PKCS11_LOAD; hx509_set_error_string(context, 0, ret, "Failed to open %s: %s", list, dlerror()); goto out; } getFuncs = dlsym(p->dl_handle, "C_GetFunctionList"); if (getFuncs == NULL) { ret = HX509_PKCS11_LOAD; hx509_set_error_string(context, 0, ret, "C_GetFunctionList missing in %s: %s", list, dlerror()); goto out; } ret = (*getFuncs)(&p->funcs); if (ret) { ret = HX509_PKCS11_LOAD; hx509_set_error_string(context, 0, ret, "C_GetFunctionList failed in %s", list); goto out; } ret = P11FUNC(p, Initialize, (NULL_PTR)); if (ret != CKR_OK) { ret = HX509_PKCS11_TOKEN_CONFUSED; hx509_set_error_string(context, 0, ret, "Failed initialize the PKCS11 module"); goto out; } ret = P11FUNC(p, GetSlotList, (FALSE, NULL, &p->num_slots)); if (ret) { ret = HX509_PKCS11_TOKEN_CONFUSED; hx509_set_error_string(context, 0, ret, "Failed to get number of PKCS11 slots"); goto out; } if (p->num_slots == 0) { ret = HX509_PKCS11_NO_SLOT; hx509_set_error_string(context, 0, ret, "Selected PKCS11 module have no slots"); goto out; } { CK_SLOT_ID_PTR slot_ids; int i, num_tokens = 0; slot_ids = malloc(p->num_slots * sizeof(*slot_ids)); if (slot_ids == NULL) { hx509_clear_error_string(context); ret = ENOMEM; goto out; } ret = P11FUNC(p, GetSlotList, (FALSE, slot_ids, &p->num_slots)); if (ret) { free(slot_ids); hx509_set_error_string(context, 0, HX509_PKCS11_TOKEN_CONFUSED, "Failed getting slot-list from " "PKCS11 module"); ret = HX509_PKCS11_TOKEN_CONFUSED; goto out; } p->slot = calloc(p->num_slots, sizeof(p->slot[0])); if (p->slot == NULL) { free(slot_ids); hx509_set_error_string(context, 0, ENOMEM, "Failed to get memory for slot-list"); ret = ENOMEM; goto out; } for (i = 0; i < p->num_slots; i++) { ret = p11_init_slot(context, p, lock, slot_ids[i], i, &p->slot[i]); if (ret) break; if (p->slot[i].flags & P11_TOKEN_PRESENT) num_tokens++; } free(slot_ids); if (ret) goto out; if (num_tokens == 0) { ret = HX509_PKCS11_NO_TOKEN; goto out; } } *data = p; return 0; out: p11_release_module(p); return ret; }
static int parse_pem_private_key(hx509_context context, const char *fn, struct hx509_collector *c, const hx509_pem_header *headers, const void *data, size_t len, const AlgorithmIdentifier *ai) { int ret = 0; const char *enc; enc = hx509_pem_find_header(headers, "Proc-Type"); if (enc) { const char *dek; char *type, *iv; ssize_t ssize, size; void *ivdata; const EVP_CIPHER *cipher; const struct _hx509_password *pw; hx509_lock lock; int decrypted = 0; size_t i; lock = _hx509_collector_get_lock(c); if (lock == NULL) { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "Failed to get password for " "password protected file %s", fn); return HX509_ALG_NOT_SUPP; } if (strcmp(enc, "4,ENCRYPTED") != 0) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Private key encrypted in unknown method %s " "in file", enc, fn); hx509_clear_error_string(context); return HX509_PARSING_KEY_FAILED; } dek = hx509_pem_find_header(headers, "DEK-Info"); if (dek == NULL) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Encrypted private key missing DEK-Info"); return HX509_PARSING_KEY_FAILED; } type = strdup(dek); if (type == NULL) { hx509_clear_error_string(context); return ENOMEM; } iv = strchr(type, ','); if (iv == NULL) { free(type); hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "IV missing"); return HX509_PARSING_KEY_FAILED; } *iv++ = '\0'; size = strlen(iv); ivdata = malloc(size); if (ivdata == NULL) { hx509_clear_error_string(context); free(type); return ENOMEM; } cipher = EVP_get_cipherbyname(type); if (cipher == NULL) { free(ivdata); hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "Private key encrypted with " "unsupported cipher: %s", type); free(type); return HX509_ALG_NOT_SUPP; } #define PKCS5_SALT_LEN 8 ssize = hex_decode(iv, ivdata, size); free(type); type = NULL; iv = NULL; if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) { free(ivdata); hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Salt have wrong length in " "private key file"); return HX509_PARSING_KEY_FAILED; } pw = _hx509_lock_get_passwords(lock); if (pw != NULL) { const void *password; size_t passwordlen; for (i = 0; i < pw->len; i++) { password = pw->val[i]; passwordlen = strlen(password); ret = try_decrypt(context, c, ai, cipher, ivdata, password, passwordlen, data, len); if (ret == 0) { decrypted = 1; break; } } } if (!decrypted) { hx509_prompt prompt; char password[128]; memset(&prompt, 0, sizeof(prompt)); prompt.prompt = "Password for keyfile: "; prompt.type = HX509_PROMPT_TYPE_PASSWORD; prompt.reply.data = password; prompt.reply.length = sizeof(password); ret = hx509_lock_prompt(lock, &prompt); if (ret == 0) ret = try_decrypt(context, c, ai, cipher, ivdata, password, strlen(password), data, len); /* XXX add password to lock password collection ? */ memset(password, 0, sizeof(password)); } free(ivdata); } else { heim_octet_string keydata; keydata.data = rk_UNCONST(data); keydata.length = len; ret = _hx509_collector_private_key_add(context, c, ai, NULL, &keydata, NULL); } return ret; }
static int file_init_common(hx509_context context, hx509_certs certs, void **data, int flags, const char *residue, hx509_lock lock, outformat format) { char *p, *pnext; struct ks_file *ksf = NULL; hx509_private_key *keys = NULL; int ret; struct pem_ctx pem_ctx; pem_ctx.flags = flags; pem_ctx.c = NULL; *data = NULL; if (lock == NULL) lock = _hx509_empty_lock; ksf = calloc(1, sizeof(*ksf)); if (ksf == NULL) { hx509_clear_error_string(context); return ENOMEM; } ksf->format = format; ksf->fn = strdup(residue); if (ksf->fn == NULL) { hx509_clear_error_string(context); ret = ENOMEM; goto out; } /* * XXX this is broken, the function should parse the file before * overwriting it */ if (flags & HX509_CERTS_CREATE) { ret = hx509_certs_init(context, "MEMORY:ks-file-create", 0, lock, &ksf->certs); if (ret) goto out; *data = ksf; return 0; } ret = _hx509_collector_alloc(context, lock, &pem_ctx.c); if (ret) goto out; for (p = ksf->fn; p != NULL; p = pnext) { FILE *f; pnext = strchr(p, ','); if (pnext) *pnext++ = '\0'; if ((f = fopen(p, "r")) == NULL) { ret = ENOENT; hx509_set_error_string(context, 0, ret, "Failed to open PEM file \"%s\": %s", p, strerror(errno)); goto out; } rk_cloexec_file(f); ret = hx509_pem_read(context, f, pem_func, &pem_ctx); fclose(f); if (ret != 0 && ret != HX509_PARSING_KEY_FAILED) goto out; else if (ret == HX509_PARSING_KEY_FAILED) { size_t length; void *ptr; size_t i; ret = rk_undumpdata(p, &ptr, &length); if (ret) { hx509_clear_error_string(context); goto out; } for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) { const AlgorithmIdentifier *ai = NULL; if (formats[i].ai != NULL) ai = (*formats[i].ai)(); ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length, ai); if (ret == 0) break; } rk_xfree(ptr); if (ret) { hx509_clear_error_string(context); goto out; } } } ret = _hx509_collector_collect_certs(context, pem_ctx.c, &ksf->certs); if (ret) goto out; ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys); if (ret == 0) { int i; for (i = 0; keys[i]; i++) _hx509_certs_keys_add(context, ksf->certs, keys[i]); _hx509_certs_keys_free(context, keys); } out: if (ret == 0) *data = ksf; else { if (ksf->fn) free(ksf->fn); free(ksf); } if (pem_ctx.c) _hx509_collector_free(pem_ctx.c); return ret; }
int hx509_revoke_verify(hx509_context context, hx509_revoke_ctx ctx, hx509_certs certs, time_t now, hx509_cert cert, hx509_cert parent_cert) { const Certificate *c = _hx509_get_cert(cert); const Certificate *p = _hx509_get_cert(parent_cert); unsigned long i, j, k; int ret; hx509_clear_error_string(context); for (i = 0; i < ctx->ocsps.len; i++) { struct revoke_ocsp *ocsp = &ctx->ocsps.val[i]; struct stat sb; /* check this ocsp apply to this cert */ /* check if there is a newer version of the file */ ret = stat(ocsp->path, &sb); if (ret == 0 && ocsp->last_modfied != sb.st_mtime) { ret = load_ocsp(context, ocsp); if (ret) continue; } /* verify signature in ocsp if not already done */ if (ocsp->signer == NULL) { ret = verify_ocsp(context, ocsp, now, certs, parent_cert); if (ret) continue; } for (j = 0; j < ocsp->ocsp.tbsResponseData.responses.len; j++) { heim_octet_string os; ret = der_heim_integer_cmp(&ocsp->ocsp.tbsResponseData.responses.val[j].certID.serialNumber, &c->tbsCertificate.serialNumber); if (ret != 0) continue; /* verify issuer hashes hash */ ret = _hx509_verify_signature(context, NULL, &ocsp->ocsp.tbsResponseData.responses.val[i].certID.hashAlgorithm, &c->tbsCertificate.issuer._save, &ocsp->ocsp.tbsResponseData.responses.val[i].certID.issuerNameHash); if (ret != 0) continue; os.data = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.data; os.length = p->tbsCertificate.subjectPublicKeyInfo.subjectPublicKey.length / 8; ret = _hx509_verify_signature(context, NULL, &ocsp->ocsp.tbsResponseData.responses.val[j].certID.hashAlgorithm, &os, &ocsp->ocsp.tbsResponseData.responses.val[j].certID.issuerKeyHash); if (ret != 0) continue; switch (ocsp->ocsp.tbsResponseData.responses.val[j].certStatus.element) { case choice_OCSPCertStatus_good: break; case choice_OCSPCertStatus_revoked: hx509_set_error_string(context, 0, HX509_CERT_REVOKED, "Certificate revoked by issuer in OCSP"); return HX509_CERT_REVOKED; case choice_OCSPCertStatus_unknown: continue; } /* don't allow the update to be in the future */ if (ocsp->ocsp.tbsResponseData.responses.val[j].thisUpdate > now + context->ocsp_time_diff) continue; /* don't allow the next update to be in the past */ if (ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate) { if (*ocsp->ocsp.tbsResponseData.responses.val[j].nextUpdate < now) continue; } /* else should force a refetch, but can we ? */ return 0; } } for (i = 0; i < ctx->crls.len; i++) { struct revoke_crl *crl = &ctx->crls.val[i]; struct stat sb; int diff; /* check if cert.issuer == crls.val[i].crl.issuer */ ret = _hx509_name_cmp(&c->tbsCertificate.issuer, &crl->crl.tbsCertList.issuer, &diff); if (ret || diff) continue; ret = stat(crl->path, &sb); if (ret == 0 && crl->last_modfied != sb.st_mtime) { CRLCertificateList cl; ret = load_crl(crl->path, &crl->last_modfied, &cl); if (ret == 0) { free_CRLCertificateList(&crl->crl); crl->crl = cl; crl->verified = 0; crl->failed_verify = 0; } } if (crl->failed_verify) continue; /* verify signature in crl if not already done */ if (crl->verified == 0) { ret = verify_crl(context, ctx, &crl->crl, now, certs, parent_cert); if (ret) { crl->failed_verify = 1; continue; } crl->verified = 1; } if (crl->crl.tbsCertList.crlExtensions) { for (j = 0; j < crl->crl.tbsCertList.crlExtensions->len; j++) { if (crl->crl.tbsCertList.crlExtensions->val[j].critical) { hx509_set_error_string(context, 0, HX509_CRL_UNKNOWN_EXTENSION, "Unknown CRL extension"); return HX509_CRL_UNKNOWN_EXTENSION; } } } if (crl->crl.tbsCertList.revokedCertificates == NULL) return 0; /* check if cert is in crl */ for (j = 0; j < crl->crl.tbsCertList.revokedCertificates->len; j++) { time_t t; ret = der_heim_integer_cmp(&crl->crl.tbsCertList.revokedCertificates->val[j].userCertificate, &c->tbsCertificate.serialNumber); if (ret != 0) continue; t = _hx509_Time2time_t(&crl->crl.tbsCertList.revokedCertificates->val[j].revocationDate); if (t > now) continue; if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions) for (k = 0; k < crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->len; k++) if (crl->crl.tbsCertList.revokedCertificates->val[j].crlEntryExtensions->val[k].critical) return HX509_CRL_UNKNOWN_EXTENSION; hx509_set_error_string(context, 0, HX509_CERT_REVOKED, "Certificate revoked by issuer in CRL"); return HX509_CERT_REVOKED; } return 0; } if (context->flags & HX509_CTX_VERIFY_MISSING_OK) return 0; hx509_set_error_string(context, HX509_ERROR_APPEND, HX509_REVOKE_STATUS_MISSING, "No revoke status found for " "certificates"); return HX509_REVOKE_STATUS_MISSING; }
static int keychain_query(hx509_context context, hx509_certs certs, void *data, const hx509_query *query, hx509_cert *retcert) { CFArrayRef identities = NULL; hx509_cert cert = NULL; CFIndex n, count; int ret; int kdcLookupHack = 0; /* * First to course filtering using security framework .... */ #define FASTER_FLAGS (HX509_QUERY_MATCH_PERSISTENT|HX509_QUERY_PRIVATE_KEY) if ((query->match & FASTER_FLAGS) == 0) return HX509_UNIMPLEMENTED_OPERATION; CFMutableDictionaryRef secQuery = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); /* * XXX this is so broken, SecItem doesn't find the kdc certificte, * and kdc certificates happend to be searched by friendly name, * so find that and mundge on the structure. */ if ((query->match & HX509_QUERY_MATCH_FRIENDLY_NAME) && (query->match & HX509_QUERY_PRIVATE_KEY) && strcmp(query->friendlyname, "O=System Identity,CN=com.apple.kerberos.kdc") == 0) { ((hx509_query *)query)->match &= ~HX509_QUERY_PRIVATE_KEY; kdcLookupHack = 1; } if (kdcLookupHack || (query->match & HX509_QUERY_MATCH_PERSISTENT)) { CFDictionaryAddValue(secQuery, kSecClass, kSecClassCertificate); } else CFDictionaryAddValue(secQuery, kSecClass, kSecClassIdentity); CFDictionaryAddValue(secQuery, kSecReturnRef, kCFBooleanTrue); CFDictionaryAddValue(secQuery, kSecMatchLimit, kSecMatchLimitAll); if (query->match & HX509_QUERY_MATCH_PERSISTENT) { CFDataRef refdata = CFDataCreateWithBytesNoCopy(NULL, query->persistent->data, query->persistent->length, kCFAllocatorNull); CFDictionaryAddValue(secQuery, kSecValuePersistentRef, refdata); CFRelease(refdata); } OSStatus status = SecItemCopyMatching(secQuery, (CFTypeRef *)&identities); CFRelease(secQuery); if (status || identities == NULL) { hx509_clear_error_string(context); return HX509_CERT_NOT_FOUND; } heim_assert(CFArrayGetTypeID() == CFGetTypeID(identities), "return value not an array"); /* * ... now do hx509 filtering */ count = CFArrayGetCount(identities); for (n = 0; n < count; n++) { CFTypeRef secitem = (CFTypeRef)CFArrayGetValueAtIndex(identities, n); #ifndef __APPLE_TARGET_EMBEDDED__ if (query->match & HX509_QUERY_MATCH_PERSISTENT) { SecIdentityRef other = NULL; OSStatus osret; osret = SecIdentityCreateWithCertificate(NULL, (SecCertificateRef)secitem, &other); if (osret == noErr) { ret = hx509_cert_init_SecFramework(context, (void *)other, &cert); CFRelease(other); if (ret) continue; } else { ret = hx509_cert_init_SecFramework(context, (void *)secitem, &cert); if (ret) continue; } } else #endif { ret = hx509_cert_init_SecFramework(context, (void *)secitem, &cert); if (ret) continue; } if (_hx509_query_match_cert(context, query, cert)) { #ifndef __APPLE_TARGET_EMBEDDED__ /* certtool/keychain doesn't glue togheter the cert with keys for system keys */ if (kdcLookupHack) { SecIdentityRef other = NULL; OSStatus osret; osret = SecIdentityCreateWithCertificate(NULL, (SecCertificateRef)secitem, &other); if (osret == noErr) { hx509_cert_free(cert); ret = hx509_cert_init_SecFramework(context, other, &cert); CFRelease(other); if (ret) continue; } } #endif *retcert = cert; break; } hx509_cert_free(cert); } if (kdcLookupHack) ((hx509_query *)query)->match |= HX509_QUERY_PRIVATE_KEY; CFRelease(identities); if (*retcert == NULL) { hx509_clear_error_string(context); return HX509_CERT_NOT_FOUND; } return 0; }
int hx509_ocsp_verify(hx509_context context, time_t now, hx509_cert cert, int flags, const void *data, size_t length, time_t *expiration) { const Certificate *c = _hx509_get_cert(cert); OCSPBasicOCSPResponse basic; int ret; size_t i; if (now == 0) now = time(NULL); *expiration = 0; ret = parse_ocsp_basic(data, length, &basic); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to parse OCSP response"); return ret; } for (i = 0; i < basic.tbsResponseData.responses.len; i++) { ret = der_heim_integer_cmp(&basic.tbsResponseData.responses.val[i].certID.serialNumber, &c->tbsCertificate.serialNumber); if (ret != 0) continue; /* verify issuer hashes hash */ ret = _hx509_verify_signature(context, NULL, &basic.tbsResponseData.responses.val[i].certID.hashAlgorithm, &c->tbsCertificate.issuer._save, &basic.tbsResponseData.responses.val[i].certID.issuerNameHash); if (ret != 0) continue; switch (basic.tbsResponseData.responses.val[i].certStatus.element) { case choice_OCSPCertStatus_good: break; case choice_OCSPCertStatus_revoked: case choice_OCSPCertStatus_unknown: continue; } /* don't allow the update to be in the future */ if (basic.tbsResponseData.responses.val[i].thisUpdate > now + context->ocsp_time_diff) continue; /* don't allow the next update to be in the past */ if (basic.tbsResponseData.responses.val[i].nextUpdate) { if (*basic.tbsResponseData.responses.val[i].nextUpdate < now) continue; *expiration = *basic.tbsResponseData.responses.val[i].nextUpdate; } else *expiration = now; free_OCSPBasicOCSPResponse(&basic); return 0; } free_OCSPBasicOCSPResponse(&basic); { hx509_name name; char *subject; ret = hx509_cert_get_subject(cert, &name); if (ret) { hx509_clear_error_string(context); goto out; } ret = hx509_name_to_string(name, &subject); hx509_name_free(&name); if (ret) { hx509_clear_error_string(context); goto out; } hx509_set_error_string(context, 0, HX509_CERT_NOT_IN_OCSP, "Certificate %s not in OCSP response " "or not good", subject); free(subject); } out: return HX509_CERT_NOT_IN_OCSP; }
int hx509_crl_sign(hx509_context context, hx509_cert signer, hx509_crl crl, heim_octet_string *os) { const AlgorithmIdentifier *sigalg = _hx509_crypto_default_sig_alg; CRLCertificateList c; size_t size; int ret; hx509_private_key signerkey; memset(&c, 0, sizeof(c)); signerkey = _hx509_cert_private_key(signer); if (signerkey == NULL) { ret = HX509_PRIVATE_KEY_MISSING; hx509_set_error_string(context, 0, ret, "Private key missing for CRL signing"); return ret; } c.tbsCertList.version = malloc(sizeof(*c.tbsCertList.version)); if (c.tbsCertList.version == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } *c.tbsCertList.version = 1; ret = copy_AlgorithmIdentifier(sigalg, &c.tbsCertList.signature); if (ret) { hx509_clear_error_string(context); goto out; } ret = copy_Name(&_hx509_get_cert(signer)->tbsCertificate.issuer, &c.tbsCertList.issuer); if (ret) { hx509_clear_error_string(context); goto out; } c.tbsCertList.thisUpdate.element = choice_Time_generalTime; c.tbsCertList.thisUpdate.u.generalTime = time(NULL) - 24 * 3600; c.tbsCertList.nextUpdate = malloc(sizeof(*c.tbsCertList.nextUpdate)); if (c.tbsCertList.nextUpdate == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); ret = ENOMEM; goto out; } { time_t next = crl->expire; if (next == 0) next = time(NULL) + 24 * 3600 * 365; c.tbsCertList.nextUpdate->element = choice_Time_generalTime; c.tbsCertList.nextUpdate->u.generalTime = next; } c.tbsCertList.revokedCertificates = calloc(1, sizeof(*c.tbsCertList.revokedCertificates)); if (c.tbsCertList.revokedCertificates == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); ret = ENOMEM; goto out; } c.tbsCertList.crlExtensions = NULL; ret = hx509_certs_iter_f(context, crl->revoked, add_revoked, &c.tbsCertList); if (ret) goto out; /* if not revoked certs, remove OPTIONAL entry */ if (c.tbsCertList.revokedCertificates->len == 0) { free(c.tbsCertList.revokedCertificates); c.tbsCertList.revokedCertificates = NULL; } ASN1_MALLOC_ENCODE(TBSCRLCertList, os->data, os->length, &c.tbsCertList, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "failed to encode tbsCRL"); goto out; } if (size != os->length) _hx509_abort("internal ASN.1 encoder error"); ret = _hx509_create_signature_bitstring(context, signerkey, sigalg, os, &c.signatureAlgorithm, &c.signatureValue); free(os->data); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to sign CRL"); goto out; } ASN1_MALLOC_ENCODE(CRLCertificateList, os->data, os->length, &c, &size, ret); if (ret) { hx509_set_error_string(context, 0, ret, "failed to encode CRL"); goto out; } if (size != os->length) _hx509_abort("internal ASN.1 encoder error"); free_CRLCertificateList(&c); return 0; out: free_CRLCertificateList(&c); return ret; }
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, hx509_certs pool, heim_oid *contentType, heim_octet_string *content, hx509_certs *signer_certs) { SignerInfo *signer_info; hx509_cert cert = NULL; hx509_certs certs = NULL; SignedData sd; size_t size; int ret, i, found_valid_sig; *signer_certs = NULL; content->data = NULL; content->length = 0; contentType->length = 0; contentType->components = NULL; memset(&sd, 0, sizeof(sd)); ret = decode_SignedData(data, length, &sd, &size); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode SignedData"); goto out; } if (sd.encapContentInfo.eContent == NULL && signedContent == NULL) { ret = HX509_CMS_NO_DATA_AVAILABLE; hx509_set_error_string(context, 0, ret, "No content data in SignedData"); goto out; } if (sd.encapContentInfo.eContent && signedContent) { ret = HX509_CMS_NO_DATA_AVAILABLE; hx509_set_error_string(context, 0, ret, "Both external and internal SignedData"); goto out; } if (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); if (ret) goto out; ret = hx509_certs_init(context, "MEMORY:cms-signer-certs", 0, NULL, signer_certs); if (ret) goto out; /* XXX Check CMS version */ ret = any_to_certs(context, &sd, certs); if (ret) goto out; if (pool) { ret = hx509_certs_merge(context, certs, pool); if (ret) goto out; } for (found_valid_sig = 0, i = 0; i < sd.signerInfos.len; i++) { heim_octet_string signed_data; const heim_oid *match_oid; heim_oid decode_oid; signer_info = &sd.signerInfos.val[i]; match_oid = NULL; if (signer_info->signature.length == 0) { ret = HX509_CMS_MISSING_SIGNER_DATA; hx509_set_error_string(context, 0, ret, "SignerInfo %d in SignedData " "missing sigature", i); continue; } ret = find_CMSIdentifier(context, &signer_info->sid, certs, _hx509_verify_get_time(ctx), &cert, HX509_QUERY_KU_DIGITALSIGNATURE); 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; sa.val = signer_info->signedAttrs->val; sa.len = signer_info->signedAttrs->len; /* verify that sigature exists */ attr = find_attribute(&sa, &asn1_oid_id_pkcs9_messageDigest); if (attr == NULL) { ret = HX509_CRYPTO_SIGNATURE_MISSING; hx509_set_error_string(context, 0, ret, "SignerInfo have signed attributes " "but messageDigest (signature) " "is missing"); goto next_sigature; } if (attr->value.len != 1) { ret = HX509_CRYPTO_SIGNATURE_MISSING; hx509_set_error_string(context, 0, ret, "SignerInfo have more then one " "messageDigest (signature)"); goto next_sigature; } ret = decode_MessageDigest(attr->value.val[0].data, attr->value.val[0].length, &os, &size); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode " "messageDigest (signature)"); goto next_sigature; } ret = _hx509_verify_signature(context, NULL, &signer_info->digestAlgorithm, content, &os); der_free_octet_string(&os); if (ret) { hx509_set_error_string(context, HX509_ERROR_APPEND, ret, "Failed to verify messageDigest"); goto next_sigature; } /* * Fetch content oid inside signedAttrs or set it to * id-pkcs7-data. */ attr = find_attribute(&sa, &asn1_oid_id_pkcs9_contentType); if (attr == NULL) { match_oid = &asn1_oid_id_pkcs7_data; } else { if (attr->value.len != 1) { ret = HX509_CMS_DATA_OID_MISMATCH; hx509_set_error_string(context, 0, ret, "More then one oid in signedAttrs"); goto next_sigature; } ret = decode_ContentType(attr->value.val[0].data, attr->value.val[0].length, &decode_oid, &size); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode " "oid in signedAttrs"); goto next_sigature; } match_oid = &decode_oid; } ASN1_MALLOC_ENCODE(CMSAttributes, signed_data.data, signed_data.length, &sa, &size, ret); if (ret) { if (match_oid == &decode_oid) der_free_oid(&decode_oid); hx509_clear_error_string(context); goto next_sigature; } if (size != signed_data.length) _hx509_abort("internal ASN.1 encoder error"); } else { signed_data.data = content->data; signed_data.length = content->length; match_oid = &asn1_oid_id_pkcs7_data; } /** * 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"); } if (match_oid == &decode_oid) der_free_oid(&decode_oid); if (ret == 0) { ret = hx509_verify_signature(context, cert, &signer_info->signatureAlgorithm, &signed_data, &signer_info->signature); if (ret) hx509_set_error_string(context, HX509_ERROR_APPEND, ret, "Failed to verify signature in " "CMS SignedData"); } if (signer_info->signedAttrs) free(signed_data.data); 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) goto next_sigature; found_valid_sig++; next_sigature: if (cert) hx509_cert_free(cert); cert = NULL; } /** * 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, "No signers where found"); } goto out; } ret = der_copy_oid(&sd.encapContentInfo.eContentType, contentType); if (ret) { hx509_clear_error_string(context); goto out; } 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); der_free_octet_string(content); } return ret; }
static int find_CMSIdentifier(hx509_context context, CMSIdentifier *client, hx509_certs certs, time_t time_now, hx509_cert *signer_cert, int match) { hx509_query q; hx509_cert cert; Certificate c; int ret; memset(&c, 0, sizeof(c)); _hx509_query_clear(&q); *signer_cert = NULL; switch (client->element) { case choice_CMSIdentifier_issuerAndSerialNumber: q.serial = &client->u.issuerAndSerialNumber.serialNumber; q.issuer_name = &client->u.issuerAndSerialNumber.issuer; q.match = HX509_QUERY_MATCH_SERIALNUMBER|HX509_QUERY_MATCH_ISSUER_NAME; break; case choice_CMSIdentifier_subjectKeyIdentifier: q.subject_id = &client->u.subjectKeyIdentifier; q.match = HX509_QUERY_MATCH_SUBJECT_KEY_ID; break; default: hx509_set_error_string(context, 0, HX509_CMS_NO_RECIPIENT_CERTIFICATE, "unknown CMS identifier element"); return HX509_CMS_NO_RECIPIENT_CERTIFICATE; } q.match |= match; q.match |= HX509_QUERY_MATCH_TIME; 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) { char *str; ret = unparse_CMSIdentifier(context, client, &str); if (ret == 0) { hx509_set_error_string(context, 0, HX509_CMS_NO_RECIPIENT_CERTIFICATE, "Failed to find %s", str); } else hx509_clear_error_string(context); return HX509_CMS_NO_RECIPIENT_CERTIFICATE; } else if (ret) { hx509_set_error_string(context, HX509_ERROR_APPEND, HX509_CMS_NO_RECIPIENT_CERTIFICATE, "Failed to find CMS id in cert store"); return HX509_CMS_NO_RECIPIENT_CERTIFICATE; } *signer_cert = cert; return 0; }
int hx509_cms_decrypt_encrypted(hx509_context context, hx509_lock lock, const void *data, size_t length, heim_oid *contentType, heim_octet_string *content) { heim_octet_string cont; CMSEncryptedData ed; AlgorithmIdentifier *ai; int ret; memset(content, 0, sizeof(*content)); memset(&cont, 0, sizeof(cont)); ret = decode_CMSEncryptedData(data, length, &ed, NULL); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode CMSEncryptedData"); return ret; } if (ed.encryptedContentInfo.encryptedContent == NULL) { ret = HX509_CMS_NO_DATA_AVAILABLE; hx509_set_error_string(context, 0, ret, "No content in EncryptedData"); goto out; } ret = der_copy_oid(&ed.encryptedContentInfo.contentType, contentType); if (ret) { hx509_clear_error_string(context); goto out; } ai = &ed.encryptedContentInfo.contentEncryptionAlgorithm; if (ai->parameters == NULL) { ret = HX509_ALG_NOT_SUPP; hx509_clear_error_string(context); goto out; } ret = _hx509_pbe_decrypt(context, lock, ai, ed.encryptedContentInfo.encryptedContent, &cont); if (ret) goto out; *content = cont; out: if (ret) { if (cont.data) free(cont.data); } free_CMSEncryptedData(&ed); return ret; }
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; 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; 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; } 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; } /** * Use HX509_CMS_SIGNATURE_NO_SIGNER to create no sigInfo (no * signatures). */ if ((flags & HX509_CMS_SIGNATURE_NO_SIGNER) == 0) { ret = hx509_certs_iter(context, certs, sig_process, &sigctx); if (ret) goto out; } if (sigctx.sd.signerInfos.len) { ALLOC_SEQ(&sigctx.sd.digestAlgorithms, sigctx.sd.signerInfos.len); if (sigctx.sd.digestAlgorithms.val == NULL) { ret = ENOMEM; hx509_clear_error_string(context); goto out; } /* XXX remove dups */ for (i = 0; i < sigctx.sd.signerInfos.len; i++) { AlgorithmIdentifier *di = &sigctx.sd.signerInfos.val[i].digestAlgorithm; ret = copy_AlgorithmIdentifier(di, &sigctx.sd.digestAlgorithms.val[i]); if (ret) { hx509_clear_error_string(context); goto out; } } } 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(context, sigctx.certs, cert_process, &sigctx); if (ret) goto out; } ASN1_MALLOC_ENCODE(SignedData, signed_data->data, signed_data->length, &sigctx.sd, &size, ret); if (ret) { hx509_clear_error_string(context); goto out; } if (signed_data->length != size) _hx509_abort("internal ASN.1 encoder error"); out: hx509_certs_free(&sigctx.certs); free_SignedData(&sigctx.sd); return ret; }
static int ecdsa_create_signature(hx509_context context, const struct signature_alg *sig_alg, const hx509_private_key signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, AlgorithmIdentifier *signatureAlgorithm, heim_octet_string *sig) { const AlgorithmIdentifier *digest_alg; heim_octet_string indata; const heim_oid *sig_oid; unsigned int siglen; int ret; if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) != 0) _hx509_abort("internal error passing private key to wrong ops"); sig_oid = sig_alg->sig_oid; digest_alg = sig_alg->digest_alg; if (signatureAlgorithm) { ret = _hx509_set_digest_alg(signatureAlgorithm, sig_oid, "\x05\x00", 2); if (ret) { hx509_clear_error_string(context); return ret; } } ret = _hx509_create_signature(context, NULL, digest_alg, data, NULL, &indata); if (ret) goto error; sig->length = ECDSA_size(signer->private_key.ecdsa); sig->data = malloc(sig->length); if (sig->data == NULL) { der_free_octet_string(&indata); ret = ENOMEM; hx509_set_error_string(context, 0, ret, "out of memory"); goto error; } siglen = sig->length; ret = ECDSA_sign(-1, indata.data, indata.length, sig->data, &siglen, signer->private_key.ecdsa); der_free_octet_string(&indata); if (ret != 1) { ret = HX509_CMS_FAILED_CREATE_SIGATURE; hx509_set_error_string(context, 0, ret, "ECDSA sign failed: %d", ret); goto error; } if (siglen > sig->length) _hx509_abort("ECDSA signature prelen longer the output len"); sig->length = siglen; return 0; error: if (signatureAlgorithm) free_AlgorithmIdentifier(signatureAlgorithm); return ret; }