#include <stdlib.h>
#include <string.h>
#include <isc/buffer.h>
#include <isc/refcount.h>
#include <isc/util.h>
#include <dns/keyvalues.h>
#include <dst/result.h>
#include "dst_internal.h"
static dst_func_t *dst_t_func[DST_MAX_ALGS];
static int dst_initialized = 0;
static dst_key_t * get_key_struct(unsigned int alg,
unsigned int flags,
unsigned int protocol,
unsigned int bits);
static isc_result_t computeid(dst_key_t *key);
static isc_result_t frombuffer(unsigned int alg,
unsigned int flags,
unsigned int protocol,
isc_buffer_t *source,
dst_key_t **keyp);
static isc_result_t algorithm_status(unsigned int alg);
#define RETERR(x) \
do { \
result = (x); \
if (result != ISC_R_SUCCESS) \
goto out; \
} while (0)
#define CHECKALG(alg) \
do { \
isc_result_t _r; \
_r = algorithm_status(alg); \
if (_r != ISC_R_SUCCESS) \
return (_r); \
} while (0); \
isc_result_t
dst_lib_init(void) {
isc_result_t result;
REQUIRE(!dst_initialized);
dst_result_register();
memset(dst_t_func, 0, sizeof(dst_t_func));
RETERR(dst__hmacsha1_init(&dst_t_func[DST_ALG_HMACSHA1]));
RETERR(dst__hmacsha224_init(&dst_t_func[DST_ALG_HMACSHA224]));
RETERR(dst__hmacsha256_init(&dst_t_func[DST_ALG_HMACSHA256]));
RETERR(dst__hmacsha384_init(&dst_t_func[DST_ALG_HMACSHA384]));
RETERR(dst__hmacsha512_init(&dst_t_func[DST_ALG_HMACSHA512]));
RETERR(dst__openssl_init());
dst_initialized = 1;
return (ISC_R_SUCCESS);
out:
dst_initialized = 1;
dst_lib_destroy();
return (result);
}
void
dst_lib_destroy(void) {
RUNTIME_CHECK(dst_initialized);
dst_initialized = 0;
dst__openssl_destroy();
}
int
dst_algorithm_supported(unsigned int alg) {
REQUIRE(dst_initialized);
if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL)
return (0);
return (1);
}
isc_result_t
dst_context_create3(dst_key_t *key,
isc_logcategory_t *category, int useforsigning,
dst_context_t **dctxp)
{
dst_context_t *dctx;
isc_result_t result;
REQUIRE(dst_initialized);
REQUIRE(dctxp != NULL && *dctxp == NULL);
dctx = malloc(sizeof(dst_context_t));
if (dctx == NULL)
return (ISC_R_NOMEMORY);
memset(dctx, 0, sizeof(*dctx));
dst_key_attach(key, &dctx->key);
dctx->category = category;
if (useforsigning)
dctx->use = DO_SIGN;
else
dctx->use = DO_VERIFY;
result = key->func->createctx(key, dctx);
if (result != ISC_R_SUCCESS) {
if (dctx->key != NULL)
dst_key_free(&dctx->key);
free(dctx);
return (result);
}
*dctxp = dctx;
return (ISC_R_SUCCESS);
}
void
dst_context_destroy(dst_context_t **dctxp) {
dst_context_t *dctx;
REQUIRE(dctxp != NULL);
dctx = *dctxp;
dctx->key->func->destroyctx(dctx);
if (dctx->key != NULL)
dst_key_free(&dctx->key);
free(dctx);
*dctxp = NULL;
}
isc_result_t
dst_context_adddata(dst_context_t *dctx, const isc_region_t *data) {
REQUIRE(data != NULL);
return (dctx->key->func->adddata(dctx, data));
}
isc_result_t
dst_context_sign(dst_context_t *dctx, isc_buffer_t *sig) {
dst_key_t *key;
REQUIRE(sig != NULL);
key = dctx->key;
CHECKALG(key->key_alg);
return (key->func->sign(dctx, sig));
}
isc_result_t
dst_context_verify(dst_context_t *dctx, isc_region_t *sig) {
REQUIRE(sig != NULL);
CHECKALG(dctx->key->key_alg);
return (dctx->key->func->verify(dctx, sig));
}
isc_result_t
dst_key_todns(const dst_key_t *key, isc_buffer_t *target) {
REQUIRE(dst_initialized);
REQUIRE(target != NULL);
CHECKALG(key->key_alg);
if (isc_buffer_availablelength(target) < 4)
return (ISC_R_NOSPACE);
isc_buffer_putuint16(target, (uint16_t)(key->key_flags & 0xffff));
isc_buffer_putuint8(target, (uint8_t)key->key_proto);
isc_buffer_putuint8(target, (uint8_t)key->key_alg);
if (key->key_flags & DNS_KEYFLAG_EXTENDED) {
if (isc_buffer_availablelength(target) < 2)
return (ISC_R_NOSPACE);
isc_buffer_putuint16(target,
(uint16_t)((key->key_flags >> 16)
& 0xffff));
}
return (key->func->todns(key, target));
}
isc_result_t
dst_key_frombuffer(unsigned int alg, unsigned int flags, unsigned int protocol,
isc_buffer_t *source, dst_key_t **keyp)
{
dst_key_t *key = NULL;
isc_result_t result;
REQUIRE(dst_initialized);
result = frombuffer(alg, flags, protocol, source, &key);
if (result != ISC_R_SUCCESS)
return (result);
result = computeid(key);
if (result != ISC_R_SUCCESS) {
dst_key_free(&key);
return (result);
}
*keyp = key;
return (ISC_R_SUCCESS);
}
void
dst_key_attach(dst_key_t *source, dst_key_t **target) {
REQUIRE(dst_initialized);
REQUIRE(target != NULL && *target == NULL);
isc_refcount_increment(&source->refs, NULL);
*target = source;
}
void
dst_key_free(dst_key_t **keyp) {
dst_key_t *key;
unsigned int refs;
REQUIRE(dst_initialized);
REQUIRE(keyp != NULL);
key = *keyp;
isc_refcount_decrement(&key->refs, &refs);
if (refs != 0)
return;
isc_refcount_destroy(&key->refs);
key->func->destroy(key);
freezero(key, sizeof(*key));
*keyp = NULL;
}
isc_result_t
dst_key_sigsize(const dst_key_t *key, unsigned int *n) {
REQUIRE(dst_initialized);
REQUIRE(n != NULL);
switch (key->key_alg) {
case DST_ALG_HMACSHA1:
*n = ISC_SHA1_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA224:
*n = ISC_SHA224_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA256:
*n = ISC_SHA256_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA384:
*n = ISC_SHA384_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA512:
*n = ISC_SHA512_DIGESTLENGTH;
break;
default:
return (DST_R_UNSUPPORTEDALG);
}
return (ISC_R_SUCCESS);
}
static dst_key_t *
get_key_struct(unsigned int alg,
unsigned int flags, unsigned int protocol,
unsigned int bits)
{
dst_key_t *key;
isc_result_t result;
key = (dst_key_t *) malloc(sizeof(dst_key_t));
if (key == NULL)
return (NULL);
memset(key, 0, sizeof(dst_key_t));
result = isc_refcount_init(&key->refs, 1);
if (result != ISC_R_SUCCESS) {
free(key);
return (NULL);
}
key->key_alg = alg;
key->key_flags = flags;
key->key_proto = protocol;
key->key_size = bits;
key->func = dst_t_func[alg];
return (key);
}
static isc_result_t
computeid(dst_key_t *key) {
isc_buffer_t dnsbuf;
unsigned char dns_array[DST_KEY_MAXSIZE];
isc_region_t r;
isc_result_t ret;
isc_buffer_init(&dnsbuf, dns_array, sizeof(dns_array));
ret = dst_key_todns(key, &dnsbuf);
if (ret != ISC_R_SUCCESS)
return (ret);
isc_buffer_usedregion(&dnsbuf, &r);
return (ISC_R_SUCCESS);
}
static isc_result_t
frombuffer(unsigned int alg, unsigned int flags,
unsigned int protocol, isc_buffer_t *source, dst_key_t **keyp)
{
dst_key_t *key;
isc_result_t ret;
REQUIRE(source != NULL);
REQUIRE(keyp != NULL && *keyp == NULL);
key = get_key_struct(alg, flags, protocol, 0);
if (key == NULL)
return (ISC_R_NOMEMORY);
if (isc_buffer_remaininglength(source) > 0) {
ret = algorithm_status(alg);
if (ret != ISC_R_SUCCESS) {
dst_key_free(&key);
return (ret);
}
ret = key->func->fromdns(key, source);
if (ret != ISC_R_SUCCESS) {
dst_key_free(&key);
return (ret);
}
}
*keyp = key;
return (ISC_R_SUCCESS);
}
static isc_result_t
algorithm_status(unsigned int alg) {
REQUIRE(dst_initialized);
if (dst_algorithm_supported(alg))
return (ISC_R_SUCCESS);
return (DST_R_UNSUPPORTEDALG);
}