diff options
Diffstat (limited to 'contrib/unbound/sldns/keyraw.c')
| -rw-r--r-- | contrib/unbound/sldns/keyraw.c | 719 | 
1 files changed, 719 insertions, 0 deletions
| diff --git a/contrib/unbound/sldns/keyraw.c b/contrib/unbound/sldns/keyraw.c new file mode 100644 index 000000000000..42a9262a30da --- /dev/null +++ b/contrib/unbound/sldns/keyraw.c @@ -0,0 +1,719 @@ +/* + * keyraw.c - raw key operations and conversions + * + * (c) NLnet Labs, 2004-2008 + * + * See the file LICENSE for the license + */ +/** + * \file + * Implementation of raw DNSKEY functions (work on wire rdata). + */ + +#include "config.h" +#include "sldns/keyraw.h" +#include "sldns/rrdef.h" + +#ifdef HAVE_SSL +#include <openssl/ssl.h> +#include <openssl/evp.h> +#include <openssl/rand.h> +#include <openssl/err.h> +#include <openssl/md5.h> +#ifdef HAVE_OPENSSL_ENGINE_H +#  include <openssl/engine.h> +#endif +#ifdef HAVE_OPENSSL_BN_H +#include <openssl/bn.h> +#endif +#ifdef HAVE_OPENSSL_PARAM_BUILD_H +#  include <openssl/param_build.h> +#else +#  ifdef HAVE_OPENSSL_RSA_H +#  include <openssl/rsa.h> +#  endif +#  ifdef HAVE_OPENSSL_DSA_H +#  include <openssl/dsa.h> +#  endif +#endif +#endif /* HAVE_SSL */ + +size_t +sldns_rr_dnskey_key_size_raw(const unsigned char* keydata, +	const size_t len, int alg) +{ +	/* for DSA keys */ +	uint8_t t; +	 +	/* for RSA keys */ +	uint16_t exp; +	uint16_t int16; +	 +	switch ((sldns_algorithm)alg) { +	case LDNS_DSA: +	case LDNS_DSA_NSEC3: +		if (len > 0) { +			t = keydata[0]; +			return (64 + t*8)*8; +		} else { +			return 0; +		} +		break; +	case LDNS_RSAMD5: +	case LDNS_RSASHA1: +	case LDNS_RSASHA1_NSEC3: +#ifdef USE_SHA2 +	case LDNS_RSASHA256: +	case LDNS_RSASHA512: +#endif +		if (len > 0) { +			if (keydata[0] == 0) { +				/* big exponent */ +				if (len > 3) { +					memmove(&int16, keydata + 1, 2); +					exp = ntohs(int16); +					return (len - exp - 3)*8; +				} else { +					return 0; +				} +			} else { +				exp = keydata[0]; +				return (len-exp-1)*8; +			} +		} else { +			return 0; +		} +		break; +#ifdef USE_GOST +	case LDNS_ECC_GOST: +		return 512; +#endif +#ifdef USE_ECDSA +        case LDNS_ECDSAP256SHA256: +                return 256; +        case LDNS_ECDSAP384SHA384: +                return 384; +#endif +#ifdef USE_ED25519 +	case LDNS_ED25519: +		return 256; +#endif +#ifdef USE_ED448 +	case LDNS_ED448: +		return 456; +#endif +	default: +		return 0; +	} +} + +uint16_t sldns_calc_keytag_raw(uint8_t* key, size_t keysize) +{ +	if(keysize < 4) { +		return 0; +	} +	/* look at the algorithm field, copied from 2535bis */ +	if (key[3] == LDNS_RSAMD5) { +		uint16_t ac16 = 0; +		if (keysize > 4) { +			memmove(&ac16, key + keysize - 3, 2); +		} +		ac16 = ntohs(ac16); +		return (uint16_t) ac16; +	} else { +		size_t i; +		uint32_t ac32 = 0; +		for (i = 0; i < keysize; ++i) { +			ac32 += ((i & 1)) ? key[i] : key[i] << 8; +		} +		ac32 += (ac32 >> 16) & 0xFFFF; +		return (uint16_t) (ac32 & 0xFFFF); +	} +} + +#ifdef HAVE_SSL +#ifdef USE_GOST +/** store GOST engine reference loaded into OpenSSL library */ +ENGINE* sldns_gost_engine = NULL; + +int +sldns_key_EVP_load_gost_id(void) +{ +	static int gost_id = 0; +	const EVP_PKEY_ASN1_METHOD* meth; +	ENGINE* e; + +	if(gost_id) return gost_id; + +	/* see if configuration loaded gost implementation from other engine*/ +	meth = EVP_PKEY_asn1_find_str(NULL, "gost2001", -1); +	if(meth) { +		EVP_PKEY_asn1_get0_info(&gost_id, NULL, NULL, NULL, NULL, meth); +		return gost_id; +	} + +	/* see if engine can be loaded already */ +	e = ENGINE_by_id("gost"); +	if(!e) { +		/* load it ourself, in case statically linked */ +		ENGINE_load_builtin_engines(); +		ENGINE_load_dynamic(); +		e = ENGINE_by_id("gost"); +	} +	if(!e) { +		/* no gost engine in openssl */ +		return 0; +	} +	if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) { +		ENGINE_finish(e); +		ENGINE_free(e); +		return 0; +	} + +	meth = EVP_PKEY_asn1_find_str(&e, "gost2001", -1); +	if(!meth) { +		/* algo not found */ +		ENGINE_finish(e); +		ENGINE_free(e); +		return 0; +	} +        /* Note: do not ENGINE_finish and ENGINE_free the acquired engine +         * on some platforms this frees up the meth and unloads gost stuff */ +        sldns_gost_engine = e; +	 +	EVP_PKEY_asn1_get0_info(&gost_id, NULL, NULL, NULL, NULL, meth); +	return gost_id; +}  + +void sldns_key_EVP_unload_gost(void) +{ +        if(sldns_gost_engine) { +                ENGINE_finish(sldns_gost_engine); +                ENGINE_free(sldns_gost_engine); +                sldns_gost_engine = NULL; +        } +} +#endif /* USE_GOST */ + +#ifdef USE_DSA +/* Retrieve params as BIGNUM from raw buffer */ +static int +sldns_key_dsa_buf_bignum(unsigned char* key, size_t len, BIGNUM** p, +	BIGNUM** q, BIGNUM** g, BIGNUM** y) +{ +	uint8_t T; +	uint16_t length; +	uint16_t offset; + +	if(len == 0) +		return 0; +	T = (uint8_t)key[0]; +	length = (64 + T * 8); +	offset = 1; + +	if (T > 8) { +		return 0; +	} +	if(len < (size_t)1 + SHA_DIGEST_LENGTH + 3*length) +		return 0; + +	*q = BN_bin2bn(key+offset, SHA_DIGEST_LENGTH, NULL); +	offset += SHA_DIGEST_LENGTH; + +	*p = BN_bin2bn(key+offset, (int)length, NULL); +	offset += length; + +	*g = BN_bin2bn(key+offset, (int)length, NULL); +	offset += length; + +	*y = BN_bin2bn(key+offset, (int)length, NULL); + +	if(!*q || !*p || !*g || !*y) { +		BN_free(*q); +		BN_free(*p); +		BN_free(*g); +		BN_free(*y); +		return 0; +	} +	return 1; +} + +#ifndef HAVE_OSSL_PARAM_BLD_NEW +DSA * +sldns_key_buf2dsa_raw(unsigned char* key, size_t len) +{ +	DSA *dsa; +	BIGNUM *Q=NULL, *P=NULL, *G=NULL, *Y=NULL; +	if(!sldns_key_dsa_buf_bignum(key, len, &P, &Q, &G, &Y)) { +		return NULL; +	} +	/* create the key and set its properties */ +	if(!(dsa = DSA_new())) { +		return NULL; +	} +#if OPENSSL_VERSION_NUMBER < 0x10100000 || \ +        (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x02070000f) +#ifndef S_SPLINT_S +	dsa->p = P; +	dsa->q = Q; +	dsa->g = G; +	dsa->pub_key = Y; +#endif /* splint */ + +#else /* OPENSSL_VERSION_NUMBER */ +	if (!DSA_set0_pqg(dsa, P, Q, G)) { +		/* QPG not yet attached, need to free */ +		BN_free(Q); +		BN_free(P); +		BN_free(G); + +		DSA_free(dsa); +		BN_free(Y); +		return NULL; +	} +	if (!DSA_set0_key(dsa, Y, NULL)) { +		/* QPG attached, cleaned up by DSA_free() */ +		DSA_free(dsa); +		BN_free(Y); +		return NULL; +	} +#endif + +	return dsa; +} +#endif /* HAVE_OSSL_PARAM_BLD_NEW */ + +EVP_PKEY *sldns_key_dsa2pkey_raw(unsigned char* key, size_t len) +{ +#ifdef HAVE_OSSL_PARAM_BLD_NEW +	EVP_PKEY* evp_key = NULL; +	EVP_PKEY_CTX* ctx; +	BIGNUM *p=NULL, *q=NULL, *g=NULL, *y=NULL; +	OSSL_PARAM_BLD* param_bld; +	OSSL_PARAM* params = NULL; +	if(!sldns_key_dsa_buf_bignum(key, len, &p, &q, &g, &y)) { +		return NULL; +	} + +	param_bld = OSSL_PARAM_BLD_new(); +	if(!param_bld) { +		BN_free(p); +		BN_free(q); +		BN_free(g); +		BN_free(y); +		return NULL; +	} +	if(!OSSL_PARAM_BLD_push_BN(param_bld, "p", p) || +	   !OSSL_PARAM_BLD_push_BN(param_bld, "g", g) || +	   !OSSL_PARAM_BLD_push_BN(param_bld, "q", q) || +	   !OSSL_PARAM_BLD_push_BN(param_bld, "pub", y)) { +		OSSL_PARAM_BLD_free(param_bld); +		BN_free(p); +		BN_free(q); +		BN_free(g); +		BN_free(y); +		return NULL; +	} +	params = OSSL_PARAM_BLD_to_param(param_bld); +	OSSL_PARAM_BLD_free(param_bld); + +	ctx = EVP_PKEY_CTX_new_from_name(NULL, "DSA", NULL); +	if(!ctx) { +		OSSL_PARAM_free(params); +		BN_free(p); +		BN_free(q); +		BN_free(g); +		BN_free(y); +		return NULL; +	} +	if(EVP_PKEY_fromdata_init(ctx) <= 0) { +		EVP_PKEY_CTX_free(ctx); +		OSSL_PARAM_free(params); +		BN_free(p); +		BN_free(q); +		BN_free(g); +		BN_free(y); +		return NULL; +	} +	if(EVP_PKEY_fromdata(ctx, &evp_key, EVP_PKEY_PUBLIC_KEY, params) <= 0) { +		EVP_PKEY_CTX_free(ctx); +		OSSL_PARAM_free(params); +		BN_free(p); +		BN_free(q); +		BN_free(g); +		BN_free(y); +		return NULL; +	} + +	EVP_PKEY_CTX_free(ctx); +	OSSL_PARAM_free(params); +	BN_free(p); +	BN_free(q); +	BN_free(g); +	BN_free(y); +	return evp_key; +#else +	DSA* dsa; +	EVP_PKEY* evp_key = EVP_PKEY_new(); +	if(!evp_key) { +		return NULL; +	} +	dsa = sldns_key_buf2dsa_raw(key, len); +	if(!dsa) { +		EVP_PKEY_free(evp_key); +		return NULL; +	} +	if(EVP_PKEY_assign_DSA(evp_key, dsa) == 0) { +		DSA_free(dsa); +		EVP_PKEY_free(evp_key); +		return NULL; +	} +	return evp_key; +#endif +} +#endif /* USE_DSA */ + +/* Retrieve params as BIGNUM from raw buffer, n is modulus, e is exponent */ +static int +sldns_key_rsa_buf_bignum(unsigned char* key, size_t len, BIGNUM** n, +	BIGNUM** e) +{ +	uint16_t offset; +	uint16_t exp; +	uint16_t int16; + +	if (len == 0) +		return 0; +	if (key[0] == 0) { +		if(len < 3) +			return 0; +		memmove(&int16, key+1, 2); +		exp = ntohs(int16); +		offset = 3; +	} else { +		exp = key[0]; +		offset = 1; +	} + +	/* key length at least one */ +	if(len < (size_t)offset + exp + 1) +		return 0; + +	/* Exponent */ +	*e = BN_new(); +	if(!*e) return 0; +	(void) BN_bin2bn(key+offset, (int)exp, *e); +	offset += exp; + +	/* Modulus */ +	*n = BN_new(); +	if(!*n) { +		BN_free(*e); +		return 0; +	} +	/* length of the buffer must match the key length! */ +	(void) BN_bin2bn(key+offset, (int)(len - offset), *n); +	return 1; +} + +#ifndef HAVE_OSSL_PARAM_BLD_NEW +RSA * +sldns_key_buf2rsa_raw(unsigned char* key, size_t len) +{ +	BIGNUM* modulus = NULL; +	BIGNUM* exponent = NULL; +	RSA *rsa; +	if(!sldns_key_rsa_buf_bignum(key, len, &modulus, &exponent)) +		return NULL; +	rsa = RSA_new(); +	if(!rsa) { +		BN_free(exponent); +		BN_free(modulus); +		return NULL; +	} +#if OPENSSL_VERSION_NUMBER < 0x10100000 || \ +        (defined(HAVE_LIBRESSL) && LIBRESSL_VERSION_NUMBER < 0x02070000f) +#ifndef S_SPLINT_S +	rsa->n = modulus; +	rsa->e = exponent; +#endif /* splint */ + +#else /* OPENSSL_VERSION_NUMBER */ +	if (!RSA_set0_key(rsa, modulus, exponent, NULL)) { +		BN_free(exponent); +		BN_free(modulus); +		RSA_free(rsa); +		return NULL; +	} +#endif + +	return rsa; +} +#endif /* HAVE_OSSL_PARAM_BLD_NEW */ + +EVP_PKEY* sldns_key_rsa2pkey_raw(unsigned char* key, size_t len) +{ +#ifdef HAVE_OSSL_PARAM_BLD_NEW +	EVP_PKEY* evp_key = NULL; +	EVP_PKEY_CTX* ctx; +	BIGNUM *n=NULL, *e=NULL; +	OSSL_PARAM_BLD* param_bld; +	OSSL_PARAM* params = NULL; + +	if(!sldns_key_rsa_buf_bignum(key, len, &n, &e)) { +		return NULL; +	} + +	param_bld = OSSL_PARAM_BLD_new(); +	if(!param_bld) { +		BN_free(n); +		BN_free(e); +		return NULL; +	} +	if(!OSSL_PARAM_BLD_push_BN(param_bld, "n", n)) { +		OSSL_PARAM_BLD_free(param_bld); +		BN_free(n); +		BN_free(e); +		return NULL; +	} +	if(!OSSL_PARAM_BLD_push_BN(param_bld, "e", e)) { +		OSSL_PARAM_BLD_free(param_bld); +		BN_free(n); +		BN_free(e); +		return NULL; +	} +	params = OSSL_PARAM_BLD_to_param(param_bld); +	OSSL_PARAM_BLD_free(param_bld); + +	ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); +	if(!ctx) { +		OSSL_PARAM_free(params); +		BN_free(n); +		BN_free(e); +		return NULL; +	} +	if(EVP_PKEY_fromdata_init(ctx) <= 0) { +		EVP_PKEY_CTX_free(ctx); +		OSSL_PARAM_free(params); +		BN_free(n); +		BN_free(e); +		return NULL; +	} +	if(EVP_PKEY_fromdata(ctx, &evp_key, EVP_PKEY_PUBLIC_KEY, params) <= 0) { +		EVP_PKEY_CTX_free(ctx); +		OSSL_PARAM_free(params); +		BN_free(n); +		BN_free(e); +		return NULL; +	} + +	EVP_PKEY_CTX_free(ctx); +	OSSL_PARAM_free(params); +	BN_free(n); +	BN_free(e); +	return evp_key; +#else +	RSA* rsa; +	EVP_PKEY *evp_key = EVP_PKEY_new(); +	if(!evp_key) { +		return NULL; +	} +	rsa = sldns_key_buf2rsa_raw(key, len); +	if(!rsa) { +		EVP_PKEY_free(evp_key); +		return NULL; +	} +	if(EVP_PKEY_assign_RSA(evp_key, rsa) == 0) { +		RSA_free(rsa); +		EVP_PKEY_free(evp_key); +		return NULL; +	} +	return evp_key; +#endif +} + +#ifdef USE_GOST +EVP_PKEY* +sldns_gost2pkey_raw(unsigned char* key, size_t keylen) +{ +	/* prefix header for X509 encoding */ +	uint8_t asn[37] = { 0x30, 0x63, 0x30, 0x1c, 0x06, 0x06, 0x2a, 0x85,  +		0x03, 0x02, 0x02, 0x13, 0x30, 0x12, 0x06, 0x07, 0x2a, 0x85,  +		0x03, 0x02, 0x02, 0x23, 0x01, 0x06, 0x07, 0x2a, 0x85, 0x03,  +		0x02, 0x02, 0x1e, 0x01, 0x03, 0x43, 0x00, 0x04, 0x40}; +	unsigned char encoded[37+64]; +	const unsigned char* pp; +	if(keylen != 64) { +		/* key wrong size */ +		return NULL; +	} + +	/* create evp_key */ +	memmove(encoded, asn, 37); +	memmove(encoded+37, key, 64); +	pp = (unsigned char*)&encoded[0]; + +	return d2i_PUBKEY(NULL, &pp, (int)sizeof(encoded)); +} +#endif /* USE_GOST */ + +#ifdef USE_ECDSA +EVP_PKEY* +sldns_ecdsa2pkey_raw(unsigned char* key, size_t keylen, uint8_t algo) +{ +#ifdef HAVE_OSSL_PARAM_BLD_NEW +	unsigned char buf[256+2]; /* sufficient for 2*384/8+1 */ +	EVP_PKEY *evp_key = NULL; +	EVP_PKEY_CTX* ctx; +	OSSL_PARAM_BLD* param_bld; +	OSSL_PARAM* params = NULL; +	char* group = NULL; + +	/* check length, which uncompressed must be 2 bignums */ +	if(algo == LDNS_ECDSAP256SHA256) { +		if(keylen != 2*256/8) return NULL; +		group = "prime256v1"; +	} else if(algo == LDNS_ECDSAP384SHA384) { +		if(keylen != 2*384/8) return NULL; +		group = "P-384"; +	} else { +		return NULL; +	} +	if(keylen+1 > sizeof(buf)) { /* sanity check */ +		return NULL; +	} +	/* prepend the 0x04 for uncompressed format */ +	buf[0] = POINT_CONVERSION_UNCOMPRESSED; +	memmove(buf+1, key, keylen); + +	param_bld = OSSL_PARAM_BLD_new(); +	if(!param_bld) { +		return NULL; +	} +	if(!OSSL_PARAM_BLD_push_utf8_string(param_bld, "group", group, 0) || +	   !OSSL_PARAM_BLD_push_octet_string(param_bld, "pub", buf, keylen+1)) { +		OSSL_PARAM_BLD_free(param_bld); +		return NULL; +	} +	params = OSSL_PARAM_BLD_to_param(param_bld); +	OSSL_PARAM_BLD_free(param_bld); + +	ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); +	if(!ctx) { +		OSSL_PARAM_free(params); +		return NULL; +	} +	if(EVP_PKEY_fromdata_init(ctx) <= 0) { +		EVP_PKEY_CTX_free(ctx); +		OSSL_PARAM_free(params); +		return NULL; +	} +	if(EVP_PKEY_fromdata(ctx, &evp_key, EVP_PKEY_PUBLIC_KEY, params) <= 0) { +		EVP_PKEY_CTX_free(ctx); +		OSSL_PARAM_free(params); +		return NULL; +	} +	EVP_PKEY_CTX_free(ctx); +	OSSL_PARAM_free(params); +	return evp_key; +#else +	unsigned char buf[256+2]; /* sufficient for 2*384/8+1 */ +        const unsigned char* pp = buf; +        EVP_PKEY *evp_key; +        EC_KEY *ec; +	/* check length, which uncompressed must be 2 bignums */ +        if(algo == LDNS_ECDSAP256SHA256) { +		if(keylen != 2*256/8) return NULL; +                ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); +        } else if(algo == LDNS_ECDSAP384SHA384) { +		if(keylen != 2*384/8) return NULL; +                ec = EC_KEY_new_by_curve_name(NID_secp384r1); +        } else    ec = NULL; +        if(!ec) return NULL; +	if(keylen+1 > sizeof(buf)) { /* sanity check */ +                EC_KEY_free(ec); +		return NULL; +	} +	/* prepend the 0x02 (from docs) (or actually 0x04 from implementation +	 * of openssl) for uncompressed data */ +	buf[0] = POINT_CONVERSION_UNCOMPRESSED; +	memmove(buf+1, key, keylen); +        if(!o2i_ECPublicKey(&ec, &pp, (int)keylen+1)) { +                EC_KEY_free(ec); +                return NULL; +        } +        evp_key = EVP_PKEY_new(); +        if(!evp_key) { +                EC_KEY_free(ec); +                return NULL; +        } +        if (!EVP_PKEY_assign_EC_KEY(evp_key, ec)) { +		EVP_PKEY_free(evp_key); +		EC_KEY_free(ec); +		return NULL; +	} +        return evp_key; +#endif /* HAVE_OSSL_PARAM_BLD_NEW */ +} +#endif /* USE_ECDSA */ + +#ifdef USE_ED25519 +EVP_PKEY* +sldns_ed255192pkey_raw(const unsigned char* key, size_t keylen) +{ +	/* ASN1 for ED25519 is 302a300506032b6570032100 <32byteskey> */ +	uint8_t pre[] = {0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, +		0x70, 0x03, 0x21, 0x00}; +	int pre_len = 12; +	uint8_t buf[256]; +	EVP_PKEY *evp_key; +	/* pp gets modified by d2i() */ +	const unsigned char* pp = (unsigned char*)buf; +	if(keylen != 32 || keylen + pre_len > sizeof(buf)) +		return NULL; /* wrong length */ +	memmove(buf, pre, pre_len); +	memmove(buf+pre_len, key, keylen); +	evp_key = d2i_PUBKEY(NULL, &pp, (int)(pre_len+keylen)); +	return evp_key; +} +#endif /* USE_ED25519 */ + +#ifdef USE_ED448 +EVP_PKEY* +sldns_ed4482pkey_raw(const unsigned char* key, size_t keylen) +{ +	/* ASN1 for ED448 is 3043300506032b6571033a00 <57byteskey> */ +	uint8_t pre[] = {0x30, 0x43, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, +		0x71, 0x03, 0x3a, 0x00}; +        int pre_len = 12; +	uint8_t buf[256]; +        EVP_PKEY *evp_key; +	/* pp gets modified by d2i() */ +        const unsigned char* pp = (unsigned char*)buf; +	if(keylen != 57 || keylen + pre_len > sizeof(buf)) +		return NULL; /* wrong length */ +	memmove(buf, pre, pre_len); +	memmove(buf+pre_len, key, keylen); +	evp_key = d2i_PUBKEY(NULL, &pp, (int)(pre_len+keylen)); +        return evp_key; +} +#endif /* USE_ED448 */ + +int +sldns_digest_evp(unsigned char* data, unsigned int len, unsigned char* dest, +	const EVP_MD* md) +{ +	EVP_MD_CTX* ctx; +	ctx = EVP_MD_CTX_create(); +	if(!ctx) +		return 0; +	if(!EVP_DigestInit_ex(ctx, md, NULL) || +		!EVP_DigestUpdate(ctx, data, len) || +		!EVP_DigestFinal_ex(ctx, dest, NULL)) { +		EVP_MD_CTX_destroy(ctx); +		return 0; +	} +	EVP_MD_CTX_destroy(ctx); +	return 1; +} +#endif /* HAVE_SSL */ | 
