/* * Copyright (c) 2004 - 2016 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" /*- * RFC5758 specifies no parameters for ecdsa-with-SHA signatures * RFC5754 specifies NULL parameters for shaWithRSAEncryption signatures * * XXX: Make sure that the parameters are either NULL in both the tbs and the * signature, or absent from both the tbs and the signature. */ 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), {0} }; 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), {0} }; 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), {0} }; 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), {0} }; 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), {0} }; 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) }, rk_UNCONST(&null_entry_oid), {0} }; 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) }, rk_UNCONST(&null_entry_oid), {0} }; 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) }, rk_UNCONST(&null_entry_oid), {0} }; 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) }, rk_UNCONST(&null_entry_oid), {0} }; 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) }, rk_UNCONST(&null_entry_oid), {0} }; static const unsigned rsa_oid[] ={ 1, 2, 840, 113549, 1, 1, 1 }; const AlgorithmIdentifier _hx509_signature_rsa_data = { { 7, rk_UNCONST(rsa_oid) }, NULL, {0} }; 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, {0} }; 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, {0} }; 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, {0} }; 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, {0} }; /* * */ #if 0 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; } #endif /* * */ HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_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; } /* * XXX This is becoming generic, but making it fully so is painful, so we'll * retain signature verification and creation functions for RSA PKCS#1 * v1.5, v2.0, ECDSA, and EdDSA. */ static int rsa_verify_signature(hx509_context context, const struct signature_alg *sig_alg, const Certificate *signer, const AlgorithmIdentifier *alg, const EVP_MD *md, const heim_octet_string *data, const heim_octet_string *sig) { EVP_PKEY_CTX *pctx = NULL; EVP_MD_CTX *mctx = NULL; EVP_PKEY *pkey = NULL; const unsigned char *p; size_t size; int ret = HX509_CRYPTO_INTERNAL_ERROR; ERR_clear_error(); /* Clear any pre-existing errors */ md = md ? md : (sig_alg->evp_md ? sig_alg->evp_md() : NULL); p = signer->tbsCertificate.subjectPublicKeyInfo._save.data; size = signer->tbsCertificate.subjectPublicKeyInfo._save.length; pkey = d2i_PUBKEY(NULL, &p, size); if (pkey == NULL) { _hx509_set_error_string_openssl(context, 0, ret, "Could not parse SubjectPublicKeyInfo (RSA)"); goto out; } if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; // Can't happen hx509_set_error_string(context, 0, ret, "public key is not RSA"); goto out; } if ((mctx = EVP_MD_CTX_new()) == NULL) { ret = ENOMEM; _hx509_set_error_string_openssl(context, 0, ret, "out of memory"); goto out; } if (EVP_DigestVerifyInit(mctx, &pctx, md, NULL, pkey) <= 0) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "digest_verify_init"); goto out; } if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) <= 0) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "setting RSA PKCS#1.5 padding failed"); goto out; } if (EVP_DigestVerify(mctx /* frees */, sig->data, sig->length, data->data, data->length) == 1) { ret = 0; } else { _hx509_set_error_string_openssl(context, 0, ret, "signature validation failed"); } out: EVP_MD_CTX_free(mctx); /* Also frees pctx */ EVP_PKEY_free(pkey); 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 EVP_MD *md, const heim_octet_string *data, AlgorithmIdentifier *signatureAlgorithm, heim_octet_string *sig) { const AlgorithmIdentifier *digest_alg; const heim_oid *sig_oid; EVP_PKEY_CTX *pctx = NULL; EVP_MD_CTX *mctx = NULL; 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 (md == NULL) { 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 // TODO Add ECDSA and EdDSA and PQ hybrids here! return HX509_ALG_NOT_SUPP; if (digest_alg) { const struct signature_alg *d; if ((d = _hx509_find_sig_alg(&digest_alg->algorithm)) == NULL) return HX509_ALG_NOT_SUPP; md = d->evp_md(); } } if (signatureAlgorithm) { // XXX Mutates signatureAlgorithm to set params to NULL, which is what // "\x05\x00" is (the DER encoding of NULL). // XXX This is a mistake. We should instead let OpenSSL 3.x APIs give // us a DER-encoded AlgorithmIdentifier (that we would then // decode). ret = _hx509_set_digest_alg(signatureAlgorithm, sig_oid, "\x05\x00", 2); if (ret) { hx509_clear_error_string(context); return ret; } } if ((mctx = EVP_MD_CTX_new()) == NULL || EVP_DigestSignInit(mctx, &pctx, md, NULL, signer->private_key.pkey) <= 0 || EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) <= 0 || EVP_DigestSign(mctx, NULL, &size, data->data, data->length) <= 0) { _hx509_set_error_string_openssl(context, 0, ENOMEM, "Could not set up to create a signature"); EVP_MD_CTX_free(mctx); return ENOMEM; } sig->length = size; sig->data = malloc(sig->length); if (sig->data == NULL) { hx509_set_error_string(context, 0, ENOMEM, "Could not set up to create a signature"); EVP_MD_CTX_free(mctx); return ENOMEM; } if (EVP_DigestSign(mctx, sig->data, &size, data->data, data->length) <= 0) { _hx509_set_error_string_openssl(context, 0, HX509_CMS_FAILED_CREATE_SIGATURE, "Could not create a signature"); EVP_MD_CTX_free(mctx); return HX509_CMS_FAILED_CREATE_SIGATURE; } if (sig->length != size) { hx509_set_error_string(context, 0, HX509_CMS_FAILED_CREATE_SIGATURE, "Could not create a signature (OpenSSL length mismatch)"); EVP_MD_CTX_free(mctx); return HX509_CMS_FAILED_CREATE_SIGATURE; } EVP_MD_CTX_free(mctx); return 0; } static EVP_PKEY * evp_from_rsa_pkcs1_der(const unsigned char *der, size_t der_len) { return d2i_PrivateKey_ex(EVP_PKEY_RSA, NULL, &der, der_len, /* libctx */ NULL, /* propq */ NULL); } 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: { private_key->private_key.pkey = evp_from_rsa_pkcs1_der(data, len); if (private_key->private_key.pkey == 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) { unsigned char *d = NULL; unsigned char *p = NULL; size_t size = 0; int len, ret; memset(spki, 0, sizeof(*spki)); len = i2d_PUBKEY(private_key->private_key.pkey, NULL); if (len < 0) return -1; if ((d = p = malloc(len)) == NULL) { hx509_set_error_string(context, 0, ENOMEM, "Could not produce SubjectPublicKeyInfo for private key"); return ENOMEM; } if (i2d_PUBKEY(private_key->private_key.pkey, &p) < 0) { _hx509_set_error_string_openssl(context, 0, ENOMEM, "Could not produce SubjectPublicKeyInfo for private key"); free(d); return ENOMEM; } ret = decode_SubjectPublicKeyInfo(d, len, spki, &size); free(d); if (ret || size != (size_t)len) { hx509_set_error_string(context, 0, ret, "Could not decode a SubjectPublicKeyInfo value from OpenSSL%s", ret == 0 ? " (length mismatch)" : ""); free_SubjectPublicKeyInfo(spki); return ret; } return 0; } static EVP_PKEY * generate_rsa_pkey(int bits, int pss) { EVP_PKEY_CTX *ctx = NULL; EVP_PKEY *pkey = NULL; ctx = pss ? EVP_PKEY_CTX_new_id(EVP_PKEY_RSA_PSS, NULL) : EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); if (ctx == NULL || EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0 || EVP_PKEY_keygen(ctx, &pkey) <= 0) { EVP_PKEY_free(pkey); pkey = NULL; } EVP_PKEY_CTX_free(ctx); return pkey; } static int rsa_generate_private_key(hx509_context context, struct hx509_generate_private_context *ctx, hx509_private_key private_key) { unsigned long bits = ctx->num_bits ? ctx->num_bits : 2048; private_key->private_key.pkey = generate_rsa_pkey(bits, 0); if (private_key->private_key.pkey == NULL) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Failed to generate RSA key"); return HX509_PARSING_KEY_FAILED; } // XXX Parameterize the digest!! Well, the caller can change this anyways // since the pkey doesn't know what digest we wanted anyways. // // At the very least don't default to SHA-1? 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) { unsigned char *p = NULL; size_t size = 0; int ret; data->data = NULL; data->length = 0; switch (format) { case HX509_KEY_FORMAT_DER: { OSSL_ENCODER_CTX *ctx = OSSL_ENCODER_CTX_new_for_pkey(key->private_key.pkey, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, "DER", "type-specific", // PKCS#1 for RSA NULL); if (ctx == NULL) { _hx509_set_error_string_openssl(context, 0, ENOMEM, "Could not allocate a private key encoder"); return ENOMEM; } ret = OSSL_ENCODER_to_data(ctx, &p, &size); OSSL_ENCODER_CTX_free(ctx); if (ret != 1) { _hx509_set_error_string_openssl(context, 0, HX509_CRYPTO_INTERNAL_ERROR, "Could not encode a private key"); return HX509_CRYPTO_INTERNAL_ERROR; } data->data = malloc(size); if (data->data == NULL) { ret = ENOMEM; hx509_set_error_string(context, 0, ret, "malloc out of memory"); return ret; } data->length = size; memcpy(data->data, p, size); OPENSSL_free(p); break; } default: return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED; } return 0; } 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, NULL }; /* * */ static int dsa_verify_signature(hx509_context context, const struct signature_alg *sig_alg, const Certificate *signer, const AlgorithmIdentifier *alg, const EVP_MD *md, const heim_octet_string *data, const heim_octet_string *sig) { EVP_PKEY_CTX *pctx = NULL; EVP_MD_CTX *mctx = NULL; EVP_PKEY *pkey = NULL; const unsigned char *p; size_t size; int ret = HX509_CRYPTO_INTERNAL_ERROR; md = md ? md : (sig_alg->evp_md ? sig_alg->evp_md() : NULL); p = signer->tbsCertificate.subjectPublicKeyInfo._save.data; size = signer->tbsCertificate.subjectPublicKeyInfo._save.length / 8; pkey = d2i_PUBKEY(NULL, &p, size); if (pkey == NULL) { ret = ENOMEM; // XXX Maybe it's not ENOMEM... _hx509_set_error_string_openssl(context, 0, ret, "Could not parse SubjectPublicKeyInfo (RSA)"); goto out; } if ((mctx = EVP_MD_CTX_new()) == NULL) { ret = ENOMEM; _hx509_set_error_string_openssl(context, 0, ret, "out of memory"); goto out; } if (EVP_DigestVerifyInit(mctx, &pctx, md, NULL, pkey) <= 0) { ret = HX509_CRYPTO_SIG_INVALID_FORMAT; hx509_set_error_string(context, 0, ret, "digest_verify_init"); goto out; } if (EVP_DigestVerify(mctx, sig->data, sig->length, data->data, data->length) == 1) ret = 0; out: EVP_MD_CTX_free(mctx); /* Also frees pctx */ EVP_PKEY_free(pkey); return ret; } #if 0 /* We don't have a dsa_private_key_ops yet, and might never want to */ static EVP_PKEY * evp_from_dsa_pkcs1_der(const unsigned char *der, size_t der_len) { return pkey = d2i_PrivateKey_ex(EVP_PKEY_DSA, NULL, &der, der_len, /* libctx */ NULL, /* propq */ NULL); } static int dsa_parse_private_key(hx509_context context, const void *data, size_t len, hx509_private_key private_key) { private_key->private_key.pkey = evp_from_dsa_pkcs1_der(data, len); if (private_key->private_key.pkey == NULL) return EINVAL; private_key->signature_alg = ASN1_OID_ID_DSA_WITH_SHA1; return 0; } #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 EVP_MD *md, 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; md = md ? md : (sig_alg->evp_md ? sig_alg->evp_md() : NULL); memset(sig, 0, sizeof(*sig)); if (signatureAlgorithm) { int ret; ret = _hx509_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, 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 EVP_MD *md, 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()); md = md ? md : (sig_alg->evp_md ? sig_alg->evp_md() : NULL); if (sig->length != sigsize || sigsize > sizeof(digest)) { hx509_set_error_string(context, 0, HX509_CRYPTO_SIG_INVALID_FORMAT, "SHA256 sigature has wrong length"); return HX509_CRYPTO_SIG_INVALID_FORMAT; } ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, 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; } extern const struct signature_alg ecdsa_with_sha384_alg; extern const struct signature_alg ecdsa_with_sha256_alg; extern const struct signature_alg ecdsa_with_sha1_alg; 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, EVP_sha1, 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, EVP_sha512, 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, EVP_sha384, 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, EVP_sha256, 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, EVP_sha1, 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, EVP_sha1, 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|WEAK_SIG_ALG, 1230739889, EVP_md5, 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, EVP_sha1, 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_SHA384, &_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|WEAK_SIG_ALG, 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) */ extern const struct signature_alg ecdsa_with_sha512_alg; extern const struct signature_alg ecdsa_with_sha384_alg; extern const struct signature_alg ecdsa_with_sha256_alg; extern const struct signature_alg ecdsa_with_sha1_alg; extern const struct signature_alg ed25519_alg; extern const struct signature_alg ed448_alg; static const struct signature_alg *sig_algs[] = { &ed25519_alg, &ed448_alg, &ecdsa_with_sha512_alg, &ecdsa_with_sha384_alg, &ecdsa_with_sha256_alg, &ecdsa_with_sha1_alg, &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 }; const struct signature_alg * _hx509_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; } /* * */ extern hx509_private_key_ops ecdsa_private_key_ops; extern hx509_private_key_ops ed25519_private_key_ops; extern hx509_private_key_ops ed448_private_key_ops; static struct hx509_private_key_ops *private_algs[] = { &rsa_private_key_ops, &ecdsa_private_key_ops, &ed25519_private_key_ops, &ed448_private_key_ops, NULL }; HX509_LIB_FUNCTION hx509_private_key_ops * HX509_LIB_CALL 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'. */ HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_signature_is_weak(hx509_context context, const AlgorithmIdentifier *alg) { const struct signature_alg *md; md = _hx509_find_sig_alg(&alg->algorithm); if (md == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } if (md->flags & WEAK_SIG_ALG) { hx509_set_error_string(context, 0, HX509_CRYPTO_ALGORITHM_BEST_BEFORE, "Algorithm %s is weak", md->name); return HX509_CRYPTO_ALGORITHM_BEST_BEFORE; } return 0; } HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_self_signed_valid(hx509_context context, const AlgorithmIdentifier *alg) { const struct signature_alg *md; md = _hx509_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; } HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_verify_signature(hx509_context context, const hx509_cert cert, const AlgorithmIdentifier *alg, const AlgorithmIdentifier *digest_alg, const heim_octet_string *data, const heim_octet_string *sig) { const struct signature_alg *md = NULL; const struct signature_alg *sa; const Certificate *signer = NULL; const EVP_MD *evp_md; if (cert) signer = _hx509_get_cert(cert); if (digest_alg && (md = _hx509_find_sig_alg(&digest_alg->algorithm)) == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } evp_md = md ? md->evp_md() : NULL; sa = _hx509_find_sig_alg(&alg->algorithm); if (sa == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } if (signer && (sa->flags & PROVIDE_CONF) == 0) { hx509_clear_error_string(context); return HX509_CRYPTO_SIG_NO_CONF; } if (signer == NULL && (sa->flags & REQUIRE_SIGNER)) { hx509_clear_error_string(context); return HX509_CRYPTO_SIGNATURE_WITHOUT_SIGNER; } if (sa->key_oid && signer) { const SubjectPublicKeyInfo *spi; spi = &signer->tbsCertificate.subjectPublicKeyInfo; if (der_heim_oid_cmp(&spi->algorithm.algorithm, sa->key_oid) != 0) { hx509_clear_error_string(context); return HX509_SIG_ALG_DONT_MATCH_KEY_ALG; } } return (*sa->verify_signature)(context, sa, signer, alg, evp_md, data, sig); } HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_create_signature(hx509_context context, const hx509_private_key signer, const AlgorithmIdentifier *alg, const AlgorithmIdentifier *digest_alg, const heim_octet_string *data, AlgorithmIdentifier *signatureAlgorithm, heim_octet_string *sig) { const struct signature_alg *md = NULL; const struct signature_alg *sa; const EVP_MD *evp_md; if (digest_alg && (md = _hx509_find_sig_alg(&digest_alg->algorithm)) == NULL) { hx509_clear_error_string(context); return HX509_SIG_ALG_NO_SUPPORTED; } evp_md = md ? md->evp_md() : NULL; sa = _hx509_find_sig_alg(&alg->algorithm); if (sa == NULL) { hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED, "algorithm no supported"); return HX509_SIG_ALG_NO_SUPPORTED; } if (signer && (sa->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 (*sa->create_signature)(context, sa, signer, alg, evp_md, data, signatureAlgorithm, sig); } HX509_LIB_FUNCTION int HX509_LIB_CALL _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, NULL, data, signatureAlgorithm, &os); if (ret) return ret; sig->data = os.data; sig->length = os.length * 8; return 0; } HX509_LIB_FUNCTION int HX509_LIB_CALL _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; const unsigned char *p; unsigned char *to; size_t size, tosize = 0; EVP_PKEY_CTX *pctx; EVP_PKEY *pkey; int ret; ciphertext->data = NULL; ciphertext->length = 0; spi = &cert->tbsCertificate.subjectPublicKeyInfo; p = spi->_save.data; size = spi->_save.length; pkey = d2i_PUBKEY(NULL, &p, size); if (pkey == NULL) { _hx509_set_error_string_openssl(context, 0, HX509_CRYPTO_INTERNAL_ERROR, "Could not parse SubjectPublicKeyInfo"); return HX509_CRYPTO_INTERNAL_ERROR; } pctx = EVP_PKEY_CTX_new_from_pkey(context->ossl->libctx, pkey, context->ossl->propq); if (pctx == NULL || // XXX Want OEAP instead pls EVP_PKEY_encrypt_init(pctx) != 1 || EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) <= 0 || EVP_PKEY_encrypt(pctx, NULL, &tosize, cleartext->data, cleartext->length) <= 0) { _hx509_set_error_string_openssl(context, 0, ENOMEM, "Could not set up to encrypt to a public key"); EVP_PKEY_CTX_free(pctx); EVP_PKEY_free(pkey); return ENOMEM; } if ((to = malloc(tosize)) == NULL) { EVP_PKEY_CTX_free(pctx); EVP_PKEY_free(pkey); hx509_set_error_string(context, 0, ENOMEM, "out of memory"); return ENOMEM; } if (EVP_PKEY_encrypt(pctx, to, &tosize, cleartext->data, cleartext->length) <= 0) { _hx509_set_error_string_openssl(context, 0, HX509_CRYPTO_INTERNAL_ERROR, "Could not encrypt to a public key"); EVP_PKEY_CTX_free(pctx); EVP_PKEY_free(pkey); return HX509_CRYPTO_INTERNAL_ERROR; } ciphertext->length = tosize; ciphertext->data = to; // XXX Want OEAP instead pls 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"); EVP_PKEY_CTX_free(pctx); EVP_PKEY_free(pkey); return ENOMEM; } EVP_PKEY_CTX_free(pctx); EVP_PKEY_free(pkey); return 0; } HX509_LIB_FUNCTION int HX509_LIB_CALL 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) { EVP_PKEY_CTX *pctx; size_t len; cleartext->data = NULL; cleartext->length = 0; if (p->private_key.pkey == NULL) { hx509_set_error_string(context, 0, HX509_PRIVATE_KEY_MISSING, "Private key missing"); return HX509_PRIVATE_KEY_MISSING; } pctx = EVP_PKEY_CTX_new_from_pkey(context->ossl->libctx, p->private_key.pkey, context->ossl->propq); if (pctx == NULL || EVP_PKEY_decrypt_init(pctx) != 1 || EVP_PKEY_decrypt(pctx, NULL, &len, ciphertext->data, ciphertext->length) < 0) { _hx509_set_error_string_openssl(context, 0, ENOMEM, "Could not set up to decrypt with a private key"); EVP_PKEY_CTX_free(pctx); return ENOMEM; } cleartext->length = len; cleartext->data = malloc(len); if (cleartext->data == NULL) { hx509_set_error_string(context, 0, ENOMEM, "Could not set up to decrypt with a private key"); EVP_PKEY_CTX_free(pctx); return ENOMEM; } if (EVP_PKEY_decrypt(pctx, cleartext->data, &cleartext->length, ciphertext->data, ciphertext->length) < 0) { hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR, "Could not decrypt with a private key"); EVP_PKEY_CTX_free(pctx); return HX509_CRYPTO_INTERNAL_ERROR; } EVP_PKEY_CTX_free(pctx); return 0; } HX509_LIB_FUNCTION int HX509_LIB_CALL 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); if (ret && format == HX509_KEY_FORMAT_PKCS8) { PKCS8PrivateKeyInfo ki; hx509_private_key key; /* Re-enter to try parsing the DER-encoded key from PKCS#8 envelope */ ret = decode_PKCS8PrivateKeyInfo(data, len, &ki, NULL); if (ret) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Failed to parse PKCS#8-encoded private " "key"); return HX509_PARSING_KEY_FAILED; } ret = hx509_parse_private_key(context, &ki.privateKeyAlgorithm, ki.privateKey.data, ki.privateKey.length, HX509_KEY_FORMAT_DER, &key); free_PKCS8PrivateKeyInfo(&ki); if (ret) { hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, "Failed to parse RSA key from PKCS#8 " "envelope"); return HX509_PARSING_KEY_FAILED; } *private_key = key; } return ret; } /* * */ HX509_LIB_FUNCTION int HX509_LIB_CALL 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); } HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_generate_private_key_init(hx509_context context, const heim_oid *oid, struct hx509_generate_private_context **ctx) { *ctx = NULL; /* Check that we support this key type */ if (der_heim_oid_cmp(oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) != 0 && der_heim_oid_cmp(oid, ASN1_OID_ID_ECPUBLICKEY) != 0 && der_heim_oid_cmp(oid, ASN1_OID_ID_ED25519) != 0 && der_heim_oid_cmp(oid, ASN1_OID_ID_ED448) != 0) { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "unsupported key type for generation"); return HX509_ALG_NOT_SUPP; } *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; } HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_generate_private_key_is_ca(hx509_context context, struct hx509_generate_private_context *ctx) { ctx->isCA = 1; return 0; } HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_generate_private_key_bits(hx509_context context, struct hx509_generate_private_context *ctx, unsigned long bits) { ctx->num_bits = bits; return 0; } HX509_LIB_FUNCTION void HX509_LIB_CALL _hx509_generate_private_key_free(struct hx509_generate_private_context **ctx) { free(*ctx); *ctx = NULL; } HX509_LIB_FUNCTION int HX509_LIB_CALL _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_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; /* * */ HX509_LIB_FUNCTION int HX509_LIB_CALL hx509_private_key_init(hx509_private_key *key, hx509_private_key_ops *ops, void *keydata) { (void) keydata; *key = calloc(1, sizeof(**key)); if (*key == NULL) return ENOMEM; (*key)->ref = 1; (*key)->ops = ops; return 0; } HX509_LIB_FUNCTION hx509_private_key HX509_LIB_CALL _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; } HX509_LIB_FUNCTION const char * HX509_LIB_CALL _hx509_private_pem_name(hx509_private_key key) { return key->ops->pemtype; } HX509_LIB_FUNCTION int HX509_LIB_CALL hx509_private_key_free(hx509_private_key *keyp) { hx509_private_key key; if (keyp == NULL || (key = *keyp) == NULL) return 0; *keyp = NULL; if (key->ref == 0) // XXX Use atomics; don't check for zero anyways _hx509_abort("key refcount == 0 on free"); if (--key->ref > 0) // XXX Use atomics return 0; EVP_PKEY_free(key->private_key.pkey); key->private_key.pkey = NULL; free(key); return 0; } HX509_LIB_FUNCTION void HX509_LIB_CALL hx509_private_key_assign_rsa(hx509_private_key key, void *ptr) { if (key->private_key.pkey) EVP_PKEY_free(key->private_key.pkey); key->private_key.pkey = ptr; key->signature_alg = ASN1_OID_ID_PKCS1_SHA1WITHRSAENCRYPTION; key->md = &pkcs1_rsa_sha1_alg; } HX509_LIB_FUNCTION int HX509_LIB_CALL _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; } HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_private_key_exportable(hx509_private_key key) { if (key->ops->export == NULL) return 0; return 1; } /* XXX Remove */ HX509_LIB_FUNCTION BIGNUM * HX509_LIB_CALL _hx509_private_key_get_internal(hx509_context context, hx509_private_key key, const char *type) { return NULL; } HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_private_key_export(hx509_context context, const hx509_private_key key, hx509_key_format_t format, heim_octet_string *data) { data->length = 0; data->data = NULL; if (key->ops->export == NULL) { hx509_clear_error_string(context); return HX509_UNIMPLEMENTED_OPERATION; } if (format == HX509_KEY_FORMAT_PKCS8) { PKCS8PrivateKeyInfo ki; size_t size; int ret; memset(&ki, 0, sizeof(ki)); ki.attributes = NULL; /* No localKeyId needed */ ki.privateKey.data = NULL; ki.privateKeyAlgorithm.algorithm.components = NULL; ret = der_parse_hex_heim_integer("00", &ki.version); if (ret == 0) ret = _hx509_private_key_oid(context, key, &ki.privateKeyAlgorithm.algorithm); if (ret == 0) /* Re-enter */ ret = _hx509_private_key_export(context, key, HX509_KEY_FORMAT_DER, &ki.privateKey); /* * XXX To set ki.privateKeyAlgorithm.parameters we'll need to either * move this code into the *key->ops->export() functions, or expand * their signature to allow them to set it for us, or add a method to * hx509_private_key_ops that allows us to get the parameters from the * backend. */ ki.privateKeyAlgorithm.parameters = NULL; if (ret == 0) ASN1_MALLOC_ENCODE(PKCS8PrivateKeyInfo, data->data, data->length, &ki, &size, ret); free_PKCS8PrivateKeyInfo(&ki); if (ret == 0 && size != data->length) ret = HX509_CRYPTO_INTERNAL_ERROR; if (ret) hx509_set_error_string(context, 0, ret, "Private key PKCS#8 encoding failed"); return ret; } 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 = context->ossl ? context->ossl->rc2_40_cbc : EVP_rc2_40_cbc(); p->maximum_effective_key = 40; break; case 120: crypto->c = context->ossl ? context->ossl->rc2_64_cbc : EVP_rc2_64_cbc(); p->maximum_effective_key = 64; break; case 58: crypto->c = context->ossl ? context->ossl->rc2_cbc : 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; } HX509_LIB_FUNCTION const heim_oid * HX509_LIB_CALL 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; } HX509_LIB_FUNCTION int HX509_LIB_CALL 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; } HX509_LIB_FUNCTION const char * HX509_LIB_CALL hx509_crypto_provider(hx509_crypto crypto) { return "unknown"; } HX509_LIB_FUNCTION void HX509_LIB_CALL 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); } HX509_LIB_FUNCTION int HX509_LIB_CALL hx509_crypto_set_key_name(hx509_crypto crypto, const char *name) { return 0; } HX509_LIB_FUNCTION void HX509_LIB_CALL hx509_crypto_allow_weak(hx509_crypto crypto) { crypto->flags |= ALLOW_WEAK; } HX509_LIB_FUNCTION void HX509_LIB_CALL 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"); } } HX509_LIB_FUNCTION int HX509_LIB_CALL 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; } HX509_LIB_FUNCTION int HX509_LIB_CALL 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; } HX509_LIB_FUNCTION int HX509_LIB_CALL 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); } HX509_LIB_FUNCTION int HX509_LIB_CALL 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); } HX509_LIB_FUNCTION int HX509_LIB_CALL 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; } HX509_LIB_FUNCTION int HX509_LIB_CALL 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); if ((evp = EVP_CIPHER_CTX_new()) == NULL || EVP_CipherInit_ex(evp, crypto->c, NULL, crypto->key.data, ivec->data, 1) <= 0) { ret = ENOMEM; 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 != length + padsize) { 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_free(evp); return ret; } HX509_LIB_FUNCTION int HX509_LIB_CALL 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 = NULL; 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; /* * Note: The legacy provider for weak crypto (RC2, 3DES-CBC, etc.) used in * PKCS#12 is now loaded during hx509_context_init() and unloaded during * hx509_context_free(). This ensures proper cleanup without memory leaks. */ 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; if ((evp = EVP_CIPHER_CTX_new()) == NULL) return ENOMEM; if (EVP_CipherInit_ex(evp, crypto->c, NULL, crypto->key.data, idata, 0) <= 0) { ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } clear->length = length; clear->data = malloc(length); if (clear->data == NULL) { ret = ENOMEM; goto out; } if (EVP_Cipher(evp, clear->data, data, length) <= 0) { ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } 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; } } } EVP_CIPHER_CTX_free(evp); return 0; out: EVP_CIPHER_CTX_free(evp); 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(hx509_context context, const heim_oid *oid, const EVP_CIPHER **c, const EVP_MD **md, PBE_string2key_func *s2k) { hx509_context_ossl ossl = context ? context->ossl : NULL; if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND40BITRC2_CBC) == 0) { *c = ossl ? ossl->rc2_40_cbc : EVP_rc2_40_cbc(); if (*c == NULL) return NULL; *md = ossl ? ossl->sha1 : EVP_sha1(); if (*md == NULL) return NULL; *s2k = PBE_string2key; return &asn1_oid_private_rc2_40; } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND128BITRC2_CBC) == 0) { *c = ossl ? ossl->rc2_cbc : EVP_rc2_cbc(); if (*c == NULL) return NULL; *md = ossl ? ossl->sha1 : EVP_sha1(); if (*md == NULL) return NULL; *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 = ossl ? ossl->rc4_40 : EVP_rc4_40(); if (*c == NULL) return NULL; *md = ossl ? ossl->sha1 : EVP_sha1(); if (*md == NULL) return NULL; *s2k = PBE_string2key; return NULL; } else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND128BITRC4) == 0) { *c = ossl ? ossl->rc4 : EVP_rc4(); if (*c == NULL) return NULL; *md = ossl ? ossl->sha1 : EVP_sha1(); if (*md == NULL) return NULL; *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 = ossl ? ossl->des_ede3_cbc : EVP_des_ede3_cbc(); if (*c == NULL) return NULL; *md = ossl ? ossl->sha1 : EVP_sha1(); if (*md == NULL) return NULL; *s2k = PBE_string2key; return ASN1_OID_ID_PKCS3_DES_EDE3_CBC; } return NULL; } /* * */ HX509_LIB_FUNCTION int HX509_LIB_CALL _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; } /* * Try PBES2 decryption - returns 1 if handled (success or error), 0 if not PBES2 */ static int try_pbes2_decrypt(hx509_context context, hx509_lock lock, const AlgorithmIdentifier *ai, const heim_octet_string *econtent, heim_octet_string *content) { const struct _hx509_password *pw; PBES2_params pbes2; PBKDF2_params pbkdf2; const EVP_MD *prf_md = NULL; const EVP_CIPHER *cipher = NULL; const heim_oid *enc_oid = NULL; hx509_context_ossl ossl = context ? context->ossl : NULL; heim_octet_string key, iv, iv_param; int ret; size_t i; memset(&pbes2, 0, sizeof(pbes2)); memset(&pbkdf2, 0, sizeof(pbkdf2)); memset(&key, 0, sizeof(key)); memset(&iv, 0, sizeof(iv)); memset(&iv_param, 0, sizeof(iv_param)); memset(content, 0, sizeof(*content)); /* Check if this is PBES2 */ if (der_heim_oid_cmp(&ai->algorithm, ASN1_OID_ID_PBES2) != 0) return -1; /* Not PBES2, caller should try legacy PBE */ if (ai->parameters == NULL) { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "PBES2 missing parameters"); return HX509_ALG_NOT_SUPP; } ret = decode_PBES2_params(ai->parameters->data, ai->parameters->length, &pbes2, NULL); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode PBES2 parameters"); return ret; } /* Check that the KDF is PBKDF2 */ if (der_heim_oid_cmp(&pbes2.keyDerivationFunc.algorithm, ASN1_OID_ID_PBKDF2) != 0) { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "PBES2 KDF algorithm not supported (only PBKDF2)"); ret = HX509_ALG_NOT_SUPP; goto out; } /* Decode PBKDF2 parameters */ if (pbes2.keyDerivationFunc.parameters == NULL) { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "PBKDF2 missing parameters"); ret = HX509_ALG_NOT_SUPP; goto out; } ret = decode_PBKDF2_params(pbes2.keyDerivationFunc.parameters->data, pbes2.keyDerivationFunc.parameters->length, &pbkdf2, NULL); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode PBKDF2 parameters"); goto out; } /* Determine the PRF (defaults to HMAC-SHA1) */ if (pbkdf2.prf == NULL) { prf_md = ossl ? ossl->sha1 : EVP_sha1(); } else if (der_heim_oid_cmp(&pbkdf2.prf->algorithm, ASN1_OID_ID_HMACWITHSHA1) == 0) { prf_md = ossl ? ossl->sha1 : EVP_sha1(); } else if (der_heim_oid_cmp(&pbkdf2.prf->algorithm, ASN1_OID_ID_HMACWITHSHA256) == 0) { prf_md = ossl ? ossl->sha256 : EVP_sha256(); } else if (der_heim_oid_cmp(&pbkdf2.prf->algorithm, ASN1_OID_ID_HMACWITHSHA384) == 0) { prf_md = ossl ? ossl->sha384 : EVP_sha384(); } else if (der_heim_oid_cmp(&pbkdf2.prf->algorithm, ASN1_OID_ID_HMACWITHSHA512) == 0) { prf_md = ossl ? ossl->sha512 : EVP_sha512(); } else { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "PBKDF2 PRF algorithm not supported"); ret = HX509_ALG_NOT_SUPP; goto out; } if (prf_md == NULL) { ret = HX509_ALG_NOT_SUPP; goto out; } /* Determine the encryption cipher * Note: Use the rfc2459 OID macros (ASN1_OID_ID_AES_*_CBC) for enc_oid * since those are what hx509_crypto_init() uses for cipher lookup */ if (der_heim_oid_cmp(&pbes2.encryptionScheme.algorithm, ASN1_OID_ID_AES_128_CBC) == 0) { cipher = ossl ? ossl->aes_128_cbc : EVP_aes_128_cbc(); enc_oid = ASN1_OID_ID_AES_128_CBC; } else if (der_heim_oid_cmp(&pbes2.encryptionScheme.algorithm, ASN1_OID_ID_AES_192_CBC) == 0) { cipher = ossl ? ossl->aes_192_cbc : EVP_aes_192_cbc(); enc_oid = ASN1_OID_ID_AES_192_CBC; } else if (der_heim_oid_cmp(&pbes2.encryptionScheme.algorithm, ASN1_OID_ID_AES_256_CBC) == 0) { cipher = ossl ? ossl->aes_256_cbc : EVP_aes_256_cbc(); enc_oid = ASN1_OID_ID_AES_256_CBC; } else if (der_heim_oid_cmp(&pbes2.encryptionScheme.algorithm, ASN1_OID_ID_PKCS3_DES_EDE3_CBC) == 0) { cipher = ossl ? ossl->des_ede3_cbc : EVP_des_ede3_cbc(); enc_oid = ASN1_OID_ID_PKCS3_DES_EDE3_CBC; } else { hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, "PBES2 encryption algorithm not supported"); ret = HX509_ALG_NOT_SUPP; goto out; } if (cipher == NULL) { ret = HX509_ALG_NOT_SUPP; goto out; } /* Extract the IV from the encryption scheme parameters */ if (pbes2.encryptionScheme.parameters == NULL) { hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR, "PBES2 encryption scheme missing IV"); ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } ret = decode_PKCS12_OctetString(pbes2.encryptionScheme.parameters->data, pbes2.encryptionScheme.parameters->length, &iv_param, NULL); if (ret) { hx509_set_error_string(context, 0, ret, "Failed to decode PBES2 IV"); goto out; } if (iv_param.length != (size_t)EVP_CIPHER_iv_length(cipher)) { hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR, "PBES2 IV length mismatch"); ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } /* Allocate key and IV buffers */ key.length = EVP_CIPHER_key_length(cipher); key.data = malloc(key.length); if (key.data == NULL) { ret = ENOMEM; hx509_clear_error_string(context); goto out; } iv.length = EVP_CIPHER_iv_length(cipher); iv.data = malloc(iv.length); if (iv.data == NULL) { ret = ENOMEM; hx509_clear_error_string(context); goto out; } memcpy(iv.data, iv_param.data, iv.length); 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; int passwordlen; if (i < pw->len) password = pw->val[i]; else if (i < pw->len + 1) password = ""; else password = NULL; passwordlen = password ? strlen(password) : 0; /* Derive the key using PBKDF2 */ if (PKCS5_PBKDF2_HMAC(password, passwordlen, pbkdf2.salt.data, pbkdf2.salt.length, pbkdf2.iterationCount, prf_md, key.length, key.data) != 1) { hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR, "PBKDF2 key derivation failed"); ret = HX509_CRYPTO_INTERNAL_ERROR; goto out; } /* Create the crypto context */ ret = hx509_crypto_init(context, NULL, enc_oid, &crypto); if (ret) goto out; ret = hx509_crypto_set_key_data(crypto, key.data, key.length); if (ret) { hx509_crypto_destroy(crypto); goto out; } ret = hx509_crypto_decrypt(crypto, econtent->data, econtent->length, &iv, content); hx509_crypto_destroy(crypto); if (ret == 0) goto out; } out: free_PBES2_params(&pbes2); free_PBKDF2_params(&pbkdf2); der_free_octet_string(&iv_param); if (key.data) der_free_octet_string(&key); if (iv.data) der_free_octet_string(&iv); return ret; } /* * */ HX509_LIB_FUNCTION int HX509_LIB_CALL _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; /* First try PBES2 (modern PKCS#12) */ ret = try_pbes2_decrypt(context, lock, ai, econtent, content); if (ret != -1) return ret; /* PBES2, either error or success */ /* Fall back to legacy PKCS#12 PBE */ memset(&key, 0, sizeof(key)); memset(&iv, 0, sizeof(iv)); memset(content, 0, sizeof(*content)); enc_oid = find_string2key(context, &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; } /* * */ HX509_LIB_FUNCTION int HX509_LIB_CALL _hx509_match_keys(hx509_cert c, hx509_private_key key) { const SubjectPublicKeyInfo *spi; const Certificate *cert; const unsigned char *p; EVP_PKEY *pkey; size_t size; int ret = 0; cert = _hx509_get_cert(c); spi = &cert->tbsCertificate.subjectPublicKeyInfo; p = spi->_save.data; size = spi->_save.length; if ((pkey = d2i_PUBKEY(NULL, &p, size))) ret = EVP_PKEY_eq(key->private_key.pkey, pkey); EVP_PKEY_free(pkey); return ret; } static const heim_oid * find_keytype(const hx509_private_key key) { const struct signature_alg *md; if (key == NULL) return NULL; md = _hx509_find_sig_alg(key->signature_alg); if (md == NULL) return NULL; return md->key_oid; } HX509_LIB_FUNCTION int HX509_LIB_CALL 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; } HX509_LIB_FUNCTION int HX509_LIB_CALL 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; } HX509_LIB_FUNCTION void HX509_LIB_CALL hx509_crypto_free_algs(AlgorithmIdentifier *val, unsigned int len) { unsigned int i; for (i = 0; i < len; i++) free_AlgorithmIdentifier(&val[i]); free(val); }