summaryrefslogtreecommitdiff
path: root/usr.sbin/unbound/validator/val_secalgo.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/unbound/validator/val_secalgo.c')
-rw-r--r--usr.sbin/unbound/validator/val_secalgo.c628
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 */