monero/external/unbound/validator/val_secalgo.c
Erik de Castro Lopo a85b5759f3 Upgrade unbound library
These files were pulled from the 1.6.3 release tarball.

This new version builds against OpenSSL version 1.1 which will be
the default in the new Debian Stable which is due to be released
RealSoonNow (tm).
2017-06-17 23:04:00 +10:00

1753 lines
45 KiB
C

/*
* validator/val_secalgo.c - validator security algorithm functions.
*
* Copyright (c) 2012, NLnet Labs. All rights reserved.
*
* This software is open source.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 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.
*
* Neither the name of the NLNET LABS 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 COPYRIGHT HOLDERS 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 COPYRIGHT
* HOLDER 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.
*/
/**
* \file
*
* This file contains helper functions for the validator module.
* These functions take raw data buffers, formatted for crypto verification,
* and do the library calls (for the crypto library in use).
*/
#include "config.h"
/* packed_rrset on top to define enum types (forced by c99 standard) */
#include "util/data/packed_rrset.h"
#include "validator/val_secalgo.h"
#include "validator/val_nsec3.h"
#include "util/log.h"
#include "sldns/rrdef.h"
#include "sldns/keyraw.h"
#include "sldns/sbuffer.h"
#if !defined(HAVE_SSL) && !defined(HAVE_NSS) && !defined(HAVE_NETTLE)
#error "Need crypto library to do digital signature cryptography"
#endif
/* OpenSSL implementation */
#ifdef HAVE_SSL
#ifdef HAVE_OPENSSL_ERR_H
#include <openssl/err.h>
#endif
#ifdef HAVE_OPENSSL_RAND_H
#include <openssl/rand.h>
#endif
#ifdef HAVE_OPENSSL_CONF_H
#include <openssl/conf.h>
#endif
#ifdef HAVE_OPENSSL_ENGINE_H
#include <openssl/engine.h>
#endif
/** fake DSA support for unit tests */
int fake_dsa = 0;
/** fake SHA1 support for unit tests */
int fake_sha1 = 0;
/* return size of digest if supported, or 0 otherwise */
size_t
nsec3_hash_algo_size_supported(int id)
{
switch(id) {
case NSEC3_HASH_SHA1:
return SHA_DIGEST_LENGTH;
default:
return 0;
}
}
/* perform nsec3 hash. return false on failure */
int
secalgo_nsec3_hash(int algo, unsigned char* buf, size_t len,
unsigned char* res)
{
switch(algo) {
case NSEC3_HASH_SHA1:
(void)SHA1(buf, len, res);
return 1;
default:
return 0;
}
}
void
secalgo_hash_sha256(unsigned char* buf, size_t len, unsigned char* res)
{
(void)SHA256(buf, len, res);
}
/**
* Return size of DS digest according to its hash algorithm.
* @param algo: DS digest algo.
* @return size in bytes of digest, or 0 if not supported.
*/
size_t
ds_digest_size_supported(int algo)
{
switch(algo) {
case LDNS_SHA1:
#if defined(HAVE_EVP_SHA1) && defined(USE_SHA1)
return SHA_DIGEST_LENGTH;
#else
if(fake_sha1) return 20;
return 0;
#endif
#ifdef HAVE_EVP_SHA256
case LDNS_SHA256:
return SHA256_DIGEST_LENGTH;
#endif
#ifdef USE_GOST
case LDNS_HASH_GOST:
/* we support GOST if it can be loaded */
(void)sldns_key_EVP_load_gost_id();
if(EVP_get_digestbyname("md_gost94"))
return 32;
else return 0;
#endif
#ifdef USE_ECDSA
case LDNS_SHA384:
return SHA384_DIGEST_LENGTH;
#endif
default: break;
}
return 0;
}
#ifdef USE_GOST
/** Perform GOST hash */
static int
do_gost94(unsigned char* data, size_t len, unsigned char* dest)
{
const EVP_MD* md = EVP_get_digestbyname("md_gost94");
if(!md)
return 0;
return sldns_digest_evp(data, (unsigned int)len, dest, md);
}
#endif
int
secalgo_ds_digest(int algo, unsigned char* buf, size_t len,
unsigned char* res)
{
switch(algo) {
#if defined(HAVE_EVP_SHA1) && defined(USE_SHA1)
case LDNS_SHA1:
(void)SHA1(buf, len, res);
return 1;
#endif
#ifdef HAVE_EVP_SHA256
case LDNS_SHA256:
(void)SHA256(buf, len, res);
return 1;
#endif
#ifdef USE_GOST
case LDNS_HASH_GOST:
if(do_gost94(buf, len, res))
return 1;
break;
#endif
#ifdef USE_ECDSA
case LDNS_SHA384:
(void)SHA384(buf, len, res);
return 1;
#endif
default:
verbose(VERB_QUERY, "unknown DS digest algorithm %d",
algo);
break;
}
return 0;
}
/** return true if DNSKEY algorithm id is supported */
int
dnskey_algo_id_is_supported(int id)
{
switch(id) {
case LDNS_RSAMD5:
/* RFC 6725 deprecates RSAMD5 */
return 0;
case LDNS_DSA:
case LDNS_DSA_NSEC3:
#if defined(USE_DSA) && defined(USE_SHA1)
return 1;
#else
if(fake_dsa || fake_sha1) return 1;
return 0;
#endif
case LDNS_RSASHA1:
case LDNS_RSASHA1_NSEC3:
#ifdef USE_SHA1
return 1;
#else
if(fake_sha1) return 1;
return 0;
#endif
#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2)
case LDNS_RSASHA256:
#endif
#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2)
case LDNS_RSASHA512:
#endif
#ifdef USE_ECDSA
case LDNS_ECDSAP256SHA256:
case LDNS_ECDSAP384SHA384:
#endif
#if (defined(HAVE_EVP_SHA256) && defined(USE_SHA2)) || (defined(HAVE_EVP_SHA512) && defined(USE_SHA2)) || defined(USE_ECDSA)
return 1;
#endif
#ifdef USE_GOST
case LDNS_ECC_GOST:
/* we support GOST if it can be loaded */
return sldns_key_EVP_load_gost_id();
#endif
default:
return 0;
}
}
/**
* Output a libcrypto openssl error to the logfile.
* @param str: string to add to it.
* @param e: the error to output, error number from ERR_get_error().
*/
static void
log_crypto_error(const char* str, unsigned long e)
{
char buf[128];
/* or use ERR_error_string if ERR_error_string_n is not avail TODO */
ERR_error_string_n(e, buf, sizeof(buf));
/* buf now contains */
/* error:[error code]:[library name]:[function name]:[reason string] */
log_err("%s crypto %s", str, buf);
}
#ifdef USE_DSA
/**
* Setup DSA key digest in DER encoding ...
* @param sig: input is signature output alloced ptr (unless failure).
* caller must free alloced ptr if this routine returns true.
* @param len: input is initial siglen, output is output len.
* @return false on failure.
*/
static int
setup_dsa_sig(unsigned char** sig, unsigned int* len)
{
unsigned char* orig = *sig;
unsigned int origlen = *len;
int newlen;
BIGNUM *R, *S;
DSA_SIG *dsasig;
/* extract the R and S field from the sig buffer */
if(origlen < 1 + 2*SHA_DIGEST_LENGTH)
return 0;
R = BN_new();
if(!R) return 0;
(void) BN_bin2bn(orig + 1, SHA_DIGEST_LENGTH, R);
S = BN_new();
if(!S) return 0;
(void) BN_bin2bn(orig + 21, SHA_DIGEST_LENGTH, S);
dsasig = DSA_SIG_new();
if(!dsasig) return 0;
#ifdef HAVE_DSA_SIG_SET0
if(!DSA_SIG_set0(dsasig, R, S)) return 0;
#else
dsasig->r = R;
dsasig->s = S;
#endif
*sig = NULL;
newlen = i2d_DSA_SIG(dsasig, sig);
if(newlen < 0) {
DSA_SIG_free(dsasig);
free(*sig);
return 0;
}
*len = (unsigned int)newlen;
DSA_SIG_free(dsasig);
return 1;
}
#endif /* USE_DSA */
#ifdef USE_ECDSA
/**
* Setup the ECDSA signature in its encoding that the library wants.
* Converts from plain numbers to ASN formatted.
* @param sig: input is signature, output alloced ptr (unless failure).
* caller must free alloced ptr if this routine returns true.
* @param len: input is initial siglen, output is output len.
* @return false on failure.
*/
static int
setup_ecdsa_sig(unsigned char** sig, unsigned int* len)
{
/* convert from two BIGNUMs in the rdata buffer, to ASN notation.
* ASN preable: 30440220 <R 32bytefor256> 0220 <S 32bytefor256>
* the '20' is the length of that field (=bnsize).
i * the '44' is the total remaining length.
* if negative, start with leading zero.
* if starts with 00s, remove them from the number.
*/
uint8_t pre[] = {0x30, 0x44, 0x02, 0x20};
int pre_len = 4;
uint8_t mid[] = {0x02, 0x20};
int mid_len = 2;
int raw_sig_len, r_high, s_high, r_rem=0, s_rem=0;
int bnsize = (int)((*len)/2);
unsigned char* d = *sig;
uint8_t* p;
/* if too short or not even length, fails */
if(*len < 16 || bnsize*2 != (int)*len)
return 0;
/* strip leading zeroes from r (but not last one) */
while(r_rem < bnsize-1 && d[r_rem] == 0)
r_rem++;
/* strip leading zeroes from s (but not last one) */
while(s_rem < bnsize-1 && d[bnsize+s_rem] == 0)
s_rem++;
r_high = ((d[0+r_rem]&0x80)?1:0);
s_high = ((d[bnsize+s_rem]&0x80)?1:0);
raw_sig_len = pre_len + r_high + bnsize - r_rem + mid_len +
s_high + bnsize - s_rem;
*sig = (unsigned char*)malloc((size_t)raw_sig_len);
if(!*sig)
return 0;
p = (uint8_t*)*sig;
p[0] = pre[0];
p[1] = (uint8_t)(raw_sig_len-2);
p[2] = pre[2];
p[3] = (uint8_t)(bnsize + r_high - r_rem);
p += 4;
if(r_high) {
*p = 0;
p += 1;
}
memmove(p, d+r_rem, (size_t)bnsize-r_rem);
p += bnsize-r_rem;
memmove(p, mid, (size_t)mid_len-1);
p += mid_len-1;
*p = (uint8_t)(bnsize + s_high - s_rem);
p += 1;
if(s_high) {
*p = 0;
p += 1;
}
memmove(p, d+bnsize+s_rem, (size_t)bnsize-s_rem);
*len = (unsigned int)raw_sig_len;
return 1;
}
#endif /* USE_ECDSA */
#ifdef USE_ECDSA_EVP_WORKAROUND
static EVP_MD ecdsa_evp_256_md;
static EVP_MD ecdsa_evp_384_md;
void ecdsa_evp_workaround_init(void)
{
/* openssl before 1.0.0 fixes RSA with the SHA256
* hash in EVP. We create one for ecdsa_sha256 */
ecdsa_evp_256_md = *EVP_sha256();
ecdsa_evp_256_md.required_pkey_type[0] = EVP_PKEY_EC;
ecdsa_evp_256_md.verify = (void*)ECDSA_verify;
ecdsa_evp_384_md = *EVP_sha384();
ecdsa_evp_384_md.required_pkey_type[0] = EVP_PKEY_EC;
ecdsa_evp_384_md.verify = (void*)ECDSA_verify;
}
#endif /* USE_ECDSA_EVP_WORKAROUND */
/**
* Setup key and digest for verification. Adjust sig if necessary.
*
* @param algo: key algorithm
* @param evp_key: EVP PKEY public key to create.
* @param digest_type: digest type to use
* @param key: key to setup for.
* @param keylen: length of key.
* @return false on failure.
*/
static int
setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type,
unsigned char* key, size_t keylen)
{
#if defined(USE_DSA) && defined(USE_SHA1)
DSA* dsa;
#endif
RSA* rsa;
switch(algo) {
#if defined(USE_DSA) && defined(USE_SHA1)
case LDNS_DSA:
case LDNS_DSA_NSEC3:
*evp_key = EVP_PKEY_new();
if(!*evp_key) {
log_err("verify: malloc failure in crypto");
return 0;
}
dsa = sldns_key_buf2dsa_raw(key, keylen);
if(!dsa) {
verbose(VERB_QUERY, "verify: "
"sldns_key_buf2dsa_raw failed");
return 0;
}
if(EVP_PKEY_assign_DSA(*evp_key, dsa) == 0) {
verbose(VERB_QUERY, "verify: "
"EVP_PKEY_assign_DSA failed");
return 0;
}
#ifdef HAVE_EVP_DSS1
*digest_type = EVP_dss1();
#else
*digest_type = EVP_sha1();
#endif
break;
#endif /* USE_DSA && USE_SHA1 */
#if defined(USE_SHA1) || (defined(HAVE_EVP_SHA256) && defined(USE_SHA2)) || (defined(HAVE_EVP_SHA512) && defined(USE_SHA2))
#ifdef USE_SHA1
case LDNS_RSASHA1:
case LDNS_RSASHA1_NSEC3:
#endif
#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2)
case LDNS_RSASHA256:
#endif
#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2)
case LDNS_RSASHA512:
#endif
*evp_key = EVP_PKEY_new();
if(!*evp_key) {
log_err("verify: malloc failure in crypto");
return 0;
}
rsa = sldns_key_buf2rsa_raw(key, keylen);
if(!rsa) {
verbose(VERB_QUERY, "verify: "
"sldns_key_buf2rsa_raw SHA failed");
return 0;
}
if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) {
verbose(VERB_QUERY, "verify: "
"EVP_PKEY_assign_RSA SHA failed");
return 0;
}
/* select SHA version */
#if defined(HAVE_EVP_SHA256) && defined(USE_SHA2)
if(algo == LDNS_RSASHA256)
*digest_type = EVP_sha256();
else
#endif
#if defined(HAVE_EVP_SHA512) && defined(USE_SHA2)
if(algo == LDNS_RSASHA512)
*digest_type = EVP_sha512();
else
#endif
#ifdef USE_SHA1
*digest_type = EVP_sha1();
#else
{ verbose(VERB_QUERY, "no digest available"); return 0; }
#endif
break;
#endif /* defined(USE_SHA1) || (defined(HAVE_EVP_SHA256) && defined(USE_SHA2)) || (defined(HAVE_EVP_SHA512) && defined(USE_SHA2)) */
case LDNS_RSAMD5:
*evp_key = EVP_PKEY_new();
if(!*evp_key) {
log_err("verify: malloc failure in crypto");
return 0;
}
rsa = sldns_key_buf2rsa_raw(key, keylen);
if(!rsa) {
verbose(VERB_QUERY, "verify: "
"sldns_key_buf2rsa_raw MD5 failed");
return 0;
}
if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) {
verbose(VERB_QUERY, "verify: "
"EVP_PKEY_assign_RSA MD5 failed");
return 0;
}
*digest_type = EVP_md5();
break;
#ifdef USE_GOST
case LDNS_ECC_GOST:
*evp_key = sldns_gost2pkey_raw(key, keylen);
if(!*evp_key) {
verbose(VERB_QUERY, "verify: "
"sldns_gost2pkey_raw failed");
return 0;
}
*digest_type = EVP_get_digestbyname("md_gost94");
if(!*digest_type) {
verbose(VERB_QUERY, "verify: "
"EVP_getdigest md_gost94 failed");
return 0;
}
break;
#endif
#ifdef USE_ECDSA
case LDNS_ECDSAP256SHA256:
*evp_key = sldns_ecdsa2pkey_raw(key, keylen,
LDNS_ECDSAP256SHA256);
if(!*evp_key) {
verbose(VERB_QUERY, "verify: "
"sldns_ecdsa2pkey_raw failed");
return 0;
}
#ifdef USE_ECDSA_EVP_WORKAROUND
*digest_type = &ecdsa_evp_256_md;
#else
*digest_type = EVP_sha256();
#endif
break;
case LDNS_ECDSAP384SHA384:
*evp_key = sldns_ecdsa2pkey_raw(key, keylen,
LDNS_ECDSAP384SHA384);
if(!*evp_key) {
verbose(VERB_QUERY, "verify: "
"sldns_ecdsa2pkey_raw failed");
return 0;
}
#ifdef USE_ECDSA_EVP_WORKAROUND
*digest_type = &ecdsa_evp_384_md;
#else
*digest_type = EVP_sha384();
#endif
break;
#endif /* USE_ECDSA */
default:
verbose(VERB_QUERY, "verify: unknown algorithm %d",
algo);
return 0;
}
return 1;
}
/**
* Check a canonical sig+rrset and signature against a dnskey
* @param buf: buffer with data to verify, the first rrsig part and the
* canonicalized rrset.
* @param algo: DNSKEY algorithm.
* @param sigblock: signature rdata field from RRSIG
* @param sigblock_len: length of sigblock data.
* @param key: public key data from DNSKEY RR.
* @param keylen: length of keydata.
* @param reason: bogus reason in more detail.
* @return secure if verification succeeded, bogus on crypto failure,
* unchecked on format errors and alloc failures.
*/
enum sec_status
verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock,
unsigned int sigblock_len, unsigned char* key, unsigned int keylen,
char** reason)
{
const EVP_MD *digest_type;
EVP_MD_CTX* ctx;
int res, dofree = 0, docrypto_free = 0;
EVP_PKEY *evp_key = NULL;
#ifndef USE_DSA
if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3) &&(fake_dsa||fake_sha1))
return sec_status_secure;
#endif
#ifndef USE_SHA1
if(fake_sha1 && (algo == LDNS_DSA || algo == LDNS_DSA_NSEC3 || algo == LDNS_RSASHA1 || algo == LDNS_RSASHA1_NSEC3))
return sec_status_secure;
#endif
if(!setup_key_digest(algo, &evp_key, &digest_type, key, keylen)) {
verbose(VERB_QUERY, "verify: failed to setup key");
*reason = "use of key for crypto failed";
EVP_PKEY_free(evp_key);
return sec_status_bogus;
}
#ifdef USE_DSA
/* if it is a DSA signature in bind format, convert to DER format */
if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3) &&
sigblock_len == 1+2*SHA_DIGEST_LENGTH) {
if(!setup_dsa_sig(&sigblock, &sigblock_len)) {
verbose(VERB_QUERY, "verify: failed to setup DSA sig");
*reason = "use of key for DSA crypto failed";
EVP_PKEY_free(evp_key);
return sec_status_bogus;
}
docrypto_free = 1;
}
#endif
#if defined(USE_ECDSA) && defined(USE_DSA)
else
#endif
#ifdef USE_ECDSA
if(algo == LDNS_ECDSAP256SHA256 || algo == LDNS_ECDSAP384SHA384) {
/* EVP uses ASN prefix on sig, which is not in the wire data */
if(!setup_ecdsa_sig(&sigblock, &sigblock_len)) {
verbose(VERB_QUERY, "verify: failed to setup ECDSA sig");
*reason = "use of signature for ECDSA crypto failed";
EVP_PKEY_free(evp_key);
return sec_status_bogus;
}
dofree = 1;
}
#endif /* USE_ECDSA */
/* do the signature cryptography work */
#ifdef HAVE_EVP_MD_CTX_NEW
ctx = EVP_MD_CTX_new();
#else
ctx = (EVP_MD_CTX*)malloc(sizeof(*ctx));
if(ctx) EVP_MD_CTX_init(ctx);
#endif
if(!ctx) {
log_err("EVP_MD_CTX_new: malloc failure");
EVP_PKEY_free(evp_key);
if(dofree) free(sigblock);
else if(docrypto_free) OPENSSL_free(sigblock);
return sec_status_unchecked;
}
if(EVP_VerifyInit(ctx, digest_type) == 0) {
verbose(VERB_QUERY, "verify: EVP_VerifyInit failed");
EVP_MD_CTX_destroy(ctx);
EVP_PKEY_free(evp_key);
if(dofree) free(sigblock);
else if(docrypto_free) OPENSSL_free(sigblock);
return sec_status_unchecked;
}
if(EVP_VerifyUpdate(ctx, (unsigned char*)sldns_buffer_begin(buf),
(unsigned int)sldns_buffer_limit(buf)) == 0) {
verbose(VERB_QUERY, "verify: EVP_VerifyUpdate failed");
EVP_MD_CTX_destroy(ctx);
EVP_PKEY_free(evp_key);
if(dofree) free(sigblock);
else if(docrypto_free) OPENSSL_free(sigblock);
return sec_status_unchecked;
}
res = EVP_VerifyFinal(ctx, sigblock, sigblock_len, evp_key);
#ifdef HAVE_EVP_MD_CTX_NEW
EVP_MD_CTX_destroy(ctx);
#else
EVP_MD_CTX_cleanup(ctx);
free(ctx);
#endif
EVP_PKEY_free(evp_key);
if(dofree) free(sigblock);
else if(docrypto_free) OPENSSL_free(sigblock);
if(res == 1) {
return sec_status_secure;
} else if(res == 0) {
verbose(VERB_QUERY, "verify: signature mismatch");
*reason = "signature crypto failed";
return sec_status_bogus;
}
log_crypto_error("verify:", ERR_get_error());
return sec_status_unchecked;
}
/**************************************************/
#elif defined(HAVE_NSS)
/* libnss implementation */
/* nss3 */
#include "sechash.h"
#include "pk11pub.h"
#include "keyhi.h"
#include "secerr.h"
#include "cryptohi.h"
/* nspr4 */
#include "prerror.h"
/* return size of digest if supported, or 0 otherwise */
size_t
nsec3_hash_algo_size_supported(int id)
{
switch(id) {
case NSEC3_HASH_SHA1:
return SHA1_LENGTH;
default:
return 0;
}
}
/* perform nsec3 hash. return false on failure */
int
secalgo_nsec3_hash(int algo, unsigned char* buf, size_t len,
unsigned char* res)
{
switch(algo) {
case NSEC3_HASH_SHA1:
(void)HASH_HashBuf(HASH_AlgSHA1, res, buf, (unsigned long)len);
return 1;
default:
return 0;
}
}
void
secalgo_hash_sha256(unsigned char* buf, size_t len, unsigned char* res)
{
(void)HASH_HashBuf(HASH_AlgSHA256, res, buf, (unsigned long)len);
}
size_t
ds_digest_size_supported(int algo)
{
/* uses libNSS */
switch(algo) {
#ifdef USE_SHA1
case LDNS_SHA1:
return SHA1_LENGTH;
#endif
#ifdef USE_SHA2
case LDNS_SHA256:
return SHA256_LENGTH;
#endif
#ifdef USE_ECDSA
case LDNS_SHA384:
return SHA384_LENGTH;
#endif
/* GOST not supported in NSS */
case LDNS_HASH_GOST:
default: break;
}
return 0;
}
int
secalgo_ds_digest(int algo, unsigned char* buf, size_t len,
unsigned char* res)
{
/* uses libNSS */
switch(algo) {
#ifdef USE_SHA1
case LDNS_SHA1:
return HASH_HashBuf(HASH_AlgSHA1, res, buf, len)
== SECSuccess;
#endif
#if defined(USE_SHA2)
case LDNS_SHA256:
return HASH_HashBuf(HASH_AlgSHA256, res, buf, len)
== SECSuccess;
#endif
#ifdef USE_ECDSA
case LDNS_SHA384:
return HASH_HashBuf(HASH_AlgSHA384, res, buf, len)
== SECSuccess;
#endif
case LDNS_HASH_GOST:
default:
verbose(VERB_QUERY, "unknown DS digest algorithm %d",
algo);
break;
}
return 0;
}
int
dnskey_algo_id_is_supported(int id)
{
/* uses libNSS */
switch(id) {
case LDNS_RSAMD5:
/* RFC 6725 deprecates RSAMD5 */
return 0;
#if defined(USE_SHA1) || defined(USE_SHA2)
#if defined(USE_DSA) && defined(USE_SHA1)
case LDNS_DSA:
case LDNS_DSA_NSEC3:
#endif
#ifdef USE_SHA1
case LDNS_RSASHA1:
case LDNS_RSASHA1_NSEC3:
#endif
#ifdef USE_SHA2
case LDNS_RSASHA256:
#endif
#ifdef USE_SHA2
case LDNS_RSASHA512:
#endif
return 1;
#endif /* SHA1 or SHA2 */
#ifdef USE_ECDSA
case LDNS_ECDSAP256SHA256:
case LDNS_ECDSAP384SHA384:
return PK11_TokenExists(CKM_ECDSA);
#endif
case LDNS_ECC_GOST:
default:
return 0;
}
}
/* return a new public key for NSS */
static SECKEYPublicKey* nss_key_create(KeyType ktype)
{
SECKEYPublicKey* key;
PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if(!arena) {
log_err("out of memory, PORT_NewArena failed");
return NULL;
}
key = PORT_ArenaZNew(arena, SECKEYPublicKey);
if(!key) {
log_err("out of memory, PORT_ArenaZNew failed");
PORT_FreeArena(arena, PR_FALSE);
return NULL;
}
key->arena = arena;
key->keyType = ktype;
key->pkcs11Slot = NULL;
key->pkcs11ID = CK_INVALID_HANDLE;
return key;
}
static SECKEYPublicKey* nss_buf2ecdsa(unsigned char* key, size_t len, int algo)
{
SECKEYPublicKey* pk;
SECItem pub = {siBuffer, NULL, 0};
SECItem params = {siBuffer, NULL, 0};
static unsigned char param256[] = {
/* OBJECTIDENTIFIER 1.2.840.10045.3.1.7 (P-256)
* {iso(1) member-body(2) us(840) ansi-x962(10045) curves(3) prime(1) prime256v1(7)} */
0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07
};
static unsigned char param384[] = {
/* OBJECTIDENTIFIER 1.3.132.0.34 (P-384)
* {iso(1) identified-organization(3) certicom(132) curve(0) ansip384r1(34)} */
0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22
};
unsigned char buf[256+2]; /* sufficient for 2*384/8+1 */
/* check length, which uncompressed must be 2 bignums */
if(algo == LDNS_ECDSAP256SHA256) {
if(len != 2*256/8) return NULL;
/* ECCurve_X9_62_PRIME_256V1 */
} else if(algo == LDNS_ECDSAP384SHA384) {
if(len != 2*384/8) return NULL;
/* ECCurve_X9_62_PRIME_384R1 */
} else return NULL;
buf[0] = 0x04; /* POINT_FORM_UNCOMPRESSED */
memmove(buf+1, key, len);
pub.data = buf;
pub.len = len+1;
if(algo == LDNS_ECDSAP256SHA256) {
params.data = param256;
params.len = sizeof(param256);
} else {
params.data = param384;
params.len = sizeof(param384);
}
pk = nss_key_create(ecKey);
if(!pk)
return NULL;
pk->u.ec.size = (len/2)*8;
if(SECITEM_CopyItem(pk->arena, &pk->u.ec.publicValue, &pub)) {
SECKEY_DestroyPublicKey(pk);
return NULL;
}
if(SECITEM_CopyItem(pk->arena, &pk->u.ec.DEREncodedParams, &params)) {
SECKEY_DestroyPublicKey(pk);
return NULL;
}
return pk;
}
static SECKEYPublicKey* nss_buf2dsa(unsigned char* key, size_t len)
{
SECKEYPublicKey* pk;
uint8_t T;
uint16_t length;
uint16_t offset;
SECItem Q = {siBuffer, NULL, 0};
SECItem P = {siBuffer, NULL, 0};
SECItem G = {siBuffer, NULL, 0};
SECItem Y = {siBuffer, NULL, 0};
if(len == 0)
return NULL;
T = (uint8_t)key[0];
length = (64 + T * 8);
offset = 1;
if (T > 8) {
return NULL;
}
if(len < (size_t)1 + SHA1_LENGTH + 3*length)
return NULL;
Q.data = key+offset;
Q.len = SHA1_LENGTH;
offset += SHA1_LENGTH;
P.data = key+offset;
P.len = length;
offset += length;
G.data = key+offset;
G.len = length;
offset += length;
Y.data = key+offset;
Y.len = length;
offset += length;
pk = nss_key_create(dsaKey);
if(!pk)
return NULL;
if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.params.prime, &P)) {
SECKEY_DestroyPublicKey(pk);
return NULL;
}
if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.params.subPrime, &Q)) {
SECKEY_DestroyPublicKey(pk);
return NULL;
}
if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.params.base, &G)) {
SECKEY_DestroyPublicKey(pk);
return NULL;
}
if(SECITEM_CopyItem(pk->arena, &pk->u.dsa.publicValue, &Y)) {
SECKEY_DestroyPublicKey(pk);
return NULL;
}
return pk;
}
static SECKEYPublicKey* nss_buf2rsa(unsigned char* key, size_t len)
{
SECKEYPublicKey* pk;
uint16_t exp;
uint16_t offset;
uint16_t int16;
SECItem modulus = {siBuffer, NULL, 0};
SECItem exponent = {siBuffer, NULL, 0};
if(len == 0)
return NULL;
if(key[0] == 0) {
if(len < 3)
return NULL;
/* the exponent is too large so it's places further */
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 NULL;
exponent.data = key+offset;
exponent.len = exp;
offset += exp;
modulus.data = key+offset;
modulus.len = (len - offset);
pk = nss_key_create(rsaKey);
if(!pk)
return NULL;
if(SECITEM_CopyItem(pk->arena, &pk->u.rsa.modulus, &modulus)) {
SECKEY_DestroyPublicKey(pk);
return NULL;
}
if(SECITEM_CopyItem(pk->arena, &pk->u.rsa.publicExponent, &exponent)) {
SECKEY_DestroyPublicKey(pk);
return NULL;
}
return pk;
}
/**
* Setup key and digest for verification. Adjust sig if necessary.
*
* @param algo: key algorithm
* @param evp_key: EVP PKEY public key to create.
* @param digest_type: digest type to use
* @param key: key to setup for.
* @param keylen: length of key.
* @param prefix: if returned, the ASN prefix for the hashblob.
* @param prefixlen: length of the prefix.
* @return false on failure.
*/
static int
nss_setup_key_digest(int algo, SECKEYPublicKey** pubkey, HASH_HashType* htype,
unsigned char* key, size_t keylen, unsigned char** prefix,
size_t* prefixlen)
{
/* uses libNSS */
/* hash prefix for md5, RFC2537 */
static unsigned char p_md5[] = {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a,
0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10};
/* hash prefix to prepend to hash output, from RFC3110 */
static unsigned char p_sha1[] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B,
0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14};
/* from RFC5702 */
static unsigned char p_sha256[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60,
0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
static unsigned char p_sha512[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60,
0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40};
/* from RFC6234 */
/* for future RSASHA384 ..
static unsigned char p_sha384[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60,
0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30};
*/
switch(algo) {
#if defined(USE_SHA1) || defined(USE_SHA2)
#if defined(USE_DSA) && defined(USE_SHA1)
case LDNS_DSA:
case LDNS_DSA_NSEC3:
*pubkey = nss_buf2dsa(key, keylen);
if(!*pubkey) {
log_err("verify: malloc failure in crypto");
return 0;
}
*htype = HASH_AlgSHA1;
/* no prefix for DSA verification */
break;
#endif
#ifdef USE_SHA1
case LDNS_RSASHA1:
case LDNS_RSASHA1_NSEC3:
#endif
#ifdef USE_SHA2
case LDNS_RSASHA256:
#endif
#ifdef USE_SHA2
case LDNS_RSASHA512:
#endif
*pubkey = nss_buf2rsa(key, keylen);
if(!*pubkey) {
log_err("verify: malloc failure in crypto");
return 0;
}
/* select SHA version */
#ifdef USE_SHA2
if(algo == LDNS_RSASHA256) {
*htype = HASH_AlgSHA256;
*prefix = p_sha256;
*prefixlen = sizeof(p_sha256);
} else
#endif
#ifdef USE_SHA2
if(algo == LDNS_RSASHA512) {
*htype = HASH_AlgSHA512;
*prefix = p_sha512;
*prefixlen = sizeof(p_sha512);
} else
#endif
#ifdef USE_SHA1
{
*htype = HASH_AlgSHA1;
*prefix = p_sha1;
*prefixlen = sizeof(p_sha1);
}
#else
{
verbose(VERB_QUERY, "verify: no digest algo");
return 0;
}
#endif
break;
#endif /* SHA1 or SHA2 */
case LDNS_RSAMD5:
*pubkey = nss_buf2rsa(key, keylen);
if(!*pubkey) {
log_err("verify: malloc failure in crypto");
return 0;
}
*htype = HASH_AlgMD5;
*prefix = p_md5;
*prefixlen = sizeof(p_md5);
break;
#ifdef USE_ECDSA
case LDNS_ECDSAP256SHA256:
*pubkey = nss_buf2ecdsa(key, keylen,
LDNS_ECDSAP256SHA256);
if(!*pubkey) {
log_err("verify: malloc failure in crypto");
return 0;
}
*htype = HASH_AlgSHA256;
/* no prefix for DSA verification */
break;
case LDNS_ECDSAP384SHA384:
*pubkey = nss_buf2ecdsa(key, keylen,
LDNS_ECDSAP384SHA384);
if(!*pubkey) {
log_err("verify: malloc failure in crypto");
return 0;
}
*htype = HASH_AlgSHA384;
/* no prefix for DSA verification */
break;
#endif /* USE_ECDSA */
case LDNS_ECC_GOST:
default:
verbose(VERB_QUERY, "verify: unknown algorithm %d",
algo);
return 0;
}
return 1;
}
/**
* Check a canonical sig+rrset and signature against a dnskey
* @param buf: buffer with data to verify, the first rrsig part and the
* canonicalized rrset.
* @param algo: DNSKEY algorithm.
* @param sigblock: signature rdata field from RRSIG
* @param sigblock_len: length of sigblock data.
* @param key: public key data from DNSKEY RR.
* @param keylen: length of keydata.
* @param reason: bogus reason in more detail.
* @return secure if verification succeeded, bogus on crypto failure,
* unchecked on format errors and alloc failures.
*/
enum sec_status
verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock,
unsigned int sigblock_len, unsigned char* key, unsigned int keylen,
char** reason)
{
/* uses libNSS */
/* large enough for the different hashes */
unsigned char hash[HASH_LENGTH_MAX];
unsigned char hash2[HASH_LENGTH_MAX*2];
HASH_HashType htype = 0;
SECKEYPublicKey* pubkey = NULL;
SECItem secsig = {siBuffer, sigblock, sigblock_len};
SECItem sechash = {siBuffer, hash, 0};
SECStatus res;
unsigned char* prefix = NULL; /* prefix for hash, RFC3110, RFC5702 */
size_t prefixlen = 0;
int err;
if(!nss_setup_key_digest(algo, &pubkey, &htype, key, keylen,
&prefix, &prefixlen)) {
verbose(VERB_QUERY, "verify: failed to setup key");
*reason = "use of key for crypto failed";
SECKEY_DestroyPublicKey(pubkey);
return sec_status_bogus;
}
#if defined(USE_DSA) && defined(USE_SHA1)
/* need to convert DSA, ECDSA signatures? */
if((algo == LDNS_DSA || algo == LDNS_DSA_NSEC3)) {
if(sigblock_len == 1+2*SHA1_LENGTH) {
secsig.data ++;
secsig.len --;
} else {
SECItem* p = DSAU_DecodeDerSig(&secsig);
if(!p) {
verbose(VERB_QUERY, "verify: failed DER decode");
*reason = "signature DER decode failed";
SECKEY_DestroyPublicKey(pubkey);
return sec_status_bogus;
}
if(SECITEM_CopyItem(pubkey->arena, &secsig, p)) {
log_err("alloc failure in DER decode");
SECKEY_DestroyPublicKey(pubkey);
SECITEM_FreeItem(p, PR_TRUE);
return sec_status_unchecked;
}
SECITEM_FreeItem(p, PR_TRUE);
}
}
#endif /* USE_DSA */
/* do the signature cryptography work */
/* hash the data */
sechash.len = HASH_ResultLen(htype);
if(sechash.len > sizeof(hash)) {
verbose(VERB_QUERY, "verify: hash too large for buffer");
SECKEY_DestroyPublicKey(pubkey);
return sec_status_unchecked;
}
if(HASH_HashBuf(htype, hash, (unsigned char*)sldns_buffer_begin(buf),
(unsigned int)sldns_buffer_limit(buf)) != SECSuccess) {
verbose(VERB_QUERY, "verify: HASH_HashBuf failed");
SECKEY_DestroyPublicKey(pubkey);
return sec_status_unchecked;
}
if(prefix) {
int hashlen = sechash.len;
if(prefixlen+hashlen > sizeof(hash2)) {
verbose(VERB_QUERY, "verify: hashprefix too large");
SECKEY_DestroyPublicKey(pubkey);
return sec_status_unchecked;
}
sechash.data = hash2;
sechash.len = prefixlen+hashlen;
memcpy(sechash.data, prefix, prefixlen);
memmove(sechash.data+prefixlen, hash, hashlen);
}
/* verify the signature */
res = PK11_Verify(pubkey, &secsig, &sechash, NULL /*wincx*/);
SECKEY_DestroyPublicKey(pubkey);
if(res == SECSuccess) {
return sec_status_secure;
}
err = PORT_GetError();
if(err != SEC_ERROR_BAD_SIGNATURE) {
/* failed to verify */
verbose(VERB_QUERY, "verify: PK11_Verify failed: %s",
PORT_ErrorToString(err));
/* if it is not supported, like ECC is removed, we get,
* SEC_ERROR_NO_MODULE */
if(err == SEC_ERROR_NO_MODULE)
return sec_status_unchecked;
/* but other errors are commonly returned
* for a bad signature from NSS. Thus we return bogus,
* not unchecked */
*reason = "signature crypto failed";
return sec_status_bogus;
}
verbose(VERB_QUERY, "verify: signature mismatch: %s",
PORT_ErrorToString(err));
*reason = "signature crypto failed";
return sec_status_bogus;
}
#elif defined(HAVE_NETTLE)
#include "sha.h"
#include "bignum.h"
#include "macros.h"
#include "rsa.h"
#include "dsa.h"
#ifdef HAVE_NETTLE_DSA_COMPAT_H
#include "dsa-compat.h"
#endif
#include "asn1.h"
#ifdef USE_ECDSA
#include "ecdsa.h"
#include "ecc-curve.h"
#endif
static int
_digest_nettle(int algo, uint8_t* buf, size_t len,
unsigned char* res)
{
switch(algo) {
case SHA1_DIGEST_SIZE:
{
struct sha1_ctx ctx;
sha1_init(&ctx);
sha1_update(&ctx, len, buf);
sha1_digest(&ctx, SHA1_DIGEST_SIZE, res);
return 1;
}
case SHA256_DIGEST_SIZE:
{
struct sha256_ctx ctx;
sha256_init(&ctx);
sha256_update(&ctx, len, buf);
sha256_digest(&ctx, SHA256_DIGEST_SIZE, res);
return 1;
}
case SHA384_DIGEST_SIZE:
{
struct sha384_ctx ctx;
sha384_init(&ctx);
sha384_update(&ctx, len, buf);
sha384_digest(&ctx, SHA384_DIGEST_SIZE, res);
return 1;
}
case SHA512_DIGEST_SIZE:
{
struct sha512_ctx ctx;
sha512_init(&ctx);
sha512_update(&ctx, len, buf);
sha512_digest(&ctx, SHA512_DIGEST_SIZE, res);
return 1;
}
default:
break;
}
return 0;
}
/* return size of digest if supported, or 0 otherwise */
size_t
nsec3_hash_algo_size_supported(int id)
{
switch(id) {
case NSEC3_HASH_SHA1:
return SHA1_DIGEST_SIZE;
default:
return 0;
}
}
/* perform nsec3 hash. return false on failure */
int
secalgo_nsec3_hash(int algo, unsigned char* buf, size_t len,
unsigned char* res)
{
switch(algo) {
case NSEC3_HASH_SHA1:
return _digest_nettle(SHA1_DIGEST_SIZE, (uint8_t*)buf, len,
res);
default:
return 0;
}
}
void
secalgo_hash_sha256(unsigned char* buf, size_t len, unsigned char* res)
{
_digest_nettle(SHA256_DIGEST_SIZE, (uint8_t*)buf, len, res);
}
/**
* Return size of DS digest according to its hash algorithm.
* @param algo: DS digest algo.
* @return size in bytes of digest, or 0 if not supported.
*/
size_t
ds_digest_size_supported(int algo)
{
switch(algo) {
case LDNS_SHA1:
#ifdef USE_SHA1
return SHA1_DIGEST_SIZE;
#else
if(fake_sha1) return 20;
return 0;
#endif
#ifdef USE_SHA2
case LDNS_SHA256:
return SHA256_DIGEST_SIZE;
#endif
#ifdef USE_ECDSA
case LDNS_SHA384:
return SHA384_DIGEST_SIZE;
#endif
/* GOST not supported */
case LDNS_HASH_GOST:
default:
break;
}
return 0;
}
int
secalgo_ds_digest(int algo, unsigned char* buf, size_t len,
unsigned char* res)
{
switch(algo) {
#ifdef USE_SHA1
case LDNS_SHA1:
return _digest_nettle(SHA1_DIGEST_SIZE, buf, len, res);
#endif
#if defined(USE_SHA2)
case LDNS_SHA256:
return _digest_nettle(SHA256_DIGEST_SIZE, buf, len, res);
#endif
#ifdef USE_ECDSA
case LDNS_SHA384:
return _digest_nettle(SHA384_DIGEST_SIZE, buf, len, res);
#endif
case LDNS_HASH_GOST:
default:
verbose(VERB_QUERY, "unknown DS digest algorithm %d",
algo);
break;
}
return 0;
}
int
dnskey_algo_id_is_supported(int id)
{
/* uses libnettle */
switch(id) {
#if defined(USE_DSA) && defined(USE_SHA1)
case LDNS_DSA:
case LDNS_DSA_NSEC3:
#endif
#ifdef USE_SHA1
case LDNS_RSASHA1:
case LDNS_RSASHA1_NSEC3:
#endif
#ifdef USE_SHA2
case LDNS_RSASHA256:
case LDNS_RSASHA512:
#endif
#ifdef USE_ECDSA
case LDNS_ECDSAP256SHA256:
case LDNS_ECDSAP384SHA384:
#endif
return 1;
case LDNS_RSAMD5: /* RFC 6725 deprecates RSAMD5 */
case LDNS_ECC_GOST:
default:
return 0;
}
}
#if defined(USE_DSA) && defined(USE_SHA1)
static char *
_verify_nettle_dsa(sldns_buffer* buf, unsigned char* sigblock,
unsigned int sigblock_len, unsigned char* key, unsigned int keylen)
{
uint8_t digest[SHA1_DIGEST_SIZE];
uint8_t key_t_value;
int res = 0;
size_t offset;
struct dsa_public_key pubkey;
struct dsa_signature signature;
unsigned int expected_len;
/* Extract DSA signature from the record */
nettle_dsa_signature_init(&signature);
/* Signature length: 41 bytes - RFC 2536 sec. 3 */
if(sigblock_len == 41) {
if(key[0] != sigblock[0])
return "invalid T value in DSA signature or pubkey";
nettle_mpz_set_str_256_u(signature.r, 20, sigblock+1);
nettle_mpz_set_str_256_u(signature.s, 20, sigblock+1+20);
} else {
/* DER encoded, decode the ASN1 notated R and S bignums */
/* SEQUENCE { r INTEGER, s INTEGER } */
struct asn1_der_iterator i, seq;
if(asn1_der_iterator_first(&i, sigblock_len,
(uint8_t*)sigblock) != ASN1_ITERATOR_CONSTRUCTED
|| i.type != ASN1_SEQUENCE)
return "malformed DER encoded DSA signature";
/* decode this element of i using the seq iterator */
if(asn1_der_decode_constructed(&i, &seq) !=
ASN1_ITERATOR_PRIMITIVE || seq.type != ASN1_INTEGER)
return "malformed DER encoded DSA signature";
if(!asn1_der_get_bignum(&seq, signature.r, 20*8))
return "malformed DER encoded DSA signature";
if(asn1_der_iterator_next(&seq) != ASN1_ITERATOR_PRIMITIVE
|| seq.type != ASN1_INTEGER)
return "malformed DER encoded DSA signature";
if(!asn1_der_get_bignum(&seq, signature.s, 20*8))
return "malformed DER encoded DSA signature";
if(asn1_der_iterator_next(&i) != ASN1_ITERATOR_END)
return "malformed DER encoded DSA signature";
}
/* Validate T values constraints - RFC 2536 sec. 2 & sec. 3 */
key_t_value = key[0];
if (key_t_value > 8) {
return "invalid T value in DSA pubkey";
}
/* Pubkey minimum length: 21 bytes - RFC 2536 sec. 2 */
if (keylen < 21) {
return "DSA pubkey too short";
}
expected_len = 1 + /* T */
20 + /* Q */
(64 + key_t_value*8) + /* P */
(64 + key_t_value*8) + /* G */
(64 + key_t_value*8); /* Y */
if (keylen != expected_len ) {
return "invalid DSA pubkey length";
}
/* Extract DSA pubkey from the record */
nettle_dsa_public_key_init(&pubkey);
offset = 1;
nettle_mpz_set_str_256_u(pubkey.q, 20, key+offset);
offset += 20;
nettle_mpz_set_str_256_u(pubkey.p, (64 + key_t_value*8), key+offset);
offset += (64 + key_t_value*8);
nettle_mpz_set_str_256_u(pubkey.g, (64 + key_t_value*8), key+offset);
offset += (64 + key_t_value*8);
nettle_mpz_set_str_256_u(pubkey.y, (64 + key_t_value*8), key+offset);
/* Digest content of "buf" and verify its DSA signature in "sigblock"*/
res = _digest_nettle(SHA1_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf),
(unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest);
res &= dsa_sha1_verify_digest(&pubkey, digest, &signature);
/* Clear and return */
nettle_dsa_signature_clear(&signature);
nettle_dsa_public_key_clear(&pubkey);
if (!res)
return "DSA signature verification failed";
else
return NULL;
}
#endif /* USE_DSA */
static char *
_verify_nettle_rsa(sldns_buffer* buf, unsigned int digest_size, char* sigblock,
unsigned int sigblock_len, uint8_t* key, unsigned int keylen)
{
uint16_t exp_len = 0;
size_t exp_offset = 0, mod_offset = 0;
struct rsa_public_key pubkey;
mpz_t signature;
int res = 0;
/* RSA pubkey parsing as per RFC 3110 sec. 2 */
if( keylen <= 1) {
return "null RSA key";
}
if (key[0] != 0) {
/* 1-byte length */
exp_len = key[0];
exp_offset = 1;
} else {
/* 1-byte NUL + 2-bytes exponent length */
if (keylen < 3) {
return "incorrect RSA key length";
}
exp_len = READ_UINT16(key+1);
if (exp_len == 0)
return "null RSA exponent length";
exp_offset = 3;
}
/* Check that we are not over-running input length */
if (keylen < exp_offset + exp_len + 1) {
return "RSA key content shorter than expected";
}
mod_offset = exp_offset + exp_len;
nettle_rsa_public_key_init(&pubkey);
pubkey.size = keylen - mod_offset;
nettle_mpz_set_str_256_u(pubkey.e, exp_len, &key[exp_offset]);
nettle_mpz_set_str_256_u(pubkey.n, pubkey.size, &key[mod_offset]);
/* Digest content of "buf" and verify its RSA signature in "sigblock"*/
nettle_mpz_init_set_str_256_u(signature, sigblock_len, (uint8_t*)sigblock);
switch (digest_size) {
case SHA1_DIGEST_SIZE:
{
uint8_t digest[SHA1_DIGEST_SIZE];
res = _digest_nettle(SHA1_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf),
(unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest);
res &= rsa_sha1_verify_digest(&pubkey, digest, signature);
break;
}
case SHA256_DIGEST_SIZE:
{
uint8_t digest[SHA256_DIGEST_SIZE];
res = _digest_nettle(SHA256_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf),
(unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest);
res &= rsa_sha256_verify_digest(&pubkey, digest, signature);
break;
}
case SHA512_DIGEST_SIZE:
{
uint8_t digest[SHA512_DIGEST_SIZE];
res = _digest_nettle(SHA512_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf),
(unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest);
res &= rsa_sha512_verify_digest(&pubkey, digest, signature);
break;
}
default:
break;
}
/* Clear and return */
nettle_rsa_public_key_clear(&pubkey);
mpz_clear(signature);
if (!res) {
return "RSA signature verification failed";
} else {
return NULL;
}
}
#ifdef USE_ECDSA
static char *
_verify_nettle_ecdsa(sldns_buffer* buf, unsigned int digest_size, unsigned char* sigblock,
unsigned int sigblock_len, unsigned char* key, unsigned int keylen)
{
int res = 0;
struct ecc_point pubkey;
struct dsa_signature signature;
/* Always matched strength, as per RFC 6605 sec. 1 */
if (sigblock_len != 2*digest_size || keylen != 2*digest_size) {
return "wrong ECDSA signature length";
}
/* Parse ECDSA signature as per RFC 6605 sec. 4 */
nettle_dsa_signature_init(&signature);
switch (digest_size) {
case SHA256_DIGEST_SIZE:
{
uint8_t digest[SHA256_DIGEST_SIZE];
mpz_t x, y;
nettle_ecc_point_init(&pubkey, &nettle_secp_256r1);
nettle_mpz_init_set_str_256_u(x, SHA256_DIGEST_SIZE, key);
nettle_mpz_init_set_str_256_u(y, SHA256_DIGEST_SIZE, key+SHA256_DIGEST_SIZE);
nettle_mpz_set_str_256_u(signature.r, SHA256_DIGEST_SIZE, sigblock);
nettle_mpz_set_str_256_u(signature.s, SHA256_DIGEST_SIZE, sigblock+SHA256_DIGEST_SIZE);
res = _digest_nettle(SHA256_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf),
(unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest);
res &= nettle_ecc_point_set(&pubkey, x, y);
res &= nettle_ecdsa_verify (&pubkey, SHA256_DIGEST_SIZE, digest, &signature);
mpz_clear(x);
mpz_clear(y);
break;
}
case SHA384_DIGEST_SIZE:
{
uint8_t digest[SHA384_DIGEST_SIZE];
mpz_t x, y;
nettle_ecc_point_init(&pubkey, &nettle_secp_384r1);
nettle_mpz_init_set_str_256_u(x, SHA384_DIGEST_SIZE, key);
nettle_mpz_init_set_str_256_u(y, SHA384_DIGEST_SIZE, key+SHA384_DIGEST_SIZE);
nettle_mpz_set_str_256_u(signature.r, SHA384_DIGEST_SIZE, sigblock);
nettle_mpz_set_str_256_u(signature.s, SHA384_DIGEST_SIZE, sigblock+SHA384_DIGEST_SIZE);
res = _digest_nettle(SHA384_DIGEST_SIZE, (unsigned char*)sldns_buffer_begin(buf),
(unsigned int)sldns_buffer_limit(buf), (unsigned char*)digest);
res &= nettle_ecc_point_set(&pubkey, x, y);
res &= nettle_ecdsa_verify (&pubkey, SHA384_DIGEST_SIZE, digest, &signature);
mpz_clear(x);
mpz_clear(y);
nettle_ecc_point_clear(&pubkey);
break;
}
default:
return "unknown ECDSA algorithm";
}
/* Clear and return */
nettle_dsa_signature_clear(&signature);
if (!res)
return "ECDSA signature verification failed";
else
return NULL;
}
#endif
/**
* Check a canonical sig+rrset and signature against a dnskey
* @param buf: buffer with data to verify, the first rrsig part and the
* canonicalized rrset.
* @param algo: DNSKEY algorithm.
* @param sigblock: signature rdata field from RRSIG
* @param sigblock_len: length of sigblock data.
* @param key: public key data from DNSKEY RR.
* @param keylen: length of keydata.
* @param reason: bogus reason in more detail.
* @return secure if verification succeeded, bogus on crypto failure,
* unchecked on format errors and alloc failures.
*/
enum sec_status
verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock,
unsigned int sigblock_len, unsigned char* key, unsigned int keylen,
char** reason)
{
unsigned int digest_size = 0;
if (sigblock_len == 0 || keylen == 0) {
*reason = "null signature";
return sec_status_bogus;
}
switch(algo) {
#if defined(USE_DSA) && defined(USE_SHA1)
case LDNS_DSA:
case LDNS_DSA_NSEC3:
*reason = _verify_nettle_dsa(buf, sigblock, sigblock_len, key, keylen);
if (*reason != NULL)
return sec_status_bogus;
else
return sec_status_secure;
#endif /* USE_DSA */
#ifdef USE_SHA1
case LDNS_RSASHA1:
case LDNS_RSASHA1_NSEC3:
digest_size = (digest_size ? digest_size : SHA1_DIGEST_SIZE);
#endif
#ifdef USE_SHA2
case LDNS_RSASHA256:
digest_size = (digest_size ? digest_size : SHA256_DIGEST_SIZE);
case LDNS_RSASHA512:
digest_size = (digest_size ? digest_size : SHA512_DIGEST_SIZE);
#endif
*reason = _verify_nettle_rsa(buf, digest_size, (char*)sigblock,
sigblock_len, key, keylen);
if (*reason != NULL)
return sec_status_bogus;
else
return sec_status_secure;
#ifdef USE_ECDSA
case LDNS_ECDSAP256SHA256:
digest_size = (digest_size ? digest_size : SHA256_DIGEST_SIZE);
case LDNS_ECDSAP384SHA384:
digest_size = (digest_size ? digest_size : SHA384_DIGEST_SIZE);
*reason = _verify_nettle_ecdsa(buf, digest_size, sigblock,
sigblock_len, key, keylen);
if (*reason != NULL)
return sec_status_bogus;
else
return sec_status_secure;
#endif
case LDNS_RSAMD5:
case LDNS_ECC_GOST:
default:
*reason = "unable to verify signature, unknown algorithm";
return sec_status_bogus;
}
}
#endif /* HAVE_SSL or HAVE_NSS or HAVE_NETTLE */