void gldns_buffer_copy(gldns_buffer* result, gldns_buffer* from) { size_t tocopy = gldns_buffer_limit(from); if(tocopy > gldns_buffer_capacity(result)) tocopy = gldns_buffer_capacity(result); gldns_buffer_clear(result); gldns_buffer_write(result, gldns_buffer_begin(from), tocopy); gldns_buffer_flip(result); }
getdns_return_t getdns_rr_dict2str_scan( const getdns_dict *rr_dict, char **str, int *str_len) { getdns_return_t r; gldns_buffer gbuf; uint8_t buf_spc[4096], *buf = buf_spc, *scan_buf; size_t sz, scan_sz; int prev_str_len; char *prev_str; int sz_needed; if (!rr_dict || !str || !*str || !str_len) return GETDNS_RETURN_INVALID_PARAMETER; gldns_buffer_init_frm_data(&gbuf, buf, sizeof(buf_spc)); r = _getdns_rr_dict2wire(rr_dict, &gbuf); if (gldns_buffer_position(&gbuf) > sizeof(buf_spc)) { if (!(buf = GETDNS_XMALLOC( rr_dict->mf, uint8_t, (sz = gldns_buffer_position(&gbuf))))) { return GETDNS_RETURN_MEMORY_ERROR; } gldns_buffer_init_frm_data(&gbuf, buf, sz); r = _getdns_rr_dict2wire(rr_dict, &gbuf); } if (r) { if (buf != buf_spc) GETDNS_FREE(rr_dict->mf, buf); return r; } scan_buf = gldns_buffer_begin(&gbuf); scan_sz = gldns_buffer_position(&gbuf); prev_str = *str; prev_str_len = *str_len; sz = (size_t)*str_len; sz_needed = gldns_wire2str_rr_scan( &scan_buf, &scan_sz, str, &sz, NULL, 0); if (sz_needed > prev_str_len) { *str = prev_str + sz_needed; *str_len = prev_str_len - sz_needed; r = GETDNS_RETURN_NEED_MORE_SPACE; } else { *str_len = sz; **str = 0; } if (buf != buf_spc) GETDNS_FREE(rr_dict->mf, buf); return r; }
/** * 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. */ int _getdns_verify_canonrrset(gldns_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; EVP_PKEY *evp_key = NULL; 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 0; } #ifdef USE_DSA /* if it is a DSA signature in bind format, convert to DER format */ if((algo == GLDNS_DSA || algo == GLDNS_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 0; } dofree = 1; } #endif #if defined(USE_ECDSA) && defined(USE_DSA) else #endif #ifdef USE_ECDSA if(algo == GLDNS_ECDSAP256SHA256 || algo == GLDNS_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 0; } 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); return 0; } 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); return 0; } if(EVP_VerifyUpdate(ctx, (unsigned char*)gldns_buffer_begin(buf), (unsigned int)gldns_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); return 0; } res = EVP_VerifyFinal(ctx, sigblock, sigblock_len, evp_key); EVP_MD_CTX_destroy(ctx); EVP_PKEY_free(evp_key); if(dofree) free(sigblock); if(res == 1) { return 1; } else if(res == 0) { verbose(VERB_QUERY, "verify: signature mismatch"); *reason = "signature crypto failed"; return 0; } log_crypto_error("verify:", ERR_get_error()); return 0; }
static char * _verify_nettle_ecdsa(gldns_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*)gldns_buffer_begin(buf), (unsigned int)gldns_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*)gldns_buffer_begin(buf), (unsigned int)gldns_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; }
static char * _verify_nettle_rsa(gldns_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*)gldns_buffer_begin(buf), (unsigned int)gldns_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*)gldns_buffer_begin(buf), (unsigned int)gldns_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*)gldns_buffer_begin(buf), (unsigned int)gldns_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; } }
static char * _verify_nettle_dsa(gldns_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*)gldns_buffer_begin(buf), (unsigned int)gldns_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; }
/** * 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. */ int _getdns_verify_canonrrset(gldns_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 0; } #ifdef USE_DSA /* need to convert DSA, ECDSA signatures? */ if((algo == GLDNS_DSA || algo == GLDNS_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 0; } if(SECITEM_CopyItem(pubkey->arena, &secsig, p)) { log_err("alloc failure in DER decode"); SECKEY_DestroyPublicKey(pubkey); SECITEM_FreeItem(p, PR_TRUE); return 0; } 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 0; } if(HASH_HashBuf(htype, hash, (unsigned char*)gldns_buffer_begin(buf), (unsigned int)gldns_buffer_limit(buf)) != SECSuccess) { verbose(VERB_QUERY, "verify: HASH_HashBuf failed"); SECKEY_DestroyPublicKey(pubkey); return 0; } if(prefix) { int hashlen = sechash.len; if(prefixlen+hashlen > sizeof(hash2)) { verbose(VERB_QUERY, "verify: hashprefix too large"); SECKEY_DestroyPublicKey(pubkey); return 0; } 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 1; } 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 0; /* but other errors are commonly returned * for a bad signature from NSS. Thus we return bogus, * not unchecked */ *reason = "signature crypto failed"; return 0; } verbose(VERB_QUERY, "verify: signature mismatch: %s", PORT_ErrorToString(err)); *reason = "signature crypto failed"; return 0; }