diff options
Diffstat (limited to 'usr.sbin/unbound/validator/val_secalgo.c')
-rw-r--r-- | usr.sbin/unbound/validator/val_secalgo.c | 628 |
1 files changed, 571 insertions, 57 deletions
diff --git a/usr.sbin/unbound/validator/val_secalgo.c b/usr.sbin/unbound/validator/val_secalgo.c index f3f861ee2a4..7c8d7b28718 100644 --- a/usr.sbin/unbound/validator/val_secalgo.c +++ b/usr.sbin/unbound/validator/val_secalgo.c @@ -21,16 +21,16 @@ * 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 REGENTS 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. + * "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. */ /** @@ -41,12 +41,16 @@ * and do the library calls (for the crypto library in use). */ #include "config.h" -#include <ldns/ldns.h> -#include "validator/val_secalgo.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) +#if !defined(HAVE_SSL) && !defined(HAVE_NSS) && !defined(HAVE_NETTLE) #error "Need crypto library to do digital signature cryptography" #endif @@ -68,10 +72,36 @@ #include <openssl/engine.h> #endif +/* 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; + } +} + /** * 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. + * @return size in bytes of digest, or 0 if not supported. */ size_t ds_digest_size_supported(int algo) @@ -108,7 +138,7 @@ 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 ldns_digest_evp(data, (unsigned int)len, dest, md); + return sldns_digest_evp(data, (unsigned int)len, dest, md); } #endif @@ -152,13 +182,8 @@ dnskey_algo_id_is_supported(int id) { switch(id) { case LDNS_RSAMD5: -#ifdef HAVE_FIPS_MODE - /* openssl can return if the system is in FIPS mode, - * which does not allow MD5 hashes for network traffic */ - return !FIPS_mode(); -#else - return 1; -#endif + /* RFC 6725 deprecates RSAMD5 */ + return 0; case LDNS_DSA: case LDNS_DSA_NSEC3: case LDNS_RSASHA1: @@ -177,7 +202,7 @@ dnskey_algo_id_is_supported(int id) #ifdef USE_GOST case LDNS_ECC_GOST: /* we support GOST if it can be loaded */ - return ldns_key_EVP_load_gost_id(); + return sldns_key_EVP_load_gost_id(); #endif default: return 0; @@ -309,10 +334,10 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, log_err("verify: malloc failure in crypto"); return 0; } - dsa = ldns_key_buf2dsa_raw(key, keylen); + dsa = sldns_key_buf2dsa_raw(key, keylen); if(!dsa) { verbose(VERB_QUERY, "verify: " - "ldns_key_buf2dsa_raw failed"); + "sldns_key_buf2dsa_raw failed"); return 0; } if(EVP_PKEY_assign_DSA(*evp_key, dsa) == 0) { @@ -336,10 +361,10 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, log_err("verify: malloc failure in crypto"); return 0; } - rsa = ldns_key_buf2rsa_raw(key, keylen); + rsa = sldns_key_buf2rsa_raw(key, keylen); if(!rsa) { verbose(VERB_QUERY, "verify: " - "ldns_key_buf2rsa_raw SHA failed"); + "sldns_key_buf2rsa_raw SHA failed"); return 0; } if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) { @@ -368,10 +393,10 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, log_err("verify: malloc failure in crypto"); return 0; } - rsa = ldns_key_buf2rsa_raw(key, keylen); + rsa = sldns_key_buf2rsa_raw(key, keylen); if(!rsa) { verbose(VERB_QUERY, "verify: " - "ldns_key_buf2rsa_raw MD5 failed"); + "sldns_key_buf2rsa_raw MD5 failed"); return 0; } if(EVP_PKEY_assign_RSA(*evp_key, rsa) == 0) { @@ -384,10 +409,10 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, break; #ifdef USE_GOST case LDNS_ECC_GOST: - *evp_key = ldns_gost2pkey_raw(key, keylen); + *evp_key = sldns_gost2pkey_raw(key, keylen); if(!*evp_key) { verbose(VERB_QUERY, "verify: " - "ldns_gost2pkey_raw failed"); + "sldns_gost2pkey_raw failed"); return 0; } *digest_type = EVP_get_digestbyname("md_gost94"); @@ -400,11 +425,11 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, #endif #ifdef USE_ECDSA case LDNS_ECDSAP256SHA256: - *evp_key = ldns_ecdsa2pkey_raw(key, keylen, + *evp_key = sldns_ecdsa2pkey_raw(key, keylen, LDNS_ECDSAP256SHA256); if(!*evp_key) { verbose(VERB_QUERY, "verify: " - "ldns_ecdsa2pkey_raw failed"); + "sldns_ecdsa2pkey_raw failed"); return 0; } #ifdef USE_ECDSA_EVP_WORKAROUND @@ -427,11 +452,11 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, #endif break; case LDNS_ECDSAP384SHA384: - *evp_key = ldns_ecdsa2pkey_raw(key, keylen, + *evp_key = sldns_ecdsa2pkey_raw(key, keylen, LDNS_ECDSAP384SHA384); if(!*evp_key) { verbose(VERB_QUERY, "verify: " - "ldns_ecdsa2pkey_raw failed"); + "sldns_ecdsa2pkey_raw failed"); return 0; } #ifdef USE_ECDSA_EVP_WORKAROUND @@ -476,7 +501,7 @@ setup_key_digest(int algo, EVP_PKEY** evp_key, const EVP_MD** digest_type, * unchecked on format errors and alloc failures. */ enum sec_status -verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, +verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock, unsigned int sigblock_len, unsigned char* key, unsigned int keylen, char** reason) { @@ -523,8 +548,8 @@ verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, if(dofree) free(sigblock); return sec_status_unchecked; } - if(EVP_VerifyUpdate(&ctx, (unsigned char*)ldns_buffer_begin(buf), - (unsigned int)ldns_buffer_limit(buf)) == 0) { + 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_PKEY_free(evp_key); if(dofree) free(sigblock); @@ -558,12 +583,40 @@ verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, /**************************************************/ #elif defined(HAVE_NSS) /* libnss implementation */ -#include <nss3/sechash.h> -#include <nss3/pk11pub.h> -#include <nss3/keyhi.h> -#include <nss3/secerr.h> -#include <nss3/cryptohi.h> -#include <nspr4/prerror.h> +/* 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; + } +} size_t ds_digest_size_supported(int algo) @@ -621,8 +674,8 @@ dnskey_algo_id_is_supported(int id) /* uses libNSS */ switch(id) { case LDNS_RSAMD5: - /* disable MD5 support if FIPS mode is enabled in libnss */ - return !PK11_IsFIPS(); + /* RFC 6725 deprecates RSAMD5 */ + return 0; case LDNS_DSA: case LDNS_DSA_NSEC3: case LDNS_RSASHA1: @@ -672,12 +725,12 @@ static SECKEYPublicKey* nss_buf2ecdsa(unsigned char* key, size_t len, int algo) SECKEYPublicKey* pk; SECItem pub = {siBuffer, NULL, 0}; SECItem params = {siBuffer, NULL, 0}; - unsigned char param256[] = { + 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 }; - unsigned char param384[] = { + 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 @@ -848,19 +901,19 @@ nss_setup_key_digest(int algo, SECKEYPublicKey** pubkey, HASH_HashType* htype, /* uses libNSS */ /* hash prefix for md5, RFC2537 */ - unsigned char p_md5[] = {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, + 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 */ - unsigned char p_sha1[] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, + static unsigned char p_sha1[] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14}; /* from RFC5702 */ - unsigned char p_sha256[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, + static unsigned char p_sha256[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; - unsigned char p_sha512[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, + 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 .. - unsigned char p_sha384[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, + static unsigned char p_sha384[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}; */ @@ -966,7 +1019,7 @@ nss_setup_key_digest(int algo, SECKEYPublicKey** pubkey, HASH_HashType* htype, * unchecked on format errors and alloc failures. */ enum sec_status -verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, +verify_canonrrset(sldns_buffer* buf, int algo, unsigned char* sigblock, unsigned int sigblock_len, unsigned char* key, unsigned int keylen, char** reason) { @@ -1022,8 +1075,8 @@ verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, SECKEY_DestroyPublicKey(pubkey); return sec_status_unchecked; } - if(HASH_HashBuf(htype, hash, (unsigned char*)ldns_buffer_begin(buf), - (unsigned int)ldns_buffer_limit(buf)) != SECSuccess) { + 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; @@ -1069,5 +1122,466 @@ verify_canonrrset(ldns_buffer* buf, int algo, unsigned char* sigblock, return sec_status_bogus; } +#elif defined(HAVE_NETTLE) + +#include "sha.h" +#include "bignum.h" +#include "macros.h" +#include "rsa.h" +#include "dsa.h" +#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; + } +} + +/** + * 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: + return SHA1_DIGEST_SIZE; +#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) { + case LDNS_SHA1: + return _digest_nettle(SHA1_DIGEST_SIZE, buf, len, res); +#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) { + case LDNS_DSA: + case LDNS_DSA_NSEC3: + case LDNS_RSASHA1: + case LDNS_RSASHA1_NSEC3: +#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; + } +} + +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; + 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 = key[0]; + if (key_t > 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*8) + /* P */ + (64 + key_t*8) + /* G */ + (64 + key_t*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*8), key+offset); + offset += (64 + key_t*8); + nettle_mpz_set_str_256_u(pubkey.g, (64 + key_t*8), key+offset); + offset += (64 + key_t*8); + nettle_mpz_set_str_256_u(pubkey.y, (64 + key_t*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; +} + +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) { + 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; + + case LDNS_RSASHA1: + case LDNS_RSASHA1_NSEC3: + digest_size = (digest_size ? digest_size : SHA1_DIGEST_SIZE); +#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 */ +#endif /* HAVE_SSL or HAVE_NSS or HAVE_NETTLE */ |