Files
heimdal/lib/hx509/crypto.c
Nicolas Williams cbe156d927 Use OpenSSL 3.x _only_ and implement RFC 8636
- No more OpenSSL 1.x support
 - Remove 1DES and 3DES
 - Remove NETLOGON, NTLM (client and 'digest' service)
2026-01-18 19:06:16 -06:00

2986 lines
79 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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<N> signatures
* RFC5754 specifies NULL parameters for sha<N>WithRSAEncryption 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);
}