Abstract out HMAC functions in request-internal.c.

This commit is contained in:
Jim Hague 2018-11-27 11:49:12 +00:00
parent 4ec93a3df0
commit bc3106af94
4 changed files with 198 additions and 73 deletions

View File

@ -34,6 +34,10 @@
#ifndef _GETDNS_TLS_INTERNAL_H
#define _GETDNS_TLS_INTERNAL_H
#include <openssl/hmac.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include "getdns/getdns.h"
#ifndef HAVE_DECL_SSL_CTX_SET1_CURVES_LIST
@ -64,4 +68,12 @@ typedef struct _getdns_tls_x509
X509* ssl;
} _getdns_tls_x509;
typedef struct _getdns_tls_hmac
{
HMAC_CTX *ctx;
#ifndef HAVE_HMAC_CTX_NEW
HMAC_CTX ctx_space;
#endif
} _getdns_tls_hmac;
#endif /* _GETDNS_TLS_INTERNAL_H */

View File

@ -609,4 +609,126 @@ int _getdns_tls_x509_to_der(_getdns_tls_x509* cert, uint8_t** buf)
return i2d_X509(cert->ssl, buf);
}
unsigned char* _getdns_tls_hmac_hash(int algorithm, const void* key, size_t key_size, const void* data, size_t data_size, size_t* output_size)
{
const EVP_MD* digester;
unsigned char* res;
unsigned int md_len;
switch (algorithm) {
#ifdef HAVE_EVP_MD5
case GETDNS_HMAC_MD5 : digester = EVP_md5() ; break;
#endif
#ifdef HAVE_EVP_SHA1
case GETDNS_HMAC_SHA1 : digester = EVP_sha1() ; break;
#endif
#ifdef HAVE_EVP_SHA224
case GETDNS_HMAC_SHA224: digester = EVP_sha224(); break;
#endif
#ifdef HAVE_EVP_SHA256
case GETDNS_HMAC_SHA256: digester = EVP_sha256(); break;
#endif
#ifdef HAVE_EVP_SHA384
case GETDNS_HMAC_SHA384: digester = EVP_sha384(); break;
#endif
#ifdef HAVE_EVP_SHA512
case GETDNS_HMAC_SHA512: digester = EVP_sha512(); break;
#endif
default : return NULL;
}
res = (unsigned char*) malloc(EVP_MAX_MD_SIZE);
if (!res)
return NULL;
(void) HMAC(digester, key, key_size, data, data_size, res, &md_len);
if (output_size)
*output_size = md_len;
return res;
}
_getdns_tls_hmac* _getdns_tls_hmac_new(int algorithm, const void* key, size_t key_size)
{
const EVP_MD *digester;
_getdns_tls_hmac* res;
switch (algorithm) {
#ifdef HAVE_EVP_MD5
case GETDNS_HMAC_MD5 : digester = EVP_md5() ; break;
#endif
#ifdef HAVE_EVP_SHA1
case GETDNS_HMAC_SHA1 : digester = EVP_sha1() ; break;
#endif
#ifdef HAVE_EVP_SHA224
case GETDNS_HMAC_SHA224: digester = EVP_sha224(); break;
#endif
#ifdef HAVE_EVP_SHA256
case GETDNS_HMAC_SHA256: digester = EVP_sha256(); break;
#endif
#ifdef HAVE_EVP_SHA384
case GETDNS_HMAC_SHA384: digester = EVP_sha384(); break;
#endif
#ifdef HAVE_EVP_SHA512
case GETDNS_HMAC_SHA512: digester = EVP_sha512(); break;
#endif
default : return NULL;
}
if (!(res = malloc(sizeof(struct _getdns_tls_hmac))))
return NULL;
#ifdef HAVE_HMAC_CTX_NEW
res->ctx = HMAC_CTX_new();
if (!res->ctx) {
free(res);
return NULL;
}
#else
res->ctx = &res->ctx_space;
HMAC_CTX_init(res->ctx);
#endif
if (!HMAC_Init_ex(res->ctx, key, key_size, digester, NULL)) {
#ifdef HAVE_HMAC_CTX_NEW
HMAC_CTX_free(res->ctx);
#endif
free(res);
return NULL;
}
return res;
}
getdns_return_t _getdns_tls_hmac_add(_getdns_tls_hmac* h, const void* data, size_t data_size)
{
if (!h || !h->ctx || !data)
return GETDNS_RETURN_INVALID_PARAMETER;
if (!HMAC_Update(h->ctx, data, data_size))
return GETDNS_RETURN_GENERIC_ERROR;
else
return GETDNS_RETURN_GOOD;
}
unsigned char* _getdns_tls_hmac_end(_getdns_tls_hmac* h, size_t* output_size)
{
unsigned char* res;
unsigned int md_len;
res = (unsigned char*) malloc(EVP_MAX_MD_SIZE);
if (!res)
return NULL;
(void) HMAC_Final(h->ctx, res, &md_len);
#ifdef HAVE_HMAC_CTX_NEW
HMAC_CTX_free(h->ctx);
#endif
free(h);
if (output_size)
*output_size = md_len;
return res;
}
/* tls.c */

View File

@ -401,9 +401,8 @@ _getdns_network_req_add_tsig(getdns_network_req *req)
gldns_buffer gbuf;
uint16_t arcount;
const getdns_tsig_info *tsig_info;
uint8_t md_buf[EVP_MAX_MD_SIZE];
unsigned int md_len = EVP_MAX_MD_SIZE;
const EVP_MD *digester;
unsigned char* md_buf;
size_t md_len;
/* Should only be called when in stub mode */
assert(req->query);
@ -436,31 +435,9 @@ _getdns_network_req_add_tsig(getdns_network_req *req)
gldns_buffer_write_u16(&gbuf, 0); /* Error */
gldns_buffer_write_u16(&gbuf, 0); /* Other len */
switch (upstream->tsig_alg) {
#ifdef HAVE_EVP_MD5
case GETDNS_HMAC_MD5 : digester = EVP_md5() ; break;
#endif
#ifdef HAVE_EVP_SHA1
case GETDNS_HMAC_SHA1 : digester = EVP_sha1() ; break;
#endif
#ifdef HAVE_EVP_SHA224
case GETDNS_HMAC_SHA224: digester = EVP_sha224(); break;
#endif
#ifdef HAVE_EVP_SHA256
case GETDNS_HMAC_SHA256: digester = EVP_sha256(); break;
#endif
#ifdef HAVE_EVP_SHA384
case GETDNS_HMAC_SHA384: digester = EVP_sha384(); break;
#endif
#ifdef HAVE_EVP_SHA512
case GETDNS_HMAC_SHA512: digester = EVP_sha512(); break;
#endif
default : return req->response - req->query;
}
(void) HMAC(digester, upstream->tsig_key, upstream->tsig_size,
(void *)req->query, gldns_buffer_current(&gbuf) - req->query,
md_buf, &md_len);
md_buf = _getdns_tls_hmac_hash(upstream->tsig_alg, upstream->tsig_key, upstream->tsig_size, (void *)req->query, gldns_buffer_current(&gbuf) - req->query, &md_len);
if (!md_buf)
return req->response - req->query;
gldns_buffer_rewind(&gbuf);
gldns_buffer_write(&gbuf,
@ -480,6 +457,8 @@ _getdns_network_req_add_tsig(getdns_network_req *req)
gldns_buffer_write_u16(&gbuf, 0); /* Error */
gldns_buffer_write_u16(&gbuf, 0); /* Other len */
free(md_buf);
if (gldns_buffer_position(&gbuf) > gldns_buffer_limit(&gbuf))
return req->response - req->query;
@ -506,14 +485,10 @@ _getdns_network_validate_tsig(getdns_network_req *req)
const uint8_t *response_mac;
uint16_t response_mac_len;
uint8_t other_len;
uint8_t result_mac[EVP_MAX_MD_SIZE];
unsigned int result_mac_len = EVP_MAX_MD_SIZE;
unsigned char *result_mac;
size_t result_mac_len;
uint16_t original_id;
const EVP_MD *digester;
HMAC_CTX *ctx;
#ifndef HAVE_HMAC_CTX_NEW
HMAC_CTX ctx_space;
#endif
_getdns_tls_hmac *hmac;
DEBUG_STUB("%s %-35s: Validate TSIG\n", STUB_DEBUG_TSIG, __FUNC__);
for ( rr = _getdns_rr_iter_init(&rr_spc, req->query,
@ -620,39 +595,16 @@ _getdns_network_validate_tsig(getdns_network_req *req)
gldns_read_uint16(req->response + 10) - 1);
gldns_write_uint16(req->response, original_id);
switch (req->upstream->tsig_alg) {
#ifdef HAVE_EVP_MD5
case GETDNS_HMAC_MD5 : digester = EVP_md5() ; break;
#endif
#ifdef HAVE_EVP_SHA1
case GETDNS_HMAC_SHA1 : digester = EVP_sha1() ; break;
#endif
#ifdef HAVE_EVP_SHA224
case GETDNS_HMAC_SHA224: digester = EVP_sha224(); break;
#endif
#ifdef HAVE_EVP_SHA256
case GETDNS_HMAC_SHA256: digester = EVP_sha256(); break;
#endif
#ifdef HAVE_EVP_SHA384
case GETDNS_HMAC_SHA384: digester = EVP_sha384(); break;
#endif
#ifdef HAVE_EVP_SHA512
case GETDNS_HMAC_SHA512: digester = EVP_sha512(); break;
#endif
default : return;
}
#ifdef HAVE_HMAC_CTX_NEW
ctx = HMAC_CTX_new();
#else
ctx = &ctx_space;
HMAC_CTX_init(ctx);
#endif
(void) HMAC_Init_ex(ctx, req->upstream->tsig_key,
req->upstream->tsig_size, digester, NULL);
(void) HMAC_Update(ctx, request_mac - 2, request_mac_len + 2);
(void) HMAC_Update(ctx, req->response, rr->pos - req->response);
(void) HMAC_Update(ctx, tsig_vars, gldns_buffer_position(&gbuf));
HMAC_Final(ctx, result_mac, &result_mac_len);
hmac = _getdns_tls_hmac_new(req->upstream->tsig_alg, req->upstream->tsig_key, req->upstream->tsig_size);
if (!hmac)
return;
_getdns_tls_hmac_add(hmac, request_mac - 2, request_mac_len + 2);
_getdns_tls_hmac_add(hmac, req->response, rr->pos - req->response);
_getdns_tls_hmac_add(hmac, tsig_vars, gldns_buffer_position(&gbuf));
result_mac = _getdns_tls_hmac_end(hmac, &result_mac_len);
if (!result_mac)
return;
DEBUG_STUB("%s %-35s: Result MAC length: %d\n",
STUB_DEBUG_TSIG, __FUNC__, (int)(result_mac_len));
@ -660,11 +612,6 @@ _getdns_network_validate_tsig(getdns_network_req *req)
memcmp(result_mac, response_mac, result_mac_len) == 0)
req->tsig_status = GETDNS_DNSSEC_SECURE;
#ifdef HAVE_HMAC_CTX_FREE
HMAC_CTX_free(ctx);
#else
HMAC_CTX_cleanup(ctx);
#endif
gldns_write_uint16(req->response, gldns_read_uint16(req->query));
gldns_write_uint16(req->response + 10,
gldns_read_uint16(req->response + 10) + 1);

View File

@ -316,4 +316,48 @@ int _getdns_tls_x509_to_der(_getdns_tls_x509* cert, uint8_t** buf);
*/
getdns_return_t _getdns_tls_get_api_information(getdns_dict* dict);
/**
* Return buffer with HMAC hash.
*
* @param algorithm hash algorithm to use (<code>GETDNS_HMAC_?</code>).
* @param key the key.
* @param key_size the key size.
* @param data the data to hash.
* @param data_size the data size.
* @param output_size the output size will be written here if not NULL.
* @return output malloc'd buffer with output, NULL on error.
*/
unsigned char* _getdns_tls_hmac_hash(int algorithm, const void* key, size_t key_size, const void* data, size_t data_size, size_t* output_size);
/**
* Return a new HMAC handle.
*
* @param algorithm hash algorithm to use (<code>GETDNS_HMAC_?</code>).
* @param key the key.
* @param key_size the key size.
* @return HMAC handle or NULL on error.
*/
_getdns_tls_hmac* _getdns_tls_hmac_new(int algorithm, const void* key, size_t key_size);
/**
* Add data to a HMAC.
*
* @param h the HMAC.
* @param data the data to add.
* @param data_size the size of data to add.
* @return GETDNS_RETURN_GOOD if added.
* @return GETDNS_RETURN_INVALID_PARAMETER if h is null or has no HMAC.
* @return GETDNS_RETURN_GENERIC_ERROR on error.
*/
getdns_return_t _getdns_tls_hmac_add(_getdns_tls_hmac* h, const void* data, size_t data_size);
/**
* Return the HMAC digest and free the handle.
*
* @param h the HMAC.
* @param output_size the output size will be written here if not NULL.
* @return output malloc'd buffer with output, NULL on error.
*/
unsigned char* _getdns_tls_hmac_end(_getdns_tls_hmac* h, size_t* output_size);
#endif /* _GETDNS_TLS_H */