/* * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "hx_locl.h" struct hx509_crypto; struct signature_alg; struct hx509_generate_private_context { const heim_oid *key_oid; int isCA; unsigned long num_bits; }; struct hx509_private_key_ops { const char *pemtype; const heim_oid *key_oid; int (*available)(const hx509_private_key, const AlgorithmIdentifier *); int (*get_spki)(hx509_context, const hx509_private_key, SubjectPublicKeyInfo *); int (*export)(hx509_context context, const hx509_private_key, hx509_key_format_t, heim_octet_string *); int (*import)(hx509_context, const AlgorithmIdentifier *, const void *, size_t, hx509_key_format_t, hx509_private_key); int (*generate_private_key)(hx509_context, struct hx509_generate_private_context *, hx509_private_key); BIGNUM *(*get_internal)(hx509_context, hx509_private_key, const char *); }; struct hx509_private_key { unsigned int ref; const struct signature_alg *md; const heim_oid *signature_alg; union { RSA *rsa; void *keydata; #ifdef HAVE_OPENSSL EC_KEY *ecdsa; #endif } private_key; hx509_private_key_ops *ops; }; /* * */ struct signature_alg { const char *name; const heim_oid *sig_oid; const AlgorithmIdentifier *sig_alg; const heim_oid *key_oid; const AlgorithmIdentifier *digest_alg; int flags; #define PROVIDE_CONF 0x1 #define REQUIRE_SIGNER 0x2 #define SELF_SIGNED_OK 0x4 #define SIG_DIGEST 0x100 #define SIG_PUBLIC_SIG 0x200 #define SIG_SECRET 0x400 #define RA_RSA_USES_DIGEST_INFO 0x1000000 time_t best_before; /* refuse signature made after best before date */ const EVP_MD *(*evp_md)(void); int (*verify_signature)(hx509_context context, const struct signature_alg *, const Certificate *, const AlgorithmIdentifier *, const heim_octet_string *, const heim_octet_string *); int (*create_signature)(hx509_context, const struct signature_alg *, const hx509_private_key, const AlgorithmIdentifier *, const heim_octet_string *, AlgorithmIdentifier *, heim_octet_string *); int digest_size; }; static const struct signature_alg * find_sig_alg(const heim_oid *oid); /* * */ static const heim_octet_string null_entry_oid = { 2, rk_UNCONST("\x05\x00") }; static const unsigned sha512_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 3 }; const AlgorithmIdentifier _hx509_signature_sha512_data = { { 9, rk_UNCONST(sha512_oid_tree) }, rk_UNCONST(&null_entry_oid) }; static const unsigned sha384_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 2 }; const AlgorithmIdentifier _hx509_signature_sha384_data = { { 9, rk_UNCONST(sha384_oid_tree) }, rk_UNCONST(&null_entry_oid) }; static const unsigned sha256_oid_tree[] = { 2, 16, 840, 1, 101, 3, 4, 2, 1 }; const AlgorithmIdentifier _hx509_signature_sha256_data = { { 9, rk_UNCONST(sha256_oid_tree) }, rk_UNCONST(&null_entry_oid) }; static const unsigned sha1_oid_tree[] = { 1, 3, 14, 3, 2, 26 }; const AlgorithmIdentifier _hx509_signature_sha1_data = { { 6, rk_UNCONST(sha1_oid_tree) }, rk_UNCONST(&null_entry_oid) }; static const unsigned md5_oid_tree[] = { 1, 2, 840, 113549, 2, 5 }; const AlgorithmIdentifier _hx509_signature_md5_data = { { 6, rk_UNCONST(md5_oid_tree) }, rk_UNCONST(&null_entry_oid) }; static const unsigned ecPublicKey[] ={ 1, 2, 840, 10045, 2, 1 }; const AlgorithmIdentifier _hx509_signature_ecPublicKey = { { 6, rk_UNCONST(ecPublicKey) }, NULL }; static const unsigned ecdsa_with_sha256_oid[] ={ 1, 2, 840, 10045, 4, 3, 2 }; const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha256_data = { { 7, rk_UNCONST(ecdsa_with_sha256_oid) }, NULL }; static const unsigned ecdsa_with_sha1_oid[] ={ 1, 2, 840, 10045, 4, 1 }; const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha1_data = { { 6, rk_UNCONST(ecdsa_with_sha1_oid) }, NULL }; static const unsigned rsa_with_sha512_oid[] ={ 1, 2, 840, 113549, 1, 1, 13 }; const AlgorithmIdentifier _hx509_signature_rsa_with_sha512_data = { { 7, rk_UNCONST(rsa_with_sha512_oid) }, NULL }; static const unsigned rsa_with_sha384_oid[] ={ 1, 2, 840, 113549, 1, 1, 12 }; const AlgorithmIdentifier _hx509_signature_rsa_with_sha384_data = { { 7, rk_UNCONST(rsa_with_sha384_oid) }, NULL }; static const unsigned rsa_with_sha256_oid[] ={ 1, 2, 840, 113549, 1, 1, 11 }; const AlgorithmIdentifier _hx509_signature_rsa_with_sha256_data = { { 7, rk_UNCONST(rsa_with_sha256_oid) }, NULL }; static const unsigned rsa_with_sha1_oid[] ={ 1, 2, 840, 113549, 1, 1, 5 }; const AlgorithmIdentifier _hx509_signature_rsa_with_sha1_data = { { 7, rk_UNCONST(rsa_with_sha1_oid) }, NULL }; static const unsigned rsa_with_md5_oid[] ={ 1, 2, 840, 113549, 1, 1, 4 }; const AlgorithmIdentifier _hx509_signature_rsa_with_md5_data = { { 7, rk_UNCONST(rsa_with_md5_oid) }, NULL }; static const unsigned rsa_oid[] ={ 1, 2, 840, 113549, 1, 1, 1 }; const AlgorithmIdentifier _hx509_signature_rsa_data = { { 7, rk_UNCONST(rsa_oid) }, NULL }; static const unsigned rsa_pkcs1_x509_oid[] ={ 1, 2, 752, 43, 16, 1 }; const AlgorithmIdentifier _hx509_signature_rsa_pkcs1_x509_data = { { 6, rk_UNCONST(rsa_pkcs1_x509_oid) }, NULL }; static const unsigned des_rsdi_ede3_cbc_oid[] ={ 1, 2, 840, 113549, 3, 7 }; const AlgorithmIdentifier _hx509_des_rsdi_ede3_cbc_oid = { { 6, rk_UNCONST(des_rsdi_ede3_cbc_oid) }, NULL }; static const unsigned aes128_cbc_oid[] ={ 2, 16, 840, 1, 101, 3, 4, 1, 2 }; const AlgorithmIdentifier _hx509_crypto_aes128_cbc_data = { { 9, rk_UNCONST(aes128_cbc_oid) }, NULL }; static const unsigned aes256_cbc_oid[] ={ 2, 16, 840, 1, 101, 3, 4, 1, 42 }; const AlgorithmIdentifier _hx509_crypto_aes256_cbc_data = { { 9, rk_UNCONST(aes256_cbc_oid) }, NULL }; /* * */ static BIGNUM * heim_int2BN(const heim_integer *i) { BIGNUM *bn; bn = BN_bin2bn(i->data, i->length, NULL); BN_set_negative(bn, i->negative); return bn; } /* * */ static int set_digest_alg(DigestAlgorithmIdentifier *id, const heim_oid *oid, const void *param, size_t length) { int ret; if (param) { id->parameters = malloc(sizeof(*id->parameters)); if (id->parameters == NULL) return ENOMEM; id->parameters->data = malloc(length); if (id->parameters->data == NULL) { free(id->parameters); id->parameters = NULL; return ENOMEM; } memcpy(id->parameters->data, param, length); id->parameters->length = length; } else id->parameters = NULL; ret = der_copy_oid(oid, &id->algorithm); if (ret) { if (id->parameters) { free(id->parameters->data); free(id->parameters); id->parameters = NULL; } return ret; } return 0; } #ifdef HAVE_OPENSSL static int heim_oid2ecnid(heim_oid *oid) { /* * Now map to openssl OID fun */ if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP256R1) == 0) return NID_X9_62_prime256v1; else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R1) == 0) return NID_secp160r1; else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R2) == 0) return NID_secp160r2; return -1; } static int parse_ECParameters(hx509_context context, heim_octet_string *parameters, int *nid) { ECParameters ecparam; size_t size; int ret; if (parameters == NULL) { ret = HX509_PARSING_KEY_FAILED; hx509_set_error_string(context, 0, ret, "EC parameters missing"); return ret; } ret = decode_ECParameters(parameters->data, parameters->length, &ecparam, &size); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode EC parameters"); return ret; } if (ecparam.element != choice_ECParameters_namedCurve) { free_ECParameters(&ecparam); hx509_set_error_string(context, 0, ret, "EC parameters is not a named curve"); return HX509_CRYPTO_SIG_INVALID_FORMAT; } *nid = heim_oid2ecnid(&ecparam.u.namedCurve); free_ECParameters(&ecparam); if (*nid == -1) { hx509_set_error_string(context, 0, ret, "Failed to find matcing NID for EC curve"); return HX509_CRYPTO_SIG_INVALID_FORMAT; } return 0; } /* * */ static int ecdsa_verify_signature(hx509_context context, const struct signature_alg *sig_alg, const Certificate *signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, const heim_octet_string *sig) { const AlgorithmIdentifier *digest_alg; const SubjectPublicKeyInfo *spi; heim_octet_string digest; int ret; EC_KEY *key = NULL; int groupnid; EC_GROUP *group; const unsigned char *p; long len; digest_alg = sig_alg->digest_alg; ret = _hx509_create_signature(context, NULL, digest_alg, data, NULL, &digest); if (ret) return ret; /* set up EC KEY */ spi = &signer->tbsCertificate.subjectPublicKeyInfo; if (der_heim_oid_cmp(&spi->algorithm.algorithm, ASN1_OID_ID_ECPUBLICKEY) != 0) return HX509_CRYPTO_SIG_INVALID_FORMAT; #ifdef HAVE_OPENSSL /* * Find the group id */ ret = parse_ECParameters(context, spi->algorithm.parameters, &groupnid); if (ret) { der_free_octet_string(&digest); return ret; } /* * Create group, key, parse key */ key = EC_KEY_new(); group = EC_GROUP_new_by_curve_name(groupnid); EC_KEY_set_group(key, group); EC_GROUP_free(group); p = spi->subjectPublicKey.data; len = spi->subjectPublicKey.length / 8; if (o2i_ECPublicKey(&key, &p, len) == NULL) { EC_KEY_free(key); return HX509_CRYPTO_SIG_INVALID_FORMAT; } #else key = SubjectPublicKeyInfo2EC_KEY(spi); #endif ret = ECDSA_verify(-1, digest.data, digest.length, sig->data, sig->length, key); der_free_octet_string(&digest); EC_KEY_free(key); if (ret != 1) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; return ret; } return 0; } 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 = set_digest_alg(signatureAlgorithm, sig_oid, "\x05\x00", 2); if (ret) { hx509_clear_error_string(context); goto error; } } ret = _hx509_create_signature(context, NULL, digest_alg, data, NULL, &indata); if (ret) { if (signatureAlgorithm) free_AlgorithmIdentifier(signatureAlgorithm); 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; } static int ecdsa_available(const hx509_private_key signer, const AlgorithmIdentifier *sig_alg) { const struct signature_alg *sig; const EC_GROUP *group; BN_CTX *bnctx = NULL; BIGNUM *order = NULL; int ret = 0; if (der_heim_oid_cmp(signer->ops->key_oid, &asn1_oid_id_ecPublicKey) != 0) _hx509_abort("internal error passing private key to wrong ops"); sig = find_sig_alg(&sig_alg->algorithm); if (sig == NULL || sig->digest_size == 0) return 0; group = EC_KEY_get0_group(signer->private_key.ecdsa); if (group == NULL) return 0; bnctx = BN_CTX_new(); order = BN_new(); if (order == NULL) goto err; if (EC_GROUP_get_order(group, order, bnctx) != 1) goto err; if (BN_num_bytes(order) > sig->digest_size) ret = 1; err: if (bnctx) BN_CTX_free(bnctx); if (order) BN_clear_free(order); return ret; } #endif /* HAVE_OPENSSL */ /* * */ static int rsa_verify_signature(hx509_context context, const struct signature_alg *sig_alg, const Certificate *signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, const heim_octet_string *sig) { const SubjectPublicKeyInfo *spi; DigestInfo di; unsigned char *to; int tosize, retsize; int ret; RSA *rsa; size_t size; const unsigned char *p; memset(&di, 0, sizeof(di)); spi = &signer->tbsCertificate.subjectPublicKeyInfo; p = spi->subjectPublicKey.data; size = spi->subjectPublicKey.length / 8; rsa = d2i_RSAPublicKey(NULL, &p, size); if (rsa == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } tosize = RSA_size(rsa); to = malloc(tosize); if (to == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } retsize = RSA_public_decrypt(sig->length, (unsigned char *)sig->data, to, rsa, RSA_PKCS1_PADDING); if (retsize <= 0) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "RSA public decrypt failed: %d", retsize); free(to); goto out; } if (retsize > tosize) _hx509_abort("internal rsa decryption failure: ret > tosize"); if (sig_alg->flags & RA_RSA_USES_DIGEST_INFO) { ret = decode_DigestInfo(to, retsize, &di, &size); free(to); if (ret) { goto out; } /* Check for extra data inside the sigature */ if (size != (size_t)retsize) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "size from decryption mismatch"); goto out; } if (sig_alg->digest_alg && der_heim_oid_cmp(&di.digestAlgorithm.algorithm, &sig_alg->digest_alg->algorithm) != 0) { ret = HX509_CRYPTO_OID_MISMATCH; hx509_set_error_string(context, 0, ret, "object identifier in RSA sig mismatch"); goto out; } /* verify that the parameters are NULL or the NULL-type */ if (di.digestAlgorithm.parameters != NULL && (di.digestAlgorithm.parameters->length != 2 || memcmp(di.digestAlgorithm.parameters->data, "\x05\x00", 2) != 0)) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "Extra parameters inside RSA signature"); goto out; } ret = _hx509_verify_signature(context, NULL, &di.digestAlgorithm, data, &di.digest); } else { if ((size_t)retsize != data->length || ct_memcmp(to, data->data, retsize) != 0) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "RSA Signature incorrect"); goto out; } free(to); } ret = 0; out: free_DigestInfo(&di); if (rsa) RSA_free(rsa); return ret; } static int rsa_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; size_t size; int ret; if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) != 0) return HX509_ALG_NOT_SUPP; if (alg) sig_oid = &alg->algorithm; else sig_oid = signer->signature_alg; if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA512WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_sha512(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA384WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_sha384(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA256WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_sha256(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_sha1(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_MD5WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_md5(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_MD5WITHRSAENCRYPTION) == 0) { digest_alg = hx509_signature_md5(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_DSA_WITH_SHA1) == 0) { digest_alg = hx509_signature_sha1(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) == 0) { digest_alg = hx509_signature_sha1(); } else if (der_heim_oid_cmp(sig_oid, ASN1_OID_ID_HEIM_RSA_PKCS1_X509) == 0) { digest_alg = NULL; } else return HX509_ALG_NOT_SUPP; if (signatureAlgorithm) { ret = set_digest_alg(signatureAlgorithm, sig_oid, "\x05\x00", 2); if (ret) { hx509_clear_error_string(context); return ret; } } if (digest_alg) { DigestInfo di; memset(&di, 0, sizeof(di)); ret = _hx509_create_signature(context, NULL, digest_alg, data, &di.digestAlgorithm, &di.digest); if (ret) return ret; ASN1_MALLOC_ENCODE(DigestInfo, indata.data, indata.length, &di, &size, ret); free_DigestInfo(&di); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); return ret; } if (indata.length != size) _hx509_abort("internal ASN.1 encoder error"); } else { indata = *data; } sig->length = RSA_size(signer->private_key.rsa); sig->data = malloc(sig->length); if (sig->data == NULL) { der_free_octet_string(&indata); hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } ret = RSA_private_encrypt(indata.length, indata.data, sig->data, signer->private_key.rsa, RSA_PKCS1_PADDING); if (indata.data != data->data) der_free_octet_string(&indata); if (ret <= 0) { ret = HX509_CMS_FAILED_CREATE_SIGATURE; hx509_set_error_string(context, 0, ret, "RSA private encrypt failed: %d", ret); return ret; } if ((size_t)ret > sig->length) _hx509_abort("RSA signature prelen longer the output len"); sig->length = ret; return 0; } static int rsa_private_key_import(hx509_context context, const AlgorithmIdentifier *keyai, const void *data, size_t len, hx509_key_format_t format, hx509_private_key private_key) { switch (format) { case HX509_KEY_FORMAT_DER: { const unsigned char *p = data; private_key->private_key.rsa = d2i_RSAPrivateKey(NULL, &p, len); if (private_key->private_key.rsa == NULL) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Failed to parse RSA key"); return HX509_PARSING_KEY_FAILED; } private_key->signature_alg = ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION; break; } default: return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; } return 0; } static int rsa_private_key2SPKI(hx509_context context, hx509_private_key private_key, SubjectPublicKeyInfo *spki) { int len, ret; memset(spki, 0, sizeof(*spki)); len = i2d_RSAPublicKey(private_key->private_key.rsa, NULL); spki->subjectPublicKey.data = malloc(len); if (spki->subjectPublicKey.data == NULL) { hx509_set_error_string(context, 0, ENOMEM, "malloc - out of memory"); return ENOMEM; } spki->subjectPublicKey.length = len * 8; ret = set_digest_alg(&spki->algorithm, ASN1_OID_ID_PKCS1_RSAENCRYPTION, "\x05\x00", 2); if (ret) { hx509_set_error_string(context, 0, ret, "malloc - out of memory"); free(spki->subjectPublicKey.data); spki->subjectPublicKey.data = NULL; spki->subjectPublicKey.length = 0; return ret; } { unsigned char *pp = spki->subjectPublicKey.data; i2d_RSAPublicKey(private_key->private_key.rsa, &pp); } return 0; } static int rsa_generate_private_key(hx509_context context, struct hx509_generate_private_context *ctx, hx509_private_key private_key) { BIGNUM *e; int ret; unsigned long bits; static const int default_rsa_e = 65537; static const int default_rsa_bits = 2048; private_key->private_key.rsa = RSA_new(); if (private_key->private_key.rsa == NULL) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Failed to generate RSA key"); return HX509_PARSING_KEY_FAILED; } e = BN_new(); BN_set_word(e, default_rsa_e); bits = default_rsa_bits; if (ctx->num_bits) bits = ctx->num_bits; ret = RSA_generate_key_ex(private_key->private_key.rsa, bits, e, NULL); BN_free(e); if (ret != 1) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Failed to generate RSA key"); return HX509_PARSING_KEY_FAILED; } private_key->signature_alg = ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION; return 0; } static int rsa_private_key_export(hx509_context context, const hx509_private_key key, hx509_key_format_t format, heim_octet_string *data) { int ret; data->data = NULL; data->length = 0; switch (format) { case HX509_KEY_FORMAT_DER: ret = i2d_RSAPrivateKey(key->private_key.rsa, NULL); if (ret <= 0) { ret = EINVAL; hx509_set_error_string(context, 0, ret, "Private key is not exportable"); return ret; } data->data = malloc(ret); if (data->data == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "malloc out of memory"); return ret; } data->length = ret; { unsigned char *p = data->data; i2d_RSAPrivateKey(key->private_key.rsa, &p); } break; default: return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; } return 0; } static BIGNUM * rsa_get_internal(hx509_context context, hx509_private_key key, const char *type) { if (strcasecmp(type, "rsa-modulus") == 0) { return BN_dup(key->private_key.rsa->n); } else if (strcasecmp(type, "rsa-exponent") == 0) { return BN_dup(key->private_key.rsa->e); } else return NULL; } static hx509_private_key_ops rsa_private_key_ops = { "RSA PRIVATE KEY", ASN1_OID_ID_PKCS1_RSAENCRYPTION, NULL, rsa_private_key2SPKI, rsa_private_key_export, rsa_private_key_import, rsa_generate_private_key, rsa_get_internal }; #ifdef HAVE_OPENSSL static int ecdsa_private_key2SPKI(hx509_context context, hx509_private_key private_key, SubjectPublicKeyInfo *spki) { memset(spki, 0, sizeof(*spki)); return ENOMEM; } static int ecdsa_private_key_export(hx509_context context, const hx509_private_key key, hx509_key_format_t format, heim_octet_string *data) { return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; } static int ecdsa_private_key_import(hx509_context context, const AlgorithmIdentifier *keyai, const void *data, size_t len, hx509_key_format_t format, hx509_private_key private_key) { const unsigned char *p = data; EC_KEY **pkey = NULL; if (keyai->parameters) { EC_GROUP *group; int groupnid; EC_KEY *key; int ret; ret = parse_ECParameters(context, keyai->parameters, &groupnid); if (ret) return ret; key = EC_KEY_new(); if (key == NULL) return ENOMEM; group = EC_GROUP_new_by_curve_name(groupnid); if (group == NULL) { EC_KEY_free(key); return ENOMEM; } EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); if (EC_KEY_set_group(key, group) == 0) { EC_KEY_free(key); EC_GROUP_free(group); return ENOMEM; } EC_GROUP_free(group); pkey = &key; } switch (format) { case HX509_KEY_FORMAT_DER: private_key->private_key.ecdsa = d2i_ECPrivateKey(pkey, &p, len); if (private_key->private_key.ecdsa == NULL) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Failed to parse EC private key"); return HX509_PARSING_KEY_FAILED; } private_key->signature_alg = ASN1_OID_ID_ECDSA_WITH_SHA256; break; default: return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; } return 0; } static int ecdsa_generate_private_key(hx509_context context, struct hx509_generate_private_context *ctx, hx509_private_key private_key) { return ENOMEM; } static BIGNUM * ecdsa_get_internal(hx509_context context, hx509_private_key key, const char *type) { return NULL; } static hx509_private_key_ops ecdsa_private_key_ops = { "EC PRIVATE KEY", ASN1_OID_ID_ECPUBLICKEY, ecdsa_available, ecdsa_private_key2SPKI, ecdsa_private_key_export, ecdsa_private_key_import, ecdsa_generate_private_key, ecdsa_get_internal }; #endif /* HAVE_OPENSSL */ /* * */ static int dsa_verify_signature(hx509_context context, const struct signature_alg *sig_alg, const Certificate *signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, const heim_octet_string *sig) { const SubjectPublicKeyInfo *spi; DSAPublicKey pk; DSAParams param; size_t size; DSA *dsa; int ret; spi = &signer->tbsCertificate.subjectPublicKeyInfo; dsa = DSA_new(); if (dsa == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } ret = decode_DSAPublicKey(spi->subjectPublicKey.data, spi->subjectPublicKey.length / 8, &pk, &size); if (ret) goto out; dsa->pub_key = heim_int2BN(&pk); free_DSAPublicKey(&pk); if (dsa->pub_key == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } if (spi->algorithm.parameters == NULL) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "DSA parameters missing"); goto out; } ret = decode_DSAParams(spi->algorithm.parameters->data, spi->algorithm.parameters->length, ¶m, &size); if (ret) { hx509_set_error_string(context, 0, ret, "DSA parameters failed to decode"); goto out; } dsa->p = heim_int2BN(¶m.p); dsa->q = heim_int2BN(¶m.q); dsa->g = heim_int2BN(¶m.g); free_DSAParams(¶m); if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "out of memory"); goto out; } ret = DSA_verify(-1, data->data, data->length, (unsigned char*)sig->data, sig->length, dsa); if (ret == 1) ret = 0; else if (ret == 0 || ret == -1) { ret = HX509_CRYPTO_BAD_SIGNATURE; hx509_set_error_string(context, 0, ret, "BAD DSA sigature"); } else { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "Invalid format of DSA sigature"); } out: DSA_free(dsa); return ret; } #if 0 static int dsa_parse_private_key(hx509_context context, const void *data, size_t len, hx509_private_key private_key) { const unsigned char *p = data; private_key->private_key.dsa = d2i_DSAPrivateKey(NULL, &p, len); if (private_key->private_key.dsa == NULL) return EINVAL; private_key->signature_alg = ASN1_OID_ID_DSA_WITH_SHA1; return 0; /* else */ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "No support to parse DSA keys"); return HX509_PARSING_KEY_FAILED; } #endif static int evp_md_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) { size_t sigsize = EVP_MD_size(sig_alg->evp_md()); EVP_MD_CTX *ctx; memset(sig, 0, sizeof(*sig)); if (signatureAlgorithm) { int ret; ret = set_digest_alg(signatureAlgorithm, sig_alg->sig_oid, "\x05\x00", 2); if (ret) return ret; } sig->data = malloc(sigsize); if (sig->data == NULL) { sig->length = 0; return ENOMEM; } sig->length = sigsize; ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, sig_alg->evp_md(), NULL); EVP_DigestUpdate(ctx, data->data, data->length); EVP_DigestFinal_ex(ctx, sig->data, NULL); EVP_MD_CTX_destroy(ctx); return 0; } static int evp_md_verify_signature(hx509_context context, const struct signature_alg *sig_alg, const Certificate *signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, const heim_octet_string *sig) { unsigned char digest[EVP_MAX_MD_SIZE]; EVP_MD_CTX *ctx; size_t sigsize = EVP_MD_size(sig_alg->evp_md()); if (sig->length != sigsize || sigsize > sizeof(digest)) { hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT, "SHA256 sigature have wrong length"); return HX509_CRYPTO_SIG_INVALID_FORMAT; } ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, sig_alg->evp_md(), NULL); EVP_DigestUpdate(ctx, data->data, data->length); EVP_DigestFinal_ex(ctx, digest, NULL); EVP_MD_CTX_destroy(ctx); if (ct_memcmp(digest, sig->data, sigsize) != 0) { hx509_set_error_string(context, 0, HX509_CRYPTO_BAD_SIGNATURE, "Bad %s sigature", sig_alg->name); return HX509_CRYPTO_BAD_SIGNATURE; } return 0; } #ifdef HAVE_OPENSSL static const struct signature_alg ecdsa_with_sha256_alg = { "ecdsa-with-sha256", ASN1_OID_ID_ECDSA_WITH_SHA256, &_hx509_signature_ecdsa_with_sha256_data, ASN1_OID_ID_ECPUBLICKEY, &_hx509_signature_sha256_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, ecdsa_verify_signature, ecdsa_create_signature, 32 }; static const struct signature_alg ecdsa_with_sha1_alg = { "ecdsa-with-sha1", ASN1_OID_ID_ECDSA_WITH_SHA1, &_hx509_signature_ecdsa_with_sha1_data, ASN1_OID_ID_ECPUBLICKEY, &_hx509_signature_sha1_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, ecdsa_verify_signature, ecdsa_create_signature, 20 }; #endif static const struct signature_alg heim_rsa_pkcs1_x509 = { "rsa-pkcs1-x509", ASN1_OID_ID_HEIM_RSA_PKCS1_X509, &_hx509_signature_rsa_pkcs1_x509_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, NULL, PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg pkcs1_rsa_sha1_alg = { "rsa", ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_rsa_with_sha1_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, NULL, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_sha512_alg = { "rsa-with-sha512", ASN1_OID_ID_PKCS1_SHA512WITHRSAENCRYPTION, &_hx509_signature_rsa_with_sha512_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_sha512_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_sha384_alg = { "rsa-with-sha384", ASN1_OID_ID_PKCS1_SHA384WITHRSAENCRYPTION, &_hx509_signature_rsa_with_sha384_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_sha384_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_sha256_alg = { "rsa-with-sha256", ASN1_OID_ID_PKCS1_SHA256WITHRSAENCRYPTION, &_hx509_signature_rsa_with_sha256_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_sha256_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_sha1_alg = { "rsa-with-sha1", ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION, &_hx509_signature_rsa_with_sha1_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_sha1_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_sha1_alg_secsig = { "rsa-with-sha1", ASN1_OID_ID_SECSIG_SHA_1WITHRSAENCRYPTION, &_hx509_signature_rsa_with_sha1_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_sha1_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK, 0, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg rsa_with_md5_alg = { "rsa-with-md5", ASN1_OID_ID_PKCS1_MD5WITHRSAENCRYPTION, &_hx509_signature_rsa_with_md5_data, ASN1_OID_ID_PKCS1_RSAENCRYPTION, &_hx509_signature_md5_data, PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG, 1230739889, NULL, rsa_verify_signature, rsa_create_signature, 0 }; static const struct signature_alg dsa_sha1_alg = { "dsa-with-sha1", ASN1_OID_ID_DSA_WITH_SHA1, NULL, ASN1_OID_ID_DSA, &_hx509_signature_sha1_data, PROVIDE_CONF|REQUIRE_SIGNER|SIG_PUBLIC_SIG, 0, NULL, dsa_verify_signature, /* create_signature */ NULL, 0 }; static const struct signature_alg sha512_alg = { "sha-512", ASN1_OID_ID_SHA512, &_hx509_signature_sha512_data, NULL, NULL, SIG_DIGEST, 0, EVP_sha512, evp_md_verify_signature, evp_md_create_signature, 0 }; static const struct signature_alg sha384_alg = { "sha-384", ASN1_OID_ID_SHA512, &_hx509_signature_sha384_data, NULL, NULL, SIG_DIGEST, 0, EVP_sha384, evp_md_verify_signature, evp_md_create_signature, 0 }; static const struct signature_alg sha256_alg = { "sha-256", ASN1_OID_ID_SHA256, &_hx509_signature_sha256_data, NULL, NULL, SIG_DIGEST, 0, EVP_sha256, evp_md_verify_signature, evp_md_create_signature, 0 }; static const struct signature_alg sha1_alg = { "sha1", ASN1_OID_ID_SECSIG_SHA_1, &_hx509_signature_sha1_data, NULL, NULL, SIG_DIGEST, 0, EVP_sha1, evp_md_verify_signature, evp_md_create_signature, 0 }; static const struct signature_alg md5_alg = { "rsa-md5", ASN1_OID_ID_RSA_DIGEST_MD5, &_hx509_signature_md5_data, NULL, NULL, SIG_DIGEST, 0, EVP_md5, evp_md_verify_signature, NULL, 0 }; /* * Order matter in this structure, "best" first for each "key * compatible" type (type is ECDSA, RSA, DSA, none, etc) */ static const struct signature_alg *sig_algs[] = { #ifdef HAVE_OPENSSL &ecdsa_with_sha256_alg, &ecdsa_with_sha1_alg, #endif &rsa_with_sha512_alg, &rsa_with_sha384_alg, &rsa_with_sha256_alg, &rsa_with_sha1_alg, &rsa_with_sha1_alg_secsig, &pkcs1_rsa_sha1_alg, &rsa_with_md5_alg, &heim_rsa_pkcs1_x509, &dsa_sha1_alg, &sha512_alg, &sha384_alg, &sha256_alg, &sha1_alg, &md5_alg, NULL }; static const struct signature_alg * find_sig_alg(const heim_oid *oid) { unsigned int i; for (i = 0; sig_algs[i]; i++) if (der_heim_oid_cmp(sig_algs[i]->sig_oid, oid) == 0) return sig_algs[i]; return NULL; } static const AlgorithmIdentifier * alg_for_privatekey(const hx509_private_key pk, int type) { const heim_oid *keytype; unsigned int i; if (pk->ops == NULL) return NULL; keytype = pk->ops->key_oid; for (i = 0; sig_algs[i]; i++) { if (sig_algs[i]->key_oid == NULL) continue; if (der_heim_oid_cmp(sig_algs[i]->key_oid, keytype) != 0) continue; if (pk->ops->available && pk->ops->available(pk, sig_algs[i]->sig_alg) == 0) continue; if (type == HX509_SELECT_PUBLIC_SIG) return sig_algs[i]->sig_alg; if (type == HX509_SELECT_DIGEST) return sig_algs[i]->digest_alg; return NULL; } return NULL; } /* * */ static struct hx509_private_key_ops *private_algs[] = { &rsa_private_key_ops, #ifdef HAVE_OPENSSL &ecdsa_private_key_ops, #endif NULL }; hx509_private_key_ops * hx509_find_private_alg(const heim_oid *oid) { int i; for (i = 0; private_algs[i]; i++) { if (private_algs[i]->key_oid == NULL) continue; if (der_heim_oid_cmp(private_algs[i]->key_oid, oid) == 0) return private_algs[i]; } return NULL; } /* * Check if the algorithm `alg' have a best before date, and if it * des, make sure the its before the time `t'. */ int _hx509_signature_best_before(hx509_context context, const AlgorithmIdentifier *alg, time_t t) { const struct signature_alg *md; md = find_sig_alg(&alg->algorithm); if (md == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } if (md->best_before && md->best_before < t) { hx509_set_error_string(context, 0, HX509_CRYPTO_ALGORITHM_BEST_BEFORE, "Algorithm %s has passed it best before date", md->name); return HX509_CRYPTO_ALGORITHM_BEST_BEFORE; } return 0; } int _hx509_self_signed_valid(hx509_context context, const AlgorithmIdentifier *alg) { const struct signature_alg *md; md = find_sig_alg(&alg->algorithm); if (md == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } if ((md->flags & SELF_SIGNED_OK) == 0) { hx509_set_error_string(context, 0, HX509_CRYPTO_ALGORITHM_BEST_BEFORE, "Algorithm %s not trusted for self signatures", md->name); return HX509_CRYPTO_ALGORITHM_BEST_BEFORE; } return 0; } int _hx509_verify_signature(hx509_context context, const hx509_cert cert, const AlgorithmIdentifier *alg, const heim_octet_string *data, const heim_octet_string *sig) { const struct signature_alg *md; const Certificate *signer = NULL; if (cert) signer = _hx509_get_cert(cert); md = find_sig_alg(&alg->algorithm); if (md == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } if (signer && (md->flags & PROVIDE_CONF) == 0) { hx509_clear_error_string(context); return HX509_CRYPTO_SIG_NO_CONF; } if (signer == NULL && (md->flags & REQUIRE_SIGNER)) { hx509_clear_error_string(context); return HX509_CRYPTO_SIGNATURE_WITHOUT_SIGNER; } if (md->key_oid && signer) { const SubjectPublicKeyInfo *spi; spi = &signer->tbsCertificate.subjectPublicKeyInfo; if (der_heim_oid_cmp(&spi->algorithm.algorithm, md->key_oid) != 0) { hx509_clear_error_string(context); return HX509_SIG_ALG_DONT_MATCH_KEY_ALG; } } return (*md->verify_signature)(context, md, signer, alg, data, sig); } int _hx509_create_signature(hx509_context context, const hx509_private_key signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, AlgorithmIdentifier *signatureAlgorithm, heim_octet_string *sig) { const struct signature_alg *md; md = find_sig_alg(&alg->algorithm); if (md == NULL) { hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED, "algorithm no supported"); return HX509_SIG_ALG_NO_SUPPORTED; } if (signer && (md->flags & PROVIDE_CONF) == 0) { hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED, "algorithm provides no conf"); return HX509_CRYPTO_SIG_NO_CONF; } return (*md->create_signature)(context, md, signer, alg, data, signatureAlgorithm, sig); } int _hx509_create_signature_bitstring(hx509_context context, const hx509_private_key signer, const AlgorithmIdentifier *alg, const heim_octet_string *data, AlgorithmIdentifier *signatureAlgorithm, heim_bit_string *sig) { heim_octet_string os; int ret; ret = _hx509_create_signature(context, signer, alg, data, signatureAlgorithm, &os); if (ret) return ret; sig->data = os.data; sig->length = os.length * 8; return 0; } int _hx509_public_encrypt(hx509_context context, const heim_octet_string *cleartext, const Certificate *cert, heim_oid *encryption_oid, heim_octet_string *ciphertext) { const SubjectPublicKeyInfo *spi; unsigned char *to; int tosize; int ret; RSA *rsa; size_t size; const unsigned char *p; ciphertext->data = NULL; ciphertext->length = 0; spi = &cert->tbsCertificate.subjectPublicKeyInfo; p = spi->subjectPublicKey.data; size = spi->subjectPublicKey.length / 8; rsa = d2i_RSAPublicKey(NULL, &p, size); if (rsa == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } tosize = RSA_size(rsa); to = malloc(tosize); if (to == NULL) { RSA_free(rsa); hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } ret = RSA_public_encrypt(cleartext->length, (unsigned char *)cleartext->data, to, rsa, RSA_PKCS1_PADDING); RSA_free(rsa); if (ret <= 0) { free(to); hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PUBLIC_ENCRYPT, "RSA public encrypt failed with %d", ret); return HX509_CRYPTO_RSA_PUBLIC_ENCRYPT; } if (ret > tosize) _hx509_abort("internal rsa decryption failure: ret > tosize"); ciphertext->length = ret; ciphertext->data = to; ret = der_copy_oid(ASN1_OID_ID_PKCS1_RSAENCRYPTION, encryption_oid); if (ret) { der_free_octet_string(ciphertext); hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } return 0; } int hx509_private_key_private_decrypt(hx509_context context, const heim_octet_string *ciphertext, const heim_oid *encryption_oid, hx509_private_key p, heim_octet_string *cleartext) { int ret; cleartext->data = NULL; cleartext->length = 0; if (p->private_key.rsa == NULL) { hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING, "Private RSA key missing"); return HX509_PRIVATE_KEY_MISSING; } cleartext->length = RSA_size(p->private_key.rsa); cleartext->data = malloc(cleartext->length); if (cleartext->data == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } ret = RSA_private_decrypt(ciphertext->length, ciphertext->data, cleartext->data, p->private_key.rsa, RSA_PKCS1_PADDING); if (ret <= 0) { der_free_octet_string(cleartext); hx509_set_error_string(context, 0, HX509_CRYPTO_RSA_PRIVATE_DECRYPT, "Failed to decrypt using private key: %d", ret); return HX509_CRYPTO_RSA_PRIVATE_DECRYPT; } if (cleartext->length < (size_t)ret) _hx509_abort("internal rsa decryption failure: ret > tosize"); cleartext->length = ret; return 0; } int hx509_parse_private_key(hx509_context context, const AlgorithmIdentifier *keyai, const void *data, size_t len, hx509_key_format_t format, hx509_private_key *private_key) { struct hx509_private_key_ops *ops; int ret; *private_key = NULL; ops = hx509_find_private_alg(&keyai->algorithm); if (ops == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } ret = hx509_private_key_init(private_key, ops, NULL); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); return ret; } ret = (*ops->import)(context, keyai, data, len, format, *private_key); if (ret) hx509_private_key_free(private_key); return ret; } /* * */ int hx509_private_key2SPKI(hx509_context context, hx509_private_key private_key, SubjectPublicKeyInfo *spki) { const struct hx509_private_key_ops *ops = private_key->ops; if (ops == NULL || ops->get_spki == NULL) { hx509_set_error_string(context, 0, HX509_UNIMPLEMENTED_OPERATION, "Private key have no key2SPKI function"); return HX509_UNIMPLEMENTED_OPERATION; } return (*ops->get_spki)(context, private_key, spki); } int _hx509_generate_private_key_init(hx509_context context, const heim_oid *oid, struct hx509_generate_private_context **ctx) { *ctx = NULL; if (der_heim_oid_cmp(oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) != 0) { hx509_set_error_string(context, 0, EINVAL, "private key not an RSA key"); return EINVAL; } *ctx = calloc(1, sizeof(**ctx)); if (*ctx == NULL) { hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } (*ctx)->key_oid = oid; return 0; } int _hx509_generate_private_key_is_ca(hx509_context context, struct hx509_generate_private_context *ctx) { ctx->isCA = 1; return 0; } int _hx509_generate_private_key_bits(hx509_context context, struct hx509_generate_private_context *ctx, unsigned long bits) { ctx->num_bits = bits; return 0; } void _hx509_generate_private_key_free(struct hx509_generate_private_context **ctx) { free(*ctx); *ctx = NULL; } int _hx509_generate_private_key(hx509_context context, struct hx509_generate_private_context *ctx, hx509_private_key *private_key) { struct hx509_private_key_ops *ops; int ret; *private_key = NULL; ops = hx509_find_private_alg(ctx->key_oid); if (ops == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } ret = hx509_private_key_init(private_key, ops, NULL); if (ret) { hx509_set_error_string(context, 0, ret, "out of memory"); return ret; } ret = (*ops->generate_private_key)(context, ctx, *private_key); if (ret) hx509_private_key_free(private_key); return ret; } /* * */ const AlgorithmIdentifier * hx509_signature_sha512(void) { return &_hx509_signature_sha512_data; } const AlgorithmIdentifier * hx509_signature_sha384(void) { return &_hx509_signature_sha384_data; } const AlgorithmIdentifier * hx509_signature_sha256(void) { return &_hx509_signature_sha256_data; } const AlgorithmIdentifier * hx509_signature_sha1(void) { return &_hx509_signature_sha1_data; } const AlgorithmIdentifier * hx509_signature_md5(void) { return &_hx509_signature_md5_data; } const AlgorithmIdentifier * hx509_signature_ecPublicKey(void) { return &_hx509_signature_ecPublicKey; } const AlgorithmIdentifier * hx509_signature_ecdsa_with_sha256(void) { return &_hx509_signature_ecdsa_with_sha256_data; } const AlgorithmIdentifier * hx509_signature_ecdsa_with_sha1(void) { return &_hx509_signature_ecdsa_with_sha1_data; } const AlgorithmIdentifier * hx509_signature_rsa_with_sha512(void) { return &_hx509_signature_rsa_with_sha512_data; } const AlgorithmIdentifier * hx509_signature_rsa_with_sha384(void) { return &_hx509_signature_rsa_with_sha384_data; } const AlgorithmIdentifier * hx509_signature_rsa_with_sha256(void) { return &_hx509_signature_rsa_with_sha256_data; } const AlgorithmIdentifier * hx509_signature_rsa_with_sha1(void) { return &_hx509_signature_rsa_with_sha1_data; } const AlgorithmIdentifier * hx509_signature_rsa_with_md5(void) { return &_hx509_signature_rsa_with_md5_data; } const AlgorithmIdentifier * hx509_signature_rsa(void) { return &_hx509_signature_rsa_data; } const AlgorithmIdentifier * hx509_signature_rsa_pkcs1_x509(void) { return &_hx509_signature_rsa_pkcs1_x509_data; } const AlgorithmIdentifier * hx509_crypto_des_rsdi_ede3_cbc(void) { return &_hx509_des_rsdi_ede3_cbc_oid; } const AlgorithmIdentifier * hx509_crypto_aes128_cbc(void) { return &_hx509_crypto_aes128_cbc_data; } const AlgorithmIdentifier * hx509_crypto_aes256_cbc(void) { return &_hx509_crypto_aes256_cbc_data; } /* * */ const AlgorithmIdentifier * _hx509_crypto_default_sig_alg = &_hx509_signature_rsa_with_sha256_data; const AlgorithmIdentifier * _hx509_crypto_default_digest_alg = &_hx509_signature_sha256_data; const AlgorithmIdentifier * _hx509_crypto_default_secret_alg = &_hx509_crypto_aes128_cbc_data; /* * */ int hx509_private_key_init(hx509_private_key *key, hx509_private_key_ops *ops, void *keydata) { *key = calloc(1, sizeof(**key)); if (*key == NULL) return ENOMEM; (*key)->ref = 1; (*key)->ops = ops; (*key)->private_key.keydata = keydata; return 0; } hx509_private_key _hx509_private_key_ref(hx509_private_key key) { if (key->ref == 0) _hx509_abort("key refcount <= 0 on ref"); key->ref++; if (key->ref == UINT_MAX) _hx509_abort("key refcount == UINT_MAX on ref"); return key; } const char * _hx509_private_pem_name(hx509_private_key key) { return key->ops->pemtype; } int hx509_private_key_free(hx509_private_key *key) { if (key == NULL || *key == NULL) return 0; if ((*key)->ref == 0) _hx509_abort("key refcount == 0 on free"); if (--(*key)->ref > 0) return 0; if ((*key)->ops && der_heim_oid_cmp((*key)->ops->key_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) == 0) { if ((*key)->private_key.rsa) RSA_free((*key)->private_key.rsa); #ifdef HAVE_OPENSSL } else if ((*key)->ops && der_heim_oid_cmp((*key)->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) == 0) { if ((*key)->private_key.ecdsa) EC_KEY_free((*key)->private_key.ecdsa); #endif } (*key)->private_key.rsa = NULL; free(*key); *key = NULL; return 0; } void hx509_private_key_assign_rsa(hx509_private_key key, void *ptr) { if (key->private_key.rsa) RSA_free(key->private_key.rsa); key->private_key.rsa = ptr; key->signature_alg = ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION; key->md = &pkcs1_rsa_sha1_alg; } int _hx509_private_key_oid(hx509_context context, const hx509_private_key key, heim_oid *data) { int ret; ret = der_copy_oid(key->ops->key_oid, data); if (ret) hx509_set_error_string(context, 0, ret, "malloc out of memory"); return ret; } int _hx509_private_key_exportable(hx509_private_key key) { if (key->ops->export == NULL) return 0; return 1; } BIGNUM * _hx509_private_key_get_internal(hx509_context context, hx509_private_key key, const char *type) { if (key->ops->get_internal == NULL) return NULL; return (*key->ops->get_internal)(context, key, type); } int _hx509_private_key_export(hx509_context context, const hx509_private_key key, hx509_key_format_t format, heim_octet_string *data) { if (key->ops->export == NULL) { hx509_clear_error_string(context); return HX509_UNIMPLEMENTED_OPERATION; } return (*key->ops->export)(context, key, format, data); } /* * */ struct hx509cipher { const char *name; int flags; #define CIPHER_WEAK 1 const heim_oid *oid; const AlgorithmIdentifier *(*ai_func)(void); const EVP_CIPHER *(*evp_func)(void); int (*get_params)(hx509_context, const hx509_crypto, const heim_octet_string *, heim_octet_string *); int (*set_params)(hx509_context, const heim_octet_string *, hx509_crypto, heim_octet_string *); }; struct hx509_crypto_data { char *name; int flags; #define ALLOW_WEAK 1 #define PADDING_NONE 2 #define PADDING_PKCS7 4 #define PADDING_FLAGS (2|4) const struct hx509cipher *cipher; const EVP_CIPHER *c; heim_octet_string key; heim_oid oid; void *param; }; /* * */ static unsigned private_rc2_40_oid_data[] = { 127, 1 }; static heim_oid asn1_oid_private_rc2_40 = { 2, private_rc2_40_oid_data }; /* * */ static int CMSCBCParam_get(hx509_context context, const hx509_crypto crypto, const heim_octet_string *ivec, heim_octet_string *param) { size_t size; int ret; assert(crypto->param == NULL); if (ivec == NULL) return 0; ASN1_MALLOC_ENCODE(CMSCBCParameter, param->data, param->length, ivec, &size, ret); if (ret == 0 && size != param->length) _hx509_abort("Internal asn1 encoder failure"); if (ret) hx509_clear_error_string(context); return ret; } static int CMSCBCParam_set(hx509_context context, const heim_octet_string *param, hx509_crypto crypto, heim_octet_string *ivec) { int ret; if (ivec == NULL) return 0; ret = decode_CMSCBCParameter(param->data, param->length, ivec, NULL); if (ret) hx509_clear_error_string(context); return ret; } struct _RC2_params { int maximum_effective_key; }; static int CMSRC2CBCParam_get(hx509_context context, const hx509_crypto crypto, const heim_octet_string *ivec, heim_octet_string *param) { CMSRC2CBCParameter rc2params; const struct _RC2_params *p = crypto->param; int maximum_effective_key = 128; size_t size; int ret; memset(&rc2params, 0, sizeof(rc2params)); if (p) maximum_effective_key = p->maximum_effective_key; switch(maximum_effective_key) { case 40: rc2params.rc2ParameterVersion = 160; break; case 64: rc2params.rc2ParameterVersion = 120; break; case 128: rc2params.rc2ParameterVersion = 58; break; } rc2params.iv = *ivec; ASN1_MALLOC_ENCODE(CMSRC2CBCParameter, param->data, param->length, &rc2params, &size, ret); if (ret == 0 && size != param->length) _hx509_abort("Internal asn1 encoder failure"); return ret; } static int CMSRC2CBCParam_set(hx509_context context, const heim_octet_string *param, hx509_crypto crypto, heim_octet_string *ivec) { CMSRC2CBCParameter rc2param; struct _RC2_params *p; size_t size; int ret; ret = decode_CMSRC2CBCParameter(param->data, param->length, &rc2param, &size); if (ret) { hx509_clear_error_string(context); return ret; } p = calloc(1, sizeof(*p)); if (p == NULL) { free_CMSRC2CBCParameter(&rc2param); hx509_clear_error_string(context); return ENOMEM; } switch(rc2param.rc2ParameterVersion) { case 160: crypto->c = EVP_rc2_40_cbc(); p->maximum_effective_key = 40; break; case 120: crypto->c = EVP_rc2_64_cbc(); p->maximum_effective_key = 64; break; case 58: crypto->c = EVP_rc2_cbc(); p->maximum_effective_key = 128; break; default: free(p); free_CMSRC2CBCParameter(&rc2param); return HX509_CRYPTO_SIG_INVALID_FORMAT; } if (ivec) ret = der_copy_octet_string(&rc2param.iv, ivec); free_CMSRC2CBCParameter(&rc2param); if (ret) { free(p); hx509_clear_error_string(context); } else crypto->param = p; return ret; } /* * */ static const struct hx509cipher ciphers[] = { { "rc2-cbc", CIPHER_WEAK, ASN1_OID_ID_PKCS3_RC2_CBC, NULL, EVP_rc2_cbc, CMSRC2CBCParam_get, CMSRC2CBCParam_set }, { "rc2-cbc", CIPHER_WEAK, ASN1_OID_ID_RSADSI_RC2_CBC, NULL, EVP_rc2_cbc, CMSRC2CBCParam_get, CMSRC2CBCParam_set }, { "rc2-40-cbc", CIPHER_WEAK, &asn1_oid_private_rc2_40, NULL, EVP_rc2_40_cbc, CMSRC2CBCParam_get, CMSRC2CBCParam_set }, { "des-ede3-cbc", 0, ASN1_OID_ID_PKCS3_DES_EDE3_CBC, NULL, EVP_des_ede3_cbc, CMSCBCParam_get, CMSCBCParam_set }, { "des-ede3-cbc", 0, ASN1_OID_ID_RSADSI_DES_EDE3_CBC, hx509_crypto_des_rsdi_ede3_cbc, EVP_des_ede3_cbc, CMSCBCParam_get, CMSCBCParam_set }, { "aes-128-cbc", 0, ASN1_OID_ID_AES_128_CBC, hx509_crypto_aes128_cbc, EVP_aes_128_cbc, CMSCBCParam_get, CMSCBCParam_set }, { "aes-192-cbc", 0, ASN1_OID_ID_AES_192_CBC, NULL, EVP_aes_192_cbc, CMSCBCParam_get, CMSCBCParam_set }, { "aes-256-cbc", 0, ASN1_OID_ID_AES_256_CBC, hx509_crypto_aes256_cbc, EVP_aes_256_cbc, CMSCBCParam_get, CMSCBCParam_set } }; static const struct hx509cipher * find_cipher_by_oid(const heim_oid *oid) { size_t i; for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++) if (der_heim_oid_cmp(oid, ciphers[i].oid) == 0) return &ciphers[i]; return NULL; } static const struct hx509cipher * find_cipher_by_name(const char *name) { size_t i; for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++) if (strcasecmp(name, ciphers[i].name) == 0) return &ciphers[i]; return NULL; } const heim_oid * hx509_crypto_enctype_by_name(const char *name) { const struct hx509cipher *cipher; cipher = find_cipher_by_name(name); if (cipher == NULL) return NULL; return cipher->oid; } int hx509_crypto_init(hx509_context context, const char *provider, const heim_oid *enctype, hx509_crypto *crypto) { const struct hx509cipher *cipher; *crypto = NULL; cipher = find_cipher_by_oid(enctype); if (cipher == NULL) { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "Algorithm not supported"); return HX509_ALG_NOT_SUPP; } *crypto = calloc(1, sizeof(**crypto)); if (*crypto == NULL) { hx509_clear_error_string(context); return ENOMEM; } (*crypto)->flags = PADDING_PKCS7; (*crypto)->cipher = cipher; (*crypto)->c = (*cipher->evp_func)(); if (der_copy_oid(enctype, &(*crypto)->oid)) { hx509_crypto_destroy(*crypto); *crypto = NULL; hx509_clear_error_string(context); return ENOMEM; } return 0; } const char * hx509_crypto_provider(hx509_crypto crypto) { return "unknown"; } void hx509_crypto_destroy(hx509_crypto crypto) { if (crypto->name) free(crypto->name); if (crypto->key.data) free(crypto->key.data); if (crypto->param) free(crypto->param); der_free_oid(&crypto->oid); memset(crypto, 0, sizeof(*crypto)); free(crypto); } int hx509_crypto_set_key_name(hx509_crypto crypto, const char *name) { return 0; } void hx509_crypto_allow_weak(hx509_crypto crypto) { crypto->flags |= ALLOW_WEAK; } void hx509_crypto_set_padding(hx509_crypto crypto, int padding_type) { switch (padding_type) { case HX509_CRYPTO_PADDING_PKCS7: crypto->flags &= ~PADDING_FLAGS; crypto->flags |= PADDING_PKCS7; break; case HX509_CRYPTO_PADDING_NONE: crypto->flags &= ~PADDING_FLAGS; crypto->flags |= PADDING_NONE; break; default: _hx509_abort("Invalid padding"); } } int hx509_crypto_set_key_data(hx509_crypto crypto, const void *data, size_t length) { if (EVP_CIPHER_key_length(crypto->c) > (int)length) return HX509_CRYPTO_INTERNAL_ERROR; if (crypto->key.data) { free(crypto->key.data); crypto->key.data = NULL; crypto->key.length = 0; } crypto->key.data = malloc(length); if (crypto->key.data == NULL) return ENOMEM; memcpy(crypto->key.data, data, length); crypto->key.length = length; return 0; } int hx509_crypto_set_random_key(hx509_crypto crypto, heim_octet_string *key) { if (crypto->key.data) { free(crypto->key.data); crypto->key.length = 0; } crypto->key.length = EVP_CIPHER_key_length(crypto->c); crypto->key.data = malloc(crypto->key.length); if (crypto->key.data == NULL) { crypto->key.length = 0; return ENOMEM; } if (RAND_bytes(crypto->key.data, crypto->key.length) <= 0) { free(crypto->key.data); crypto->key.data = NULL; crypto->key.length = 0; return HX509_CRYPTO_INTERNAL_ERROR; } if (key) return der_copy_octet_string(&crypto->key, key); else return 0; } int hx509_crypto_set_params(hx509_context context, hx509_crypto crypto, const heim_octet_string *param, heim_octet_string *ivec) { return (*crypto->cipher->set_params)(context, param, crypto, ivec); } int hx509_crypto_get_params(hx509_context context, hx509_crypto crypto, const heim_octet_string *ivec, heim_octet_string *param) { return (*crypto->cipher->get_params)(context, crypto, ivec, param); } int hx509_crypto_random_iv(hx509_crypto crypto, heim_octet_string *ivec) { ivec->length = EVP_CIPHER_iv_length(crypto->c); ivec->data = malloc(ivec->length); if (ivec->data == NULL) { ivec->length = 0; return ENOMEM; } if (RAND_bytes(ivec->data, ivec->length) <= 0) { free(ivec->data); ivec->data = NULL; ivec->length = 0; return HX509_CRYPTO_INTERNAL_ERROR; } return 0; } int hx509_crypto_encrypt(hx509_crypto crypto, const void *data, const size_t length, const heim_octet_string *ivec, heim_octet_string **ciphertext) { EVP_CIPHER_CTX evp; size_t padsize, bsize; int ret; *ciphertext = NULL; if ((crypto->cipher->flags & CIPHER_WEAK) && (crypto->flags & ALLOW_WEAK) == 0) return HX509_CRYPTO_ALGORITHM_BEST_BEFORE; assert(EVP_CIPHER_iv_length(crypto->c) == (int)ivec->length); EVP_CIPHER_CTX_init(&evp); ret = EVP_CipherInit_ex(&evp, crypto->c, NULL, crypto->key.data, ivec->data, 1); if (ret != 1) { EVP_CIPHER_CTX_cleanup(&evp); ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } *ciphertext = calloc(1, sizeof(**ciphertext)); if (*ciphertext == NULL) { ret = ENOMEM; goto out; } assert(crypto->flags & PADDING_FLAGS); bsize = EVP_CIPHER_block_size(crypto->c); padsize = 0; if (crypto->flags & PADDING_NONE) { if (bsize != 1 && (length % bsize) != 0) return HX509_CMS_PADDING_ERROR; } else if (crypto->flags & PADDING_PKCS7) { if (bsize != 1) padsize = bsize - (length % bsize); } (*ciphertext)->length = length + padsize; (*ciphertext)->data = malloc(length + padsize); if ((*ciphertext)->data == NULL) { ret = ENOMEM; goto out; } memcpy((*ciphertext)->data, data, length); if (padsize) { size_t i; unsigned char *p = (*ciphertext)->data; p += length; for (i = 0; i < padsize; i++) *p++ = padsize; } ret = EVP_Cipher(&evp, (*ciphertext)->data, (*ciphertext)->data, length + padsize); if (ret != 1) { ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } ret = 0; out: if (ret) { if (*ciphertext) { if ((*ciphertext)->data) { free((*ciphertext)->data); } free(*ciphertext); *ciphertext = NULL; } } EVP_CIPHER_CTX_cleanup(&evp); return ret; } int hx509_crypto_decrypt(hx509_crypto crypto, const void *data, const size_t length, heim_octet_string *ivec, heim_octet_string *clear) { EVP_CIPHER_CTX evp; void *idata = NULL; int ret; clear->data = NULL; clear->length = 0; if ((crypto->cipher->flags & CIPHER_WEAK) && (crypto->flags & ALLOW_WEAK) == 0) return HX509_CRYPTO_ALGORITHM_BEST_BEFORE; if (ivec && EVP_CIPHER_iv_length(crypto->c) < (int)ivec->length) return HX509_CRYPTO_INTERNAL_ERROR; if (crypto->key.data == NULL) return HX509_CRYPTO_INTERNAL_ERROR; if (ivec) idata = ivec->data; EVP_CIPHER_CTX_init(&evp); ret = EVP_CipherInit_ex(&evp, crypto->c, NULL, crypto->key.data, idata, 0); if (ret != 1) { EVP_CIPHER_CTX_cleanup(&evp); return HX509_CRYPTO_INTERNAL_ERROR; } clear->length = length; clear->data = malloc(length); if (clear->data == NULL) { EVP_CIPHER_CTX_cleanup(&evp); clear->length = 0; return ENOMEM; } if (EVP_Cipher(&evp, clear->data, data, length) != 1) { return HX509_CRYPTO_INTERNAL_ERROR; } EVP_CIPHER_CTX_cleanup(&evp); if ((crypto->flags & PADDING_PKCS7) && EVP_CIPHER_block_size(crypto->c) > 1) { int padsize; unsigned char *p; int j, bsize = EVP_CIPHER_block_size(crypto->c); if ((int)clear->length < bsize) { ret = HX509_CMS_PADDING_ERROR; goto out; } p = clear->data; p += clear->length - 1; padsize = *p; if (padsize > bsize) { ret = HX509_CMS_PADDING_ERROR; goto out; } clear->length -= padsize; for (j = 0; j < padsize; j++) { if (*p-- != padsize) { ret = HX509_CMS_PADDING_ERROR; goto out; } } } return 0; out: if (clear->data) free(clear->data); clear->data = NULL; clear->length = 0; return ret; } typedef int (*PBE_string2key_func)(hx509_context, const char *, const heim_octet_string *, hx509_crypto *, heim_octet_string *, heim_octet_string *, const heim_oid *, const EVP_MD *); static int PBE_string2key(hx509_context context, const char *password, const heim_octet_string *parameters, hx509_crypto *crypto, heim_octet_string *key, heim_octet_string *iv, const heim_oid *enc_oid, const EVP_MD *md) { PKCS12_PBEParams p12params; int passwordlen; hx509_crypto c; int iter, saltlen, ret; unsigned char *salt; passwordlen = password ? strlen(password) : 0; if (parameters == NULL) return HX509_ALG_NOT_SUPP; ret = decode_PKCS12_PBEParams(parameters->data, parameters->length, &p12params, NULL); if (ret) goto out; if (p12params.iterations) iter = *p12params.iterations; else iter = 1; salt = p12params.salt.data; saltlen = p12params.salt.length; if (!PKCS12_key_gen (password, passwordlen, salt, saltlen, PKCS12_KEY_ID, iter, key->length, key->data, md)) { ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } if (!PKCS12_key_gen (password, passwordlen, salt, saltlen, PKCS12_IV_ID, iter, iv->length, iv->data, md)) { ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } ret = hx509_crypto_init(context, NULL, enc_oid, &c); if (ret) goto out; hx509_crypto_allow_weak(c); ret = hx509_crypto_set_key_data(c, key->data, key->length); if (ret) { hx509_crypto_destroy(c); goto out; } *crypto = c; out: free_PKCS12_PBEParams(&p12params); return ret; } static const heim_oid * find_string2key(const heim_oid *oid, const EVP_CIPHER **c, const EVP_MD **md, PBE_string2key_func *s2k) { if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND40BITRC2_CBC) == 0) { *c = EVP_rc2_40_cbc(); *md = EVP_sha1(); *s2k = PBE_string2key; return &asn1_oid_private_rc2_40; } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND128BITRC2_CBC) == 0) { *c = EVP_rc2_cbc(); *md = EVP_sha1(); *s2k = PBE_string2key; return ASN1_OID_ID_PKCS3_RC2_CBC; #if 0 } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND40BITRC4) == 0) { *c = EVP_rc4_40(); *md = EVP_sha1(); *s2k = PBE_string2key; return NULL; } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND128BITRC4) == 0) { *c = EVP_rc4(); *md = EVP_sha1(); *s2k = PBE_string2key; return ASN1_OID_ID_PKCS3_RC4; #endif } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND3_KEYTRIPLEDES_CBC) == 0) { *c = EVP_des_ede3_cbc(); *md = EVP_sha1(); *s2k = PBE_string2key; return ASN1_OID_ID_PKCS3_DES_EDE3_CBC; } return NULL; } /* * */ int _hx509_pbe_encrypt(hx509_context context, hx509_lock lock, const AlgorithmIdentifier *ai, const heim_octet_string *content, heim_octet_string *econtent) { hx509_clear_error_string(context); return EINVAL; } /* * */ int _hx509_pbe_decrypt(hx509_context context, hx509_lock lock, const AlgorithmIdentifier *ai, const heim_octet_string *econtent, heim_octet_string *content) { const struct _hx509_password *pw; heim_octet_string key, iv; const heim_oid *enc_oid; const EVP_CIPHER *c; const EVP_MD *md; PBE_string2key_func s2k; int ret = 0; size_t i; memset(&key, 0, sizeof(key)); memset(&iv, 0, sizeof(iv)); memset(content, 0, sizeof(*content)); enc_oid = find_string2key(&ai->algorithm, &c, &md, &s2k); if (enc_oid == NULL) { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "String to key algorithm not supported"); ret = HX509_ALG_NOT_SUPP; goto out; } key.length = EVP_CIPHER_key_length(c); key.data = malloc(key.length); if (key.data == NULL) { ret = ENOMEM; hx509_clear_error_string(context); goto out; } iv.length = EVP_CIPHER_iv_length(c); iv.data = malloc(iv.length); if (iv.data == NULL) { ret = ENOMEM; hx509_clear_error_string(context); goto out; } pw = _hx509_lock_get_passwords(lock); ret = HX509_CRYPTO_INTERNAL_ERROR; for (i = 0; i < pw->len + 1; i++) { hx509_crypto crypto; const char *password; if (i < pw->len) password = pw->val[i]; else if (i < pw->len + 1) password = ""; else password = NULL; ret = (*s2k)(context, password, ai->parameters, &crypto, &key, &iv, enc_oid, md); if (ret) goto out; ret = hx509_crypto_decrypt(crypto, econtent->data, econtent->length, &iv, content); hx509_crypto_destroy(crypto); if (ret == 0) goto out; } out: if (key.data) der_free_octet_string(&key); if (iv.data) der_free_octet_string(&iv); return ret; } /* * */ static int match_keys_rsa(hx509_cert c, hx509_private_key private_key) { const Certificate *cert; const SubjectPublicKeyInfo *spi; RSAPublicKey pk; RSA *rsa; size_t size; int ret; if (private_key->private_key.rsa == NULL) return 0; rsa = private_key->private_key.rsa; if (rsa->d == NULL || rsa->p == NULL || rsa->q == NULL) return 0; cert = _hx509_get_cert(c); spi = &cert->tbsCertificate.subjectPublicKeyInfo; rsa = RSA_new(); if (rsa == NULL) return 0; ret = decode_RSAPublicKey(spi->subjectPublicKey.data, spi->subjectPublicKey.length / 8, &pk, &size); if (ret) { RSA_free(rsa); return 0; } rsa->n = heim_int2BN(&pk.modulus); rsa->e = heim_int2BN(&pk.publicExponent); free_RSAPublicKey(&pk); rsa->d = BN_dup(private_key->private_key.rsa->d); rsa->p = BN_dup(private_key->private_key.rsa->p); rsa->q = BN_dup(private_key->private_key.rsa->q); rsa->dmp1 = BN_dup(private_key->private_key.rsa->dmp1); rsa->dmq1 = BN_dup(private_key->private_key.rsa->dmq1); rsa->iqmp = BN_dup(private_key->private_key.rsa->iqmp); if (rsa->n == NULL || rsa->e == NULL || rsa->d == NULL || rsa->p == NULL|| rsa->q == NULL || rsa->dmp1 == NULL || rsa->dmq1 == NULL) { RSA_free(rsa); return 0; } ret = RSA_check_key(rsa); RSA_free(rsa); return ret == 1; } static int match_keys_ec(hx509_cert c, hx509_private_key private_key) { return 1; /* XXX use EC_KEY_check_key */ } int _hx509_match_keys(hx509_cert c, hx509_private_key key) { if (der_heim_oid_cmp(key->ops->key_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) == 0) return match_keys_rsa(c, key); if (der_heim_oid_cmp(key->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) == 0) return match_keys_ec(c, key); return 0; } static const heim_oid * find_keytype(const hx509_private_key key) { const struct signature_alg *md; if (key == NULL) return NULL; md = find_sig_alg(key->signature_alg); if (md == NULL) return NULL; return md->key_oid; } int hx509_crypto_select(const hx509_context context, int type, const hx509_private_key source, hx509_peer_info peer, AlgorithmIdentifier *selected) { const AlgorithmIdentifier *def = NULL; size_t i, j; int ret, bits; memset(selected, 0, sizeof(*selected)); if (type == HX509_SELECT_DIGEST) { bits = SIG_DIGEST; if (source) def = alg_for_privatekey(source, type); if (def == NULL) def = _hx509_crypto_default_digest_alg; } else if (type == HX509_SELECT_PUBLIC_SIG) { bits = SIG_PUBLIC_SIG; /* XXX depend on `source´ and `peer´ */ if (source) def = alg_for_privatekey(source, type); if (def == NULL) def = _hx509_crypto_default_sig_alg; } else if (type == HX509_SELECT_SECRET_ENC) { bits = SIG_SECRET; def = _hx509_crypto_default_secret_alg; } else { hx509_set_error_string(context, 0, EINVAL, "Unknown type %d of selection", type); return EINVAL; } if (peer) { const heim_oid *keytype = NULL; keytype = find_keytype(source); for (i = 0; i < peer->len; i++) { for (j = 0; sig_algs[j]; j++) { if ((sig_algs[j]->flags & bits) != bits) continue; if (der_heim_oid_cmp(sig_algs[j]->sig_oid, &peer->val[i].algorithm) != 0) continue; if (keytype && sig_algs[j]->key_oid && der_heim_oid_cmp(keytype, sig_algs[j]->key_oid)) continue; /* found one, use that */ ret = copy_AlgorithmIdentifier(&peer->val[i], selected); if (ret) hx509_clear_error_string(context); return ret; } if (bits & SIG_SECRET) { const struct hx509cipher *cipher; cipher = find_cipher_by_oid(&peer->val[i].algorithm); if (cipher == NULL) continue; if (cipher->ai_func == NULL) continue; ret = copy_AlgorithmIdentifier(cipher->ai_func(), selected); if (ret) hx509_clear_error_string(context); return ret; } } } /* use default */ ret = copy_AlgorithmIdentifier(def, selected); if (ret) hx509_clear_error_string(context); return ret; } int hx509_crypto_available(hx509_context context, int type, hx509_cert source, AlgorithmIdentifier **val, unsigned int *plen) { const heim_oid *keytype = NULL; unsigned int len, i; void *ptr; int bits, ret; *val = NULL; if (type == HX509_SELECT_ALL) { bits = SIG_DIGEST | SIG_PUBLIC_SIG | SIG_SECRET; } else if (type == HX509_SELECT_DIGEST) { bits = SIG_DIGEST; } else if (type == HX509_SELECT_PUBLIC_SIG) { bits = SIG_PUBLIC_SIG; } else { hx509_set_error_string(context, 0, EINVAL, "Unknown type %d of available", type); return EINVAL; } if (source) keytype = find_keytype(_hx509_cert_private_key(source)); len = 0; for (i = 0; sig_algs[i]; i++) { if ((sig_algs[i]->flags & bits) == 0) continue; if (sig_algs[i]->sig_alg == NULL) continue; if (keytype && sig_algs[i]->key_oid && der_heim_oid_cmp(sig_algs[i]->key_oid, keytype)) continue; /* found one, add that to the list */ ptr = realloc(*val, sizeof(**val) * (len + 1)); if (ptr == NULL) goto out; *val = ptr; ret = copy_AlgorithmIdentifier(sig_algs[i]->sig_alg, &(*val)[len]); if (ret) goto out; len++; } /* Add AES */ if (bits & SIG_SECRET) { for (i = 0; i < sizeof(ciphers)/sizeof(ciphers[0]); i++) { if (ciphers[i].flags & CIPHER_WEAK) continue; if (ciphers[i].ai_func == NULL) continue; ptr = realloc(*val, sizeof(**val) * (len + 1)); if (ptr == NULL) goto out; *val = ptr; ret = copy_AlgorithmIdentifier((ciphers[i].ai_func)(), &(*val)[len]); if (ret) goto out; len++; } } *plen = len; return 0; out: for (i = 0; i < len; i++) free_AlgorithmIdentifier(&(*val)[i]); free(*val); *val = NULL; hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } void hx509_crypto_free_algs(AlgorithmIdentifier *val, unsigned int len) { unsigned int i; for (i = 0; i < len; i++) free_AlgorithmIdentifier(&val[i]); free(val); }