Files
bind9/lib/dns/hmac_link.c
Ondřej Surý 99ba29bc52 Change isc_random() to be just PRNG, and add isc_nonce_buf() that uses CSPRNG
This commit reverts the previous change to use system provided
entropy, as (SYS_)getrandom is very slow on Linux because it is
a syscall.

The change introduced in this commit adds a new call isc_nonce_buf
that uses CSPRNG from cryptographic library provider to generate
secure data that can be and must be used for generating nonces.
Example usage would be DNS cookies.

The isc_random() API has been changed to use fast PRNG that is not
cryptographically secure, but runs entirely in user space.  Two
contestants have been considered xoroshiro family of the functions
by Villa&Blackman and PCG by O'Neill.  After a consideration the
xoshiro128starstar function has been used as uint32_t random number
provider because it is very fast and has good enough properties
for our usage pattern.

The other change introduced in the commit is the more extensive usage
of isc_random_uniform in places where the usage pattern was
isc_random() % n to prevent modulo bias.  For usage patterns where
only 16 or 8 bits are needed (DNS Message ID), the isc_random()
functions has been renamed to isc_random32(), and isc_random16() and
isc_random8() functions have been introduced by &-ing the
isc_random32() output with 0xffff and 0xff.  Please note that the
functions that uses stripped down bit count doesn't pass our
NIST SP 800-22 based random test.
2018-05-29 22:58:21 +02:00

1798 lines
44 KiB
C

/*
* Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*
* Portions Copyright (C) Network Associates, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE
* FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <isc/buffer.h>
#include <isc/hmacmd5.h>
#include <isc/hmacsha.h>
#include <isc/md5.h>
#include <isc/nonce.h>
#include <isc/random.h>
#include <isc/sha1.h>
#include <isc/mem.h>
#include <isc/safe.h>
#include <isc/string.h>
#include <isc/util.h>
#include <pk11/site.h>
#include <dst/result.h>
#include "dst_internal.h"
#ifdef HAVE_FIPS_MODE
#include "dst_openssl.h" /* FIPS_mode() prototype */
#endif
#include "dst_parse.h"
#ifndef PK11_MD5_DISABLE
static isc_result_t hmacmd5_fromdns(dst_key_t *key, isc_buffer_t *data);
struct dst_hmacmd5_key {
unsigned char key[ISC_MD5_BLOCK_LENGTH];
};
#endif
static isc_result_t
getkeybits(dst_key_t *key, struct dst_private_element *element) {
if (element->length != 2)
return (DST_R_INVALIDPRIVATEKEY);
key->key_bits = (element->data[0] << 8) + element->data[1];
return (ISC_R_SUCCESS);
}
#ifndef PK11_MD5_DISABLE
static isc_result_t
hmacmd5_createctx(dst_key_t *key, dst_context_t *dctx) {
isc_hmacmd5_t *hmacmd5ctx;
dst_hmacmd5_key_t *hkey = key->keydata.hmacmd5;
hmacmd5ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacmd5_t));
if (hmacmd5ctx == NULL)
return (ISC_R_NOMEMORY);
isc_hmacmd5_init(hmacmd5ctx, hkey->key, ISC_MD5_BLOCK_LENGTH);
dctx->ctxdata.hmacmd5ctx = hmacmd5ctx;
return (ISC_R_SUCCESS);
}
static void
hmacmd5_destroyctx(dst_context_t *dctx) {
isc_hmacmd5_t *hmacmd5ctx = dctx->ctxdata.hmacmd5ctx;
if (hmacmd5ctx != NULL) {
isc_hmacmd5_invalidate(hmacmd5ctx);
isc_mem_put(dctx->mctx, hmacmd5ctx, sizeof(isc_hmacmd5_t));
dctx->ctxdata.hmacmd5ctx = NULL;
}
}
static isc_result_t
hmacmd5_adddata(dst_context_t *dctx, const isc_region_t *data) {
isc_hmacmd5_t *hmacmd5ctx = dctx->ctxdata.hmacmd5ctx;
isc_hmacmd5_update(hmacmd5ctx, data->base, data->length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacmd5_sign(dst_context_t *dctx, isc_buffer_t *sig) {
isc_hmacmd5_t *hmacmd5ctx = dctx->ctxdata.hmacmd5ctx;
unsigned char *digest;
if (isc_buffer_availablelength(sig) < ISC_MD5_DIGESTLENGTH)
return (ISC_R_NOSPACE);
digest = isc_buffer_used(sig);
isc_hmacmd5_sign(hmacmd5ctx, digest);
isc_buffer_add(sig, ISC_MD5_DIGESTLENGTH);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacmd5_verify(dst_context_t *dctx, const isc_region_t *sig) {
isc_hmacmd5_t *hmacmd5ctx = dctx->ctxdata.hmacmd5ctx;
if (sig->length > ISC_MD5_DIGESTLENGTH)
return (DST_R_VERIFYFAILURE);
if (isc_hmacmd5_verify2(hmacmd5ctx, sig->base, sig->length))
return (ISC_R_SUCCESS);
else
return (DST_R_VERIFYFAILURE);
}
static isc_boolean_t
hmacmd5_compare(const dst_key_t *key1, const dst_key_t *key2) {
dst_hmacmd5_key_t *hkey1, *hkey2;
hkey1 = key1->keydata.hmacmd5;
hkey2 = key2->keydata.hmacmd5;
if (hkey1 == NULL && hkey2 == NULL)
return (ISC_TRUE);
else if (hkey1 == NULL || hkey2 == NULL)
return (ISC_FALSE);
if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_MD5_BLOCK_LENGTH))
return (ISC_TRUE);
else
return (ISC_FALSE);
}
static isc_result_t
hmacmd5_generate(dst_key_t *key, int pseudorandom_ok, void (*callback)(int))
{
isc_buffer_t b;
isc_result_t ret;
unsigned int bytes;
unsigned char data[ISC_MD5_BLOCK_LENGTH];
UNUSED(pseudorandom_ok);
UNUSED(callback);
bytes = (key->key_size + 7) / 8;
if (bytes > ISC_MD5_BLOCK_LENGTH) {
bytes = ISC_MD5_BLOCK_LENGTH;
key->key_size = ISC_MD5_BLOCK_LENGTH * 8;
}
memset(data, 0, ISC_MD5_BLOCK_LENGTH);
isc_nonce_buf(data, bytes);
isc_buffer_init(&b, data, bytes);
isc_buffer_add(&b, bytes);
ret = hmacmd5_fromdns(key, &b);
isc_safe_memwipe(data, sizeof(data));
return (ret);
}
static isc_boolean_t
hmacmd5_isprivate(const dst_key_t *key) {
UNUSED(key);
return (ISC_TRUE);
}
static void
hmacmd5_destroy(dst_key_t *key) {
dst_hmacmd5_key_t *hkey = key->keydata.hmacmd5;
isc_safe_memwipe(hkey, sizeof(*hkey));
isc_mem_put(key->mctx, hkey, sizeof(*hkey));
key->keydata.hmacmd5 = NULL;
}
static isc_result_t
hmacmd5_todns(const dst_key_t *key, isc_buffer_t *data) {
dst_hmacmd5_key_t *hkey;
unsigned int bytes;
REQUIRE(key->keydata.hmacmd5 != NULL);
hkey = key->keydata.hmacmd5;
bytes = (key->key_size + 7) / 8;
if (isc_buffer_availablelength(data) < bytes)
return (ISC_R_NOSPACE);
isc_buffer_putmem(data, hkey->key, bytes);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacmd5_fromdns(dst_key_t *key, isc_buffer_t *data) {
dst_hmacmd5_key_t *hkey;
int keylen;
isc_region_t r;
isc_md5_t md5ctx;
isc_buffer_remainingregion(data, &r);
if (r.length == 0)
return (ISC_R_SUCCESS);
hkey = isc_mem_get(key->mctx, sizeof(dst_hmacmd5_key_t));
if (hkey == NULL)
return (ISC_R_NOMEMORY);
memset(hkey->key, 0, sizeof(hkey->key));
if (r.length > ISC_MD5_BLOCK_LENGTH) {
isc_md5_init(&md5ctx);
isc_md5_update(&md5ctx, r.base, r.length);
isc_md5_final(&md5ctx, hkey->key);
keylen = ISC_MD5_DIGESTLENGTH;
} else {
memmove(hkey->key, r.base, r.length);
keylen = r.length;
}
key->key_size = keylen * 8;
key->keydata.hmacmd5 = hkey;
isc_buffer_forward(data, r.length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacmd5_tofile(const dst_key_t *key, const char *directory) {
int cnt = 0;
dst_hmacmd5_key_t *hkey;
dst_private_t priv;
int bytes = (key->key_size + 7) / 8;
unsigned char buf[2];
if (key->keydata.hmacmd5 == NULL)
return (DST_R_NULLKEY);
if (key->external)
return (DST_R_EXTERNALKEY);
hkey = key->keydata.hmacmd5;
priv.elements[cnt].tag = TAG_HMACMD5_KEY;
priv.elements[cnt].length = bytes;
priv.elements[cnt++].data = hkey->key;
buf[0] = (key->key_bits >> 8) & 0xffU;
buf[1] = key->key_bits & 0xffU;
priv.elements[cnt].tag = TAG_HMACMD5_BITS;
priv.elements[cnt].data = buf;
priv.elements[cnt++].length = 2;
priv.nelements = cnt;
return (dst__privstruct_writefile(key, &priv, directory));
}
static isc_result_t
hmacmd5_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
dst_private_t priv;
isc_result_t result, tresult;
isc_buffer_t b;
isc_mem_t *mctx = key->mctx;
unsigned int i;
UNUSED(pub);
/* read private key file */
result = dst__privstruct_parse(key, DST_ALG_HMACMD5, lexer, mctx,
&priv);
if (result != ISC_R_SUCCESS)
return (result);
if (key->external)
result = DST_R_EXTERNALKEY;
key->key_bits = 0;
for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
switch (priv.elements[i].tag) {
case TAG_HMACMD5_KEY:
isc_buffer_init(&b, priv.elements[i].data,
priv.elements[i].length);
isc_buffer_add(&b, priv.elements[i].length);
tresult = hmacmd5_fromdns(key, &b);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
case TAG_HMACMD5_BITS:
tresult = getkeybits(key, &priv.elements[i]);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
default:
result = DST_R_INVALIDPRIVATEKEY;
break;
}
}
dst__privstruct_free(&priv, mctx);
isc_safe_memwipe(&priv, sizeof(priv));
return (result);
}
static dst_func_t hmacmd5_functions = {
hmacmd5_createctx,
NULL, /*%< createctx2 */
hmacmd5_destroyctx,
hmacmd5_adddata,
hmacmd5_sign,
hmacmd5_verify,
NULL, /*%< verify2 */
NULL, /*%< computesecret */
hmacmd5_compare,
NULL, /*%< paramcompare */
hmacmd5_generate,
hmacmd5_isprivate,
hmacmd5_destroy,
hmacmd5_todns,
hmacmd5_fromdns,
hmacmd5_tofile,
hmacmd5_parse,
NULL, /*%< cleanup */
NULL, /*%< fromlabel */
NULL, /*%< dump */
NULL, /*%< restore */
};
isc_result_t
dst__hmacmd5_init(dst_func_t **funcp) {
#ifdef HAVE_FIPS_MODE
/*
* Problems from OpenSSL are likely from FIPS mode
*/
int fips_mode = FIPS_mode();
if (fips_mode != 0) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"FIPS mode is %d: MD5 is only supported "
"if the value is 0.\n"
"Please disable either FIPS mode or MD5.",
fips_mode);
}
#endif
/*
* Prevent use of incorrect crypto
*/
RUNTIME_CHECK(isc_md5_check(ISC_FALSE));
RUNTIME_CHECK(isc_hmacmd5_check(0));
REQUIRE(funcp != NULL);
if (*funcp == NULL)
*funcp = &hmacmd5_functions;
return (ISC_R_SUCCESS);
}
#endif
static isc_result_t hmacsha1_fromdns(dst_key_t *key, isc_buffer_t *data);
struct dst_hmacsha1_key {
unsigned char key[ISC_SHA1_BLOCK_LENGTH];
};
static isc_result_t
hmacsha1_createctx(dst_key_t *key, dst_context_t *dctx) {
isc_hmacsha1_t *hmacsha1ctx;
dst_hmacsha1_key_t *hkey = key->keydata.hmacsha1;
hmacsha1ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacsha1_t));
if (hmacsha1ctx == NULL)
return (ISC_R_NOMEMORY);
isc_hmacsha1_init(hmacsha1ctx, hkey->key, ISC_SHA1_BLOCK_LENGTH);
dctx->ctxdata.hmacsha1ctx = hmacsha1ctx;
return (ISC_R_SUCCESS);
}
static void
hmacsha1_destroyctx(dst_context_t *dctx) {
isc_hmacsha1_t *hmacsha1ctx = dctx->ctxdata.hmacsha1ctx;
if (hmacsha1ctx != NULL) {
isc_hmacsha1_invalidate(hmacsha1ctx);
isc_mem_put(dctx->mctx, hmacsha1ctx, sizeof(isc_hmacsha1_t));
dctx->ctxdata.hmacsha1ctx = NULL;
}
}
static isc_result_t
hmacsha1_adddata(dst_context_t *dctx, const isc_region_t *data) {
isc_hmacsha1_t *hmacsha1ctx = dctx->ctxdata.hmacsha1ctx;
isc_hmacsha1_update(hmacsha1ctx, data->base, data->length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha1_sign(dst_context_t *dctx, isc_buffer_t *sig) {
isc_hmacsha1_t *hmacsha1ctx = dctx->ctxdata.hmacsha1ctx;
unsigned char *digest;
if (isc_buffer_availablelength(sig) < ISC_SHA1_DIGESTLENGTH)
return (ISC_R_NOSPACE);
digest = isc_buffer_used(sig);
isc_hmacsha1_sign(hmacsha1ctx, digest, ISC_SHA1_DIGESTLENGTH);
isc_buffer_add(sig, ISC_SHA1_DIGESTLENGTH);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha1_verify(dst_context_t *dctx, const isc_region_t *sig) {
isc_hmacsha1_t *hmacsha1ctx = dctx->ctxdata.hmacsha1ctx;
if (sig->length > ISC_SHA1_DIGESTLENGTH || sig->length == 0)
return (DST_R_VERIFYFAILURE);
if (isc_hmacsha1_verify(hmacsha1ctx, sig->base, sig->length))
return (ISC_R_SUCCESS);
else
return (DST_R_VERIFYFAILURE);
}
static isc_boolean_t
hmacsha1_compare(const dst_key_t *key1, const dst_key_t *key2) {
dst_hmacsha1_key_t *hkey1, *hkey2;
hkey1 = key1->keydata.hmacsha1;
hkey2 = key2->keydata.hmacsha1;
if (hkey1 == NULL && hkey2 == NULL)
return (ISC_TRUE);
else if (hkey1 == NULL || hkey2 == NULL)
return (ISC_FALSE);
if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_SHA1_BLOCK_LENGTH))
return (ISC_TRUE);
else
return (ISC_FALSE);
}
static isc_result_t
hmacsha1_generate(dst_key_t *key, int pseudorandom_ok, void (*callback)(int))
{
isc_buffer_t b;
isc_result_t ret;
unsigned int bytes;
unsigned char data[ISC_SHA1_BLOCK_LENGTH];
UNUSED(pseudorandom_ok);
UNUSED(callback);
bytes = (key->key_size + 7) / 8;
if (bytes > ISC_SHA1_BLOCK_LENGTH) {
bytes = ISC_SHA1_BLOCK_LENGTH;
key->key_size = ISC_SHA1_BLOCK_LENGTH * 8;
}
memset(data, 0, ISC_SHA1_BLOCK_LENGTH);
isc_nonce_buf(data, bytes);
isc_buffer_init(&b, data, bytes);
isc_buffer_add(&b, bytes);
ret = hmacsha1_fromdns(key, &b);
isc_safe_memwipe(data, sizeof(data));
return (ret);
}
static isc_boolean_t
hmacsha1_isprivate(const dst_key_t *key) {
UNUSED(key);
return (ISC_TRUE);
}
static void
hmacsha1_destroy(dst_key_t *key) {
dst_hmacsha1_key_t *hkey = key->keydata.hmacsha1;
isc_safe_memwipe(hkey, sizeof(*hkey));
isc_mem_put(key->mctx, hkey, sizeof(*hkey));
key->keydata.hmacsha1 = NULL;
}
static isc_result_t
hmacsha1_todns(const dst_key_t *key, isc_buffer_t *data) {
dst_hmacsha1_key_t *hkey;
unsigned int bytes;
REQUIRE(key->keydata.hmacsha1 != NULL);
hkey = key->keydata.hmacsha1;
bytes = (key->key_size + 7) / 8;
if (isc_buffer_availablelength(data) < bytes)
return (ISC_R_NOSPACE);
isc_buffer_putmem(data, hkey->key, bytes);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha1_fromdns(dst_key_t *key, isc_buffer_t *data) {
dst_hmacsha1_key_t *hkey;
int keylen;
isc_region_t r;
isc_sha1_t sha1ctx;
isc_buffer_remainingregion(data, &r);
if (r.length == 0)
return (ISC_R_SUCCESS);
hkey = isc_mem_get(key->mctx, sizeof(dst_hmacsha1_key_t));
if (hkey == NULL)
return (ISC_R_NOMEMORY);
memset(hkey->key, 0, sizeof(hkey->key));
if (r.length > ISC_SHA1_BLOCK_LENGTH) {
isc_sha1_init(&sha1ctx);
isc_sha1_update(&sha1ctx, r.base, r.length);
isc_sha1_final(&sha1ctx, hkey->key);
keylen = ISC_SHA1_DIGESTLENGTH;
} else {
memmove(hkey->key, r.base, r.length);
keylen = r.length;
}
key->key_size = keylen * 8;
key->keydata.hmacsha1 = hkey;
isc_buffer_forward(data, r.length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha1_tofile(const dst_key_t *key, const char *directory) {
int cnt = 0;
dst_hmacsha1_key_t *hkey;
dst_private_t priv;
int bytes = (key->key_size + 7) / 8;
unsigned char buf[2];
if (key->keydata.hmacsha1 == NULL)
return (DST_R_NULLKEY);
if (key->external)
return (DST_R_EXTERNALKEY);
hkey = key->keydata.hmacsha1;
priv.elements[cnt].tag = TAG_HMACSHA1_KEY;
priv.elements[cnt].length = bytes;
priv.elements[cnt++].data = hkey->key;
buf[0] = (key->key_bits >> 8) & 0xffU;
buf[1] = key->key_bits & 0xffU;
priv.elements[cnt].tag = TAG_HMACSHA1_BITS;
priv.elements[cnt].data = buf;
priv.elements[cnt++].length = 2;
priv.nelements = cnt;
return (dst__privstruct_writefile(key, &priv, directory));
}
static isc_result_t
hmacsha1_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
dst_private_t priv;
isc_result_t result, tresult;
isc_buffer_t b;
isc_mem_t *mctx = key->mctx;
unsigned int i;
UNUSED(pub);
/* read private key file */
result = dst__privstruct_parse(key, DST_ALG_HMACSHA1, lexer, mctx,
&priv);
if (result != ISC_R_SUCCESS)
return (result);
if (key->external)
result = DST_R_EXTERNALKEY;
key->key_bits = 0;
for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
switch (priv.elements[i].tag) {
case TAG_HMACSHA1_KEY:
isc_buffer_init(&b, priv.elements[i].data,
priv.elements[i].length);
isc_buffer_add(&b, priv.elements[i].length);
tresult = hmacsha1_fromdns(key, &b);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
case TAG_HMACSHA1_BITS:
tresult = getkeybits(key, &priv.elements[i]);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
default:
result = DST_R_INVALIDPRIVATEKEY;
break;
}
}
dst__privstruct_free(&priv, mctx);
isc_safe_memwipe(&priv, sizeof(priv));
return (result);
}
static dst_func_t hmacsha1_functions = {
hmacsha1_createctx,
NULL, /*%< createctx2 */
hmacsha1_destroyctx,
hmacsha1_adddata,
hmacsha1_sign,
hmacsha1_verify,
NULL, /* verify2 */
NULL, /* computesecret */
hmacsha1_compare,
NULL, /* paramcompare */
hmacsha1_generate,
hmacsha1_isprivate,
hmacsha1_destroy,
hmacsha1_todns,
hmacsha1_fromdns,
hmacsha1_tofile,
hmacsha1_parse,
NULL, /* cleanup */
NULL, /* fromlabel */
NULL, /* dump */
NULL, /* restore */
};
isc_result_t
dst__hmacsha1_init(dst_func_t **funcp) {
/*
* Prevent use of incorrect crypto
*/
RUNTIME_CHECK(isc_sha1_check(ISC_FALSE));
RUNTIME_CHECK(isc_hmacsha1_check(0));
REQUIRE(funcp != NULL);
if (*funcp == NULL)
*funcp = &hmacsha1_functions;
return (ISC_R_SUCCESS);
}
static isc_result_t hmacsha224_fromdns(dst_key_t *key, isc_buffer_t *data);
struct dst_hmacsha224_key {
unsigned char key[ISC_SHA224_BLOCK_LENGTH];
};
static isc_result_t
hmacsha224_createctx(dst_key_t *key, dst_context_t *dctx) {
isc_hmacsha224_t *hmacsha224ctx;
dst_hmacsha224_key_t *hkey = key->keydata.hmacsha224;
hmacsha224ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacsha224_t));
if (hmacsha224ctx == NULL)
return (ISC_R_NOMEMORY);
isc_hmacsha224_init(hmacsha224ctx, hkey->key, ISC_SHA224_BLOCK_LENGTH);
dctx->ctxdata.hmacsha224ctx = hmacsha224ctx;
return (ISC_R_SUCCESS);
}
static void
hmacsha224_destroyctx(dst_context_t *dctx) {
isc_hmacsha224_t *hmacsha224ctx = dctx->ctxdata.hmacsha224ctx;
if (hmacsha224ctx != NULL) {
isc_hmacsha224_invalidate(hmacsha224ctx);
isc_mem_put(dctx->mctx, hmacsha224ctx, sizeof(isc_hmacsha224_t));
dctx->ctxdata.hmacsha224ctx = NULL;
}
}
static isc_result_t
hmacsha224_adddata(dst_context_t *dctx, const isc_region_t *data) {
isc_hmacsha224_t *hmacsha224ctx = dctx->ctxdata.hmacsha224ctx;
isc_hmacsha224_update(hmacsha224ctx, data->base, data->length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha224_sign(dst_context_t *dctx, isc_buffer_t *sig) {
isc_hmacsha224_t *hmacsha224ctx = dctx->ctxdata.hmacsha224ctx;
unsigned char *digest;
if (isc_buffer_availablelength(sig) < ISC_SHA224_DIGESTLENGTH)
return (ISC_R_NOSPACE);
digest = isc_buffer_used(sig);
isc_hmacsha224_sign(hmacsha224ctx, digest, ISC_SHA224_DIGESTLENGTH);
isc_buffer_add(sig, ISC_SHA224_DIGESTLENGTH);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha224_verify(dst_context_t *dctx, const isc_region_t *sig) {
isc_hmacsha224_t *hmacsha224ctx = dctx->ctxdata.hmacsha224ctx;
if (sig->length > ISC_SHA224_DIGESTLENGTH || sig->length == 0)
return (DST_R_VERIFYFAILURE);
if (isc_hmacsha224_verify(hmacsha224ctx, sig->base, sig->length))
return (ISC_R_SUCCESS);
else
return (DST_R_VERIFYFAILURE);
}
static isc_boolean_t
hmacsha224_compare(const dst_key_t *key1, const dst_key_t *key2) {
dst_hmacsha224_key_t *hkey1, *hkey2;
hkey1 = key1->keydata.hmacsha224;
hkey2 = key2->keydata.hmacsha224;
if (hkey1 == NULL && hkey2 == NULL)
return (ISC_TRUE);
else if (hkey1 == NULL || hkey2 == NULL)
return (ISC_FALSE);
if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_SHA224_BLOCK_LENGTH))
return (ISC_TRUE);
else
return (ISC_FALSE);
}
static isc_result_t
hmacsha224_generate(dst_key_t *key, int pseudorandom_ok, void (*callback)(int))
{
isc_buffer_t b;
isc_result_t ret;
unsigned int bytes;
unsigned char data[ISC_SHA224_BLOCK_LENGTH];
UNUSED(pseudorandom_ok);
UNUSED(callback);
bytes = (key->key_size + 7) / 8;
if (bytes > ISC_SHA224_BLOCK_LENGTH) {
bytes = ISC_SHA224_BLOCK_LENGTH;
key->key_size = ISC_SHA224_BLOCK_LENGTH * 8;
}
memset(data, 0, ISC_SHA224_BLOCK_LENGTH);
isc_nonce_buf(data, bytes);
isc_buffer_init(&b, data, bytes);
isc_buffer_add(&b, bytes);
ret = hmacsha224_fromdns(key, &b);
isc_safe_memwipe(data, sizeof(data));
return (ret);
}
static isc_boolean_t
hmacsha224_isprivate(const dst_key_t *key) {
UNUSED(key);
return (ISC_TRUE);
}
static void
hmacsha224_destroy(dst_key_t *key) {
dst_hmacsha224_key_t *hkey = key->keydata.hmacsha224;
isc_safe_memwipe(hkey, sizeof(*hkey));
isc_mem_put(key->mctx, hkey, sizeof(*hkey));
key->keydata.hmacsha224 = NULL;
}
static isc_result_t
hmacsha224_todns(const dst_key_t *key, isc_buffer_t *data) {
dst_hmacsha224_key_t *hkey;
unsigned int bytes;
REQUIRE(key->keydata.hmacsha224 != NULL);
hkey = key->keydata.hmacsha224;
bytes = (key->key_size + 7) / 8;
if (isc_buffer_availablelength(data) < bytes)
return (ISC_R_NOSPACE);
isc_buffer_putmem(data, hkey->key, bytes);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha224_fromdns(dst_key_t *key, isc_buffer_t *data) {
dst_hmacsha224_key_t *hkey;
int keylen;
isc_region_t r;
isc_sha224_t sha224ctx;
isc_buffer_remainingregion(data, &r);
if (r.length == 0)
return (ISC_R_SUCCESS);
hkey = isc_mem_get(key->mctx, sizeof(dst_hmacsha224_key_t));
if (hkey == NULL)
return (ISC_R_NOMEMORY);
memset(hkey->key, 0, sizeof(hkey->key));
if (r.length > ISC_SHA224_BLOCK_LENGTH) {
isc_sha224_init(&sha224ctx);
isc_sha224_update(&sha224ctx, r.base, r.length);
isc_sha224_final(hkey->key, &sha224ctx);
keylen = ISC_SHA224_DIGESTLENGTH;
} else {
memmove(hkey->key, r.base, r.length);
keylen = r.length;
}
key->key_size = keylen * 8;
key->keydata.hmacsha224 = hkey;
isc_buffer_forward(data, r.length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha224_tofile(const dst_key_t *key, const char *directory) {
int cnt = 0;
dst_hmacsha224_key_t *hkey;
dst_private_t priv;
int bytes = (key->key_size + 7) / 8;
unsigned char buf[2];
if (key->keydata.hmacsha224 == NULL)
return (DST_R_NULLKEY);
if (key->external)
return (DST_R_EXTERNALKEY);
hkey = key->keydata.hmacsha224;
priv.elements[cnt].tag = TAG_HMACSHA224_KEY;
priv.elements[cnt].length = bytes;
priv.elements[cnt++].data = hkey->key;
buf[0] = (key->key_bits >> 8) & 0xffU;
buf[1] = key->key_bits & 0xffU;
priv.elements[cnt].tag = TAG_HMACSHA224_BITS;
priv.elements[cnt].data = buf;
priv.elements[cnt++].length = 2;
priv.nelements = cnt;
return (dst__privstruct_writefile(key, &priv, directory));
}
static isc_result_t
hmacsha224_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
dst_private_t priv;
isc_result_t result, tresult;
isc_buffer_t b;
isc_mem_t *mctx = key->mctx;
unsigned int i;
UNUSED(pub);
/* read private key file */
result = dst__privstruct_parse(key, DST_ALG_HMACSHA224, lexer, mctx,
&priv);
if (result != ISC_R_SUCCESS)
return (result);
if (key->external)
result = DST_R_EXTERNALKEY;
key->key_bits = 0;
for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
switch (priv.elements[i].tag) {
case TAG_HMACSHA224_KEY:
isc_buffer_init(&b, priv.elements[i].data,
priv.elements[i].length);
isc_buffer_add(&b, priv.elements[i].length);
tresult = hmacsha224_fromdns(key, &b);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
case TAG_HMACSHA224_BITS:
tresult = getkeybits(key, &priv.elements[i]);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
default:
result = DST_R_INVALIDPRIVATEKEY;
break;
}
}
dst__privstruct_free(&priv, mctx);
isc_safe_memwipe(&priv, sizeof(priv));
return (result);
}
static dst_func_t hmacsha224_functions = {
hmacsha224_createctx,
NULL, /*%< createctx2 */
hmacsha224_destroyctx,
hmacsha224_adddata,
hmacsha224_sign,
hmacsha224_verify,
NULL, /* verify2 */
NULL, /* computesecret */
hmacsha224_compare,
NULL, /* paramcompare */
hmacsha224_generate,
hmacsha224_isprivate,
hmacsha224_destroy,
hmacsha224_todns,
hmacsha224_fromdns,
hmacsha224_tofile,
hmacsha224_parse,
NULL, /* cleanup */
NULL, /* fromlabel */
NULL, /* dump */
NULL, /* restore */
};
isc_result_t
dst__hmacsha224_init(dst_func_t **funcp) {
REQUIRE(funcp != NULL);
if (*funcp == NULL)
*funcp = &hmacsha224_functions;
return (ISC_R_SUCCESS);
}
static isc_result_t hmacsha256_fromdns(dst_key_t *key, isc_buffer_t *data);
struct dst_hmacsha256_key {
unsigned char key[ISC_SHA256_BLOCK_LENGTH];
};
static isc_result_t
hmacsha256_createctx(dst_key_t *key, dst_context_t *dctx) {
isc_hmacsha256_t *hmacsha256ctx;
dst_hmacsha256_key_t *hkey = key->keydata.hmacsha256;
hmacsha256ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacsha256_t));
if (hmacsha256ctx == NULL)
return (ISC_R_NOMEMORY);
isc_hmacsha256_init(hmacsha256ctx, hkey->key, ISC_SHA256_BLOCK_LENGTH);
dctx->ctxdata.hmacsha256ctx = hmacsha256ctx;
return (ISC_R_SUCCESS);
}
static void
hmacsha256_destroyctx(dst_context_t *dctx) {
isc_hmacsha256_t *hmacsha256ctx = dctx->ctxdata.hmacsha256ctx;
if (hmacsha256ctx != NULL) {
isc_hmacsha256_invalidate(hmacsha256ctx);
isc_mem_put(dctx->mctx, hmacsha256ctx, sizeof(isc_hmacsha256_t));
dctx->ctxdata.hmacsha256ctx = NULL;
}
}
static isc_result_t
hmacsha256_adddata(dst_context_t *dctx, const isc_region_t *data) {
isc_hmacsha256_t *hmacsha256ctx = dctx->ctxdata.hmacsha256ctx;
isc_hmacsha256_update(hmacsha256ctx, data->base, data->length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha256_sign(dst_context_t *dctx, isc_buffer_t *sig) {
isc_hmacsha256_t *hmacsha256ctx = dctx->ctxdata.hmacsha256ctx;
unsigned char *digest;
if (isc_buffer_availablelength(sig) < ISC_SHA256_DIGESTLENGTH)
return (ISC_R_NOSPACE);
digest = isc_buffer_used(sig);
isc_hmacsha256_sign(hmacsha256ctx, digest, ISC_SHA256_DIGESTLENGTH);
isc_buffer_add(sig, ISC_SHA256_DIGESTLENGTH);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha256_verify(dst_context_t *dctx, const isc_region_t *sig) {
isc_hmacsha256_t *hmacsha256ctx = dctx->ctxdata.hmacsha256ctx;
if (sig->length > ISC_SHA256_DIGESTLENGTH || sig->length == 0)
return (DST_R_VERIFYFAILURE);
if (isc_hmacsha256_verify(hmacsha256ctx, sig->base, sig->length))
return (ISC_R_SUCCESS);
else
return (DST_R_VERIFYFAILURE);
}
static isc_boolean_t
hmacsha256_compare(const dst_key_t *key1, const dst_key_t *key2) {
dst_hmacsha256_key_t *hkey1, *hkey2;
hkey1 = key1->keydata.hmacsha256;
hkey2 = key2->keydata.hmacsha256;
if (hkey1 == NULL && hkey2 == NULL)
return (ISC_TRUE);
else if (hkey1 == NULL || hkey2 == NULL)
return (ISC_FALSE);
if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_SHA256_BLOCK_LENGTH))
return (ISC_TRUE);
else
return (ISC_FALSE);
}
static isc_result_t
hmacsha256_generate(dst_key_t *key, int pseudorandom_ok, void (*callback)(int))
{
isc_buffer_t b;
isc_result_t ret;
unsigned int bytes;
unsigned char data[ISC_SHA256_BLOCK_LENGTH];
UNUSED(pseudorandom_ok);
UNUSED(callback);
bytes = (key->key_size + 7) / 8;
if (bytes > ISC_SHA256_BLOCK_LENGTH) {
bytes = ISC_SHA256_BLOCK_LENGTH;
key->key_size = ISC_SHA256_BLOCK_LENGTH * 8;
}
memset(data, 0, ISC_SHA256_BLOCK_LENGTH);
isc_nonce_buf(data, bytes);
isc_buffer_init(&b, data, bytes);
isc_buffer_add(&b, bytes);
ret = hmacsha256_fromdns(key, &b);
isc_safe_memwipe(data, sizeof(data));
return (ret);
}
static isc_boolean_t
hmacsha256_isprivate(const dst_key_t *key) {
UNUSED(key);
return (ISC_TRUE);
}
static void
hmacsha256_destroy(dst_key_t *key) {
dst_hmacsha256_key_t *hkey = key->keydata.hmacsha256;
isc_safe_memwipe(hkey, sizeof(*hkey));
isc_mem_put(key->mctx, hkey, sizeof(*hkey));
key->keydata.hmacsha256 = NULL;
}
static isc_result_t
hmacsha256_todns(const dst_key_t *key, isc_buffer_t *data) {
dst_hmacsha256_key_t *hkey;
unsigned int bytes;
REQUIRE(key->keydata.hmacsha256 != NULL);
hkey = key->keydata.hmacsha256;
bytes = (key->key_size + 7) / 8;
if (isc_buffer_availablelength(data) < bytes)
return (ISC_R_NOSPACE);
isc_buffer_putmem(data, hkey->key, bytes);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha256_fromdns(dst_key_t *key, isc_buffer_t *data) {
dst_hmacsha256_key_t *hkey;
int keylen;
isc_region_t r;
isc_sha256_t sha256ctx;
isc_buffer_remainingregion(data, &r);
if (r.length == 0)
return (ISC_R_SUCCESS);
hkey = isc_mem_get(key->mctx, sizeof(dst_hmacsha256_key_t));
if (hkey == NULL)
return (ISC_R_NOMEMORY);
memset(hkey->key, 0, sizeof(hkey->key));
if (r.length > ISC_SHA256_BLOCK_LENGTH) {
isc_sha256_init(&sha256ctx);
isc_sha256_update(&sha256ctx, r.base, r.length);
isc_sha256_final(hkey->key, &sha256ctx);
keylen = ISC_SHA256_DIGESTLENGTH;
} else {
memmove(hkey->key, r.base, r.length);
keylen = r.length;
}
key->key_size = keylen * 8;
key->keydata.hmacsha256 = hkey;
isc_buffer_forward(data, r.length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha256_tofile(const dst_key_t *key, const char *directory) {
int cnt = 0;
dst_hmacsha256_key_t *hkey;
dst_private_t priv;
int bytes = (key->key_size + 7) / 8;
unsigned char buf[2];
if (key->keydata.hmacsha256 == NULL)
return (DST_R_NULLKEY);
if (key->external)
return (DST_R_EXTERNALKEY);
hkey = key->keydata.hmacsha256;
priv.elements[cnt].tag = TAG_HMACSHA256_KEY;
priv.elements[cnt].length = bytes;
priv.elements[cnt++].data = hkey->key;
buf[0] = (key->key_bits >> 8) & 0xffU;
buf[1] = key->key_bits & 0xffU;
priv.elements[cnt].tag = TAG_HMACSHA256_BITS;
priv.elements[cnt].data = buf;
priv.elements[cnt++].length = 2;
priv.nelements = cnt;
return (dst__privstruct_writefile(key, &priv, directory));
}
static isc_result_t
hmacsha256_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
dst_private_t priv;
isc_result_t result, tresult;
isc_buffer_t b;
isc_mem_t *mctx = key->mctx;
unsigned int i;
UNUSED(pub);
/* read private key file */
result = dst__privstruct_parse(key, DST_ALG_HMACSHA256, lexer, mctx,
&priv);
if (result != ISC_R_SUCCESS)
return (result);
if (key->external)
result = DST_R_EXTERNALKEY;
key->key_bits = 0;
for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
switch (priv.elements[i].tag) {
case TAG_HMACSHA256_KEY:
isc_buffer_init(&b, priv.elements[i].data,
priv.elements[i].length);
isc_buffer_add(&b, priv.elements[i].length);
tresult = hmacsha256_fromdns(key, &b);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
case TAG_HMACSHA256_BITS:
tresult = getkeybits(key, &priv.elements[i]);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
default:
result = DST_R_INVALIDPRIVATEKEY;
break;
}
}
dst__privstruct_free(&priv, mctx);
isc_safe_memwipe(&priv, sizeof(priv));
return (result);
}
static dst_func_t hmacsha256_functions = {
hmacsha256_createctx,
NULL, /*%< createctx2 */
hmacsha256_destroyctx,
hmacsha256_adddata,
hmacsha256_sign,
hmacsha256_verify,
NULL, /* verify2 */
NULL, /* computesecret */
hmacsha256_compare,
NULL, /* paramcompare */
hmacsha256_generate,
hmacsha256_isprivate,
hmacsha256_destroy,
hmacsha256_todns,
hmacsha256_fromdns,
hmacsha256_tofile,
hmacsha256_parse,
NULL, /* cleanup */
NULL, /* fromlabel */
NULL, /* dump */
NULL, /* restore */
};
isc_result_t
dst__hmacsha256_init(dst_func_t **funcp) {
REQUIRE(funcp != NULL);
if (*funcp == NULL)
*funcp = &hmacsha256_functions;
return (ISC_R_SUCCESS);
}
static isc_result_t hmacsha384_fromdns(dst_key_t *key, isc_buffer_t *data);
struct dst_hmacsha384_key {
unsigned char key[ISC_SHA384_BLOCK_LENGTH];
};
static isc_result_t
hmacsha384_createctx(dst_key_t *key, dst_context_t *dctx) {
isc_hmacsha384_t *hmacsha384ctx;
dst_hmacsha384_key_t *hkey = key->keydata.hmacsha384;
hmacsha384ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacsha384_t));
if (hmacsha384ctx == NULL)
return (ISC_R_NOMEMORY);
isc_hmacsha384_init(hmacsha384ctx, hkey->key, ISC_SHA384_BLOCK_LENGTH);
dctx->ctxdata.hmacsha384ctx = hmacsha384ctx;
return (ISC_R_SUCCESS);
}
static void
hmacsha384_destroyctx(dst_context_t *dctx) {
isc_hmacsha384_t *hmacsha384ctx = dctx->ctxdata.hmacsha384ctx;
if (hmacsha384ctx != NULL) {
isc_hmacsha384_invalidate(hmacsha384ctx);
isc_mem_put(dctx->mctx, hmacsha384ctx, sizeof(isc_hmacsha384_t));
dctx->ctxdata.hmacsha384ctx = NULL;
}
}
static isc_result_t
hmacsha384_adddata(dst_context_t *dctx, const isc_region_t *data) {
isc_hmacsha384_t *hmacsha384ctx = dctx->ctxdata.hmacsha384ctx;
isc_hmacsha384_update(hmacsha384ctx, data->base, data->length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha384_sign(dst_context_t *dctx, isc_buffer_t *sig) {
isc_hmacsha384_t *hmacsha384ctx = dctx->ctxdata.hmacsha384ctx;
unsigned char *digest;
if (isc_buffer_availablelength(sig) < ISC_SHA384_DIGESTLENGTH)
return (ISC_R_NOSPACE);
digest = isc_buffer_used(sig);
isc_hmacsha384_sign(hmacsha384ctx, digest, ISC_SHA384_DIGESTLENGTH);
isc_buffer_add(sig, ISC_SHA384_DIGESTLENGTH);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha384_verify(dst_context_t *dctx, const isc_region_t *sig) {
isc_hmacsha384_t *hmacsha384ctx = dctx->ctxdata.hmacsha384ctx;
if (sig->length > ISC_SHA384_DIGESTLENGTH || sig->length == 0)
return (DST_R_VERIFYFAILURE);
if (isc_hmacsha384_verify(hmacsha384ctx, sig->base, sig->length))
return (ISC_R_SUCCESS);
else
return (DST_R_VERIFYFAILURE);
}
static isc_boolean_t
hmacsha384_compare(const dst_key_t *key1, const dst_key_t *key2) {
dst_hmacsha384_key_t *hkey1, *hkey2;
hkey1 = key1->keydata.hmacsha384;
hkey2 = key2->keydata.hmacsha384;
if (hkey1 == NULL && hkey2 == NULL)
return (ISC_TRUE);
else if (hkey1 == NULL || hkey2 == NULL)
return (ISC_FALSE);
if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_SHA384_BLOCK_LENGTH))
return (ISC_TRUE);
else
return (ISC_FALSE);
}
static isc_result_t
hmacsha384_generate(dst_key_t *key, int pseudorandom_ok, void (*callback)(int))
{
isc_buffer_t b;
isc_result_t ret;
unsigned int bytes;
unsigned char data[ISC_SHA384_BLOCK_LENGTH];
UNUSED(pseudorandom_ok);
UNUSED(callback);
bytes = (key->key_size + 7) / 8;
if (bytes > ISC_SHA384_BLOCK_LENGTH) {
bytes = ISC_SHA384_BLOCK_LENGTH;
key->key_size = ISC_SHA384_BLOCK_LENGTH * 8;
}
memset(data, 0, ISC_SHA384_BLOCK_LENGTH);
isc_nonce_buf(data, bytes);
isc_buffer_init(&b, data, bytes);
isc_buffer_add(&b, bytes);
ret = hmacsha384_fromdns(key, &b);
isc_safe_memwipe(data, sizeof(data));
return (ret);
}
static isc_boolean_t
hmacsha384_isprivate(const dst_key_t *key) {
UNUSED(key);
return (ISC_TRUE);
}
static void
hmacsha384_destroy(dst_key_t *key) {
dst_hmacsha384_key_t *hkey = key->keydata.hmacsha384;
isc_safe_memwipe(hkey, sizeof(*hkey));
isc_mem_put(key->mctx, hkey, sizeof(*hkey));
key->keydata.hmacsha384 = NULL;
}
static isc_result_t
hmacsha384_todns(const dst_key_t *key, isc_buffer_t *data) {
dst_hmacsha384_key_t *hkey;
unsigned int bytes;
REQUIRE(key->keydata.hmacsha384 != NULL);
hkey = key->keydata.hmacsha384;
bytes = (key->key_size + 7) / 8;
if (isc_buffer_availablelength(data) < bytes)
return (ISC_R_NOSPACE);
isc_buffer_putmem(data, hkey->key, bytes);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha384_fromdns(dst_key_t *key, isc_buffer_t *data) {
dst_hmacsha384_key_t *hkey;
int keylen;
isc_region_t r;
isc_sha384_t sha384ctx;
isc_buffer_remainingregion(data, &r);
if (r.length == 0)
return (ISC_R_SUCCESS);
hkey = isc_mem_get(key->mctx, sizeof(dst_hmacsha384_key_t));
if (hkey == NULL)
return (ISC_R_NOMEMORY);
memset(hkey->key, 0, sizeof(hkey->key));
if (r.length > ISC_SHA384_BLOCK_LENGTH) {
isc_sha384_init(&sha384ctx);
isc_sha384_update(&sha384ctx, r.base, r.length);
isc_sha384_final(hkey->key, &sha384ctx);
keylen = ISC_SHA384_DIGESTLENGTH;
} else {
memmove(hkey->key, r.base, r.length);
keylen = r.length;
}
key->key_size = keylen * 8;
key->keydata.hmacsha384 = hkey;
isc_buffer_forward(data, r.length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha384_tofile(const dst_key_t *key, const char *directory) {
int cnt = 0;
dst_hmacsha384_key_t *hkey;
dst_private_t priv;
int bytes = (key->key_size + 7) / 8;
unsigned char buf[2];
if (key->keydata.hmacsha384 == NULL)
return (DST_R_NULLKEY);
if (key->external)
return (DST_R_EXTERNALKEY);
hkey = key->keydata.hmacsha384;
priv.elements[cnt].tag = TAG_HMACSHA384_KEY;
priv.elements[cnt].length = bytes;
priv.elements[cnt++].data = hkey->key;
buf[0] = (key->key_bits >> 8) & 0xffU;
buf[1] = key->key_bits & 0xffU;
priv.elements[cnt].tag = TAG_HMACSHA384_BITS;
priv.elements[cnt].data = buf;
priv.elements[cnt++].length = 2;
priv.nelements = cnt;
return (dst__privstruct_writefile(key, &priv, directory));
}
static isc_result_t
hmacsha384_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
dst_private_t priv;
isc_result_t result, tresult;
isc_buffer_t b;
isc_mem_t *mctx = key->mctx;
unsigned int i;
UNUSED(pub);
/* read private key file */
result = dst__privstruct_parse(key, DST_ALG_HMACSHA384, lexer, mctx,
&priv);
if (result != ISC_R_SUCCESS)
return (result);
if (key->external)
result = DST_R_EXTERNALKEY;
key->key_bits = 0;
for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
switch (priv.elements[i].tag) {
case TAG_HMACSHA384_KEY:
isc_buffer_init(&b, priv.elements[i].data,
priv.elements[i].length);
isc_buffer_add(&b, priv.elements[i].length);
tresult = hmacsha384_fromdns(key, &b);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
case TAG_HMACSHA384_BITS:
tresult = getkeybits(key, &priv.elements[i]);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
default:
result = DST_R_INVALIDPRIVATEKEY;
break;
}
}
dst__privstruct_free(&priv, mctx);
isc_safe_memwipe(&priv, sizeof(priv));
return (result);
}
static dst_func_t hmacsha384_functions = {
hmacsha384_createctx,
NULL, /*%< createctx2 */
hmacsha384_destroyctx,
hmacsha384_adddata,
hmacsha384_sign,
hmacsha384_verify,
NULL, /* verify2 */
NULL, /* computesecret */
hmacsha384_compare,
NULL, /* paramcompare */
hmacsha384_generate,
hmacsha384_isprivate,
hmacsha384_destroy,
hmacsha384_todns,
hmacsha384_fromdns,
hmacsha384_tofile,
hmacsha384_parse,
NULL, /* cleanup */
NULL, /* fromlabel */
NULL, /* dump */
NULL, /* restore */
};
isc_result_t
dst__hmacsha384_init(dst_func_t **funcp) {
REQUIRE(funcp != NULL);
if (*funcp == NULL)
*funcp = &hmacsha384_functions;
return (ISC_R_SUCCESS);
}
static isc_result_t hmacsha512_fromdns(dst_key_t *key, isc_buffer_t *data);
struct dst_hmacsha512_key {
unsigned char key[ISC_SHA512_BLOCK_LENGTH];
};
static isc_result_t
hmacsha512_createctx(dst_key_t *key, dst_context_t *dctx) {
isc_hmacsha512_t *hmacsha512ctx;
dst_hmacsha512_key_t *hkey = key->keydata.hmacsha512;
hmacsha512ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacsha512_t));
if (hmacsha512ctx == NULL)
return (ISC_R_NOMEMORY);
isc_hmacsha512_init(hmacsha512ctx, hkey->key, ISC_SHA512_BLOCK_LENGTH);
dctx->ctxdata.hmacsha512ctx = hmacsha512ctx;
return (ISC_R_SUCCESS);
}
static void
hmacsha512_destroyctx(dst_context_t *dctx) {
isc_hmacsha512_t *hmacsha512ctx = dctx->ctxdata.hmacsha512ctx;
if (hmacsha512ctx != NULL) {
isc_hmacsha512_invalidate(hmacsha512ctx);
isc_mem_put(dctx->mctx, hmacsha512ctx, sizeof(isc_hmacsha512_t));
dctx->ctxdata.hmacsha512ctx = NULL;
}
}
static isc_result_t
hmacsha512_adddata(dst_context_t *dctx, const isc_region_t *data) {
isc_hmacsha512_t *hmacsha512ctx = dctx->ctxdata.hmacsha512ctx;
isc_hmacsha512_update(hmacsha512ctx, data->base, data->length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha512_sign(dst_context_t *dctx, isc_buffer_t *sig) {
isc_hmacsha512_t *hmacsha512ctx = dctx->ctxdata.hmacsha512ctx;
unsigned char *digest;
if (isc_buffer_availablelength(sig) < ISC_SHA512_DIGESTLENGTH)
return (ISC_R_NOSPACE);
digest = isc_buffer_used(sig);
isc_hmacsha512_sign(hmacsha512ctx, digest, ISC_SHA512_DIGESTLENGTH);
isc_buffer_add(sig, ISC_SHA512_DIGESTLENGTH);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha512_verify(dst_context_t *dctx, const isc_region_t *sig) {
isc_hmacsha512_t *hmacsha512ctx = dctx->ctxdata.hmacsha512ctx;
if (sig->length > ISC_SHA512_DIGESTLENGTH || sig->length == 0)
return (DST_R_VERIFYFAILURE);
if (isc_hmacsha512_verify(hmacsha512ctx, sig->base, sig->length))
return (ISC_R_SUCCESS);
else
return (DST_R_VERIFYFAILURE);
}
static isc_boolean_t
hmacsha512_compare(const dst_key_t *key1, const dst_key_t *key2) {
dst_hmacsha512_key_t *hkey1, *hkey2;
hkey1 = key1->keydata.hmacsha512;
hkey2 = key2->keydata.hmacsha512;
if (hkey1 == NULL && hkey2 == NULL)
return (ISC_TRUE);
else if (hkey1 == NULL || hkey2 == NULL)
return (ISC_FALSE);
if (isc_safe_memequal(hkey1->key, hkey2->key, ISC_SHA512_BLOCK_LENGTH))
return (ISC_TRUE);
else
return (ISC_FALSE);
}
static isc_result_t
hmacsha512_generate(dst_key_t *key, int pseudorandom_ok, void (*callback)(int))
{
isc_buffer_t b;
isc_result_t ret;
unsigned int bytes;
unsigned char data[ISC_SHA512_BLOCK_LENGTH];
UNUSED(pseudorandom_ok);
UNUSED(callback);
bytes = (key->key_size + 7) / 8;
if (bytes > ISC_SHA512_BLOCK_LENGTH) {
bytes = ISC_SHA512_BLOCK_LENGTH;
key->key_size = ISC_SHA512_BLOCK_LENGTH * 8;
}
memset(data, 0, ISC_SHA512_BLOCK_LENGTH);
isc_nonce_buf(data, bytes);
isc_buffer_init(&b, data, bytes);
isc_buffer_add(&b, bytes);
ret = hmacsha512_fromdns(key, &b);
isc_safe_memwipe(data, sizeof(data));
return (ret);
}
static isc_boolean_t
hmacsha512_isprivate(const dst_key_t *key) {
UNUSED(key);
return (ISC_TRUE);
}
static void
hmacsha512_destroy(dst_key_t *key) {
dst_hmacsha512_key_t *hkey = key->keydata.hmacsha512;
isc_safe_memwipe(hkey, sizeof(*hkey));
isc_mem_put(key->mctx, hkey, sizeof(*hkey));
key->keydata.hmacsha512 = NULL;
}
static isc_result_t
hmacsha512_todns(const dst_key_t *key, isc_buffer_t *data) {
dst_hmacsha512_key_t *hkey;
unsigned int bytes;
REQUIRE(key->keydata.hmacsha512 != NULL);
hkey = key->keydata.hmacsha512;
bytes = (key->key_size + 7) / 8;
if (isc_buffer_availablelength(data) < bytes)
return (ISC_R_NOSPACE);
isc_buffer_putmem(data, hkey->key, bytes);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha512_fromdns(dst_key_t *key, isc_buffer_t *data) {
dst_hmacsha512_key_t *hkey;
int keylen;
isc_region_t r;
isc_sha512_t sha512ctx;
isc_buffer_remainingregion(data, &r);
if (r.length == 0)
return (ISC_R_SUCCESS);
hkey = isc_mem_get(key->mctx, sizeof(dst_hmacsha512_key_t));
if (hkey == NULL)
return (ISC_R_NOMEMORY);
memset(hkey->key, 0, sizeof(hkey->key));
if (r.length > ISC_SHA512_BLOCK_LENGTH) {
isc_sha512_init(&sha512ctx);
isc_sha512_update(&sha512ctx, r.base, r.length);
isc_sha512_final(hkey->key, &sha512ctx);
keylen = ISC_SHA512_DIGESTLENGTH;
} else {
memmove(hkey->key, r.base, r.length);
keylen = r.length;
}
key->key_size = keylen * 8;
key->keydata.hmacsha512 = hkey;
isc_buffer_forward(data, r.length);
return (ISC_R_SUCCESS);
}
static isc_result_t
hmacsha512_tofile(const dst_key_t *key, const char *directory) {
int cnt = 0;
dst_hmacsha512_key_t *hkey;
dst_private_t priv;
int bytes = (key->key_size + 7) / 8;
unsigned char buf[2];
if (key->keydata.hmacsha512 == NULL)
return (DST_R_NULLKEY);
if (key->external)
return (DST_R_EXTERNALKEY);
hkey = key->keydata.hmacsha512;
priv.elements[cnt].tag = TAG_HMACSHA512_KEY;
priv.elements[cnt].length = bytes;
priv.elements[cnt++].data = hkey->key;
buf[0] = (key->key_bits >> 8) & 0xffU;
buf[1] = key->key_bits & 0xffU;
priv.elements[cnt].tag = TAG_HMACSHA512_BITS;
priv.elements[cnt].data = buf;
priv.elements[cnt++].length = 2;
priv.nelements = cnt;
return (dst__privstruct_writefile(key, &priv, directory));
}
static isc_result_t
hmacsha512_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
dst_private_t priv;
isc_result_t result, tresult;
isc_buffer_t b;
isc_mem_t *mctx = key->mctx;
unsigned int i;
UNUSED(pub);
/* read private key file */
result = dst__privstruct_parse(key, DST_ALG_HMACSHA512, lexer, mctx,
&priv);
if (result != ISC_R_SUCCESS)
return (result);
if (key->external)
result = DST_R_EXTERNALKEY;
key->key_bits = 0;
for (i = 0; i < priv.nelements && result == ISC_R_SUCCESS; i++) {
switch (priv.elements[i].tag) {
case TAG_HMACSHA512_KEY:
isc_buffer_init(&b, priv.elements[i].data,
priv.elements[i].length);
isc_buffer_add(&b, priv.elements[i].length);
tresult = hmacsha512_fromdns(key, &b);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
case TAG_HMACSHA512_BITS:
tresult = getkeybits(key, &priv.elements[i]);
if (tresult != ISC_R_SUCCESS)
result = tresult;
break;
default:
result = DST_R_INVALIDPRIVATEKEY;
break;
}
}
dst__privstruct_free(&priv, mctx);
isc_safe_memwipe(&priv, sizeof(priv));
return (result);
}
static dst_func_t hmacsha512_functions = {
hmacsha512_createctx,
NULL, /*%< createctx2 */
hmacsha512_destroyctx,
hmacsha512_adddata,
hmacsha512_sign,
hmacsha512_verify,
NULL, /* verify2 */
NULL, /* computesecret */
hmacsha512_compare,
NULL, /* paramcompare */
hmacsha512_generate,
hmacsha512_isprivate,
hmacsha512_destroy,
hmacsha512_todns,
hmacsha512_fromdns,
hmacsha512_tofile,
hmacsha512_parse,
NULL, /* cleanup */
NULL, /* fromlabel */
NULL, /* dump */
NULL, /* restore */
};
isc_result_t
dst__hmacsha512_init(dst_func_t **funcp) {
REQUIRE(funcp != NULL);
if (*funcp == NULL)
*funcp = &hmacsha512_functions;
return (ISC_R_SUCCESS);
}
/*! \file */