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