- No more OpenSSL 1.x support - Remove 1DES and 3DES - Remove NETLOGON, NTLM (client and 'digest' service)
2986 lines
79 KiB
C
2986 lines
79 KiB
C
/*
|
||
* 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);
|
||
}
|