Files
dnsmasq/src/crypto.c
Simon Kelley f3223fbff6 Fix nettle_hash() function to avoid ABI incompatibilities.
The way of accessing the list of available hashes on nettle was
vulnerable to breaking if the version of libnettle in use was
different to the version dnsmasq was compiled against.
Change to a new system if libnettle >= 3.4 is in use.
Older versions if nettle are still OK, once 3.4 is reached,
the ABi problem is fixed. Thanks to Petr Menšík for clues on this.
2018-03-06 22:55:36 +00:00

471 lines
12 KiB
C

/* dnsmasq is Copyright (c) 2000-2018 Simon Kelley
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DNSSEC
#include <nettle/rsa.h>
#include <nettle/dsa.h>
#ifndef NO_NETTLE_ECC
# include <nettle/ecdsa.h>
# include <nettle/ecc-curve.h>
# include <nettle/eddsa.h>
#endif
#include <nettle/nettle-meta.h>
#include <nettle/bignum.h>
/* Nettle-3.0 moved to a new API for DSA. We use a name that's defined in the new API
to detect Nettle-3, and invoke the backwards compatibility mode. */
#ifdef dsa_params_init
#include <nettle/dsa-compat.h>
#endif
/* Implement a "hash-function" to the nettle API, which simply returns
the input data, concatenated into a single, statically maintained, buffer.
Used for the EdDSA sigs, which operate on the whole message, rather
than a digest. */
struct null_hash_digest
{
uint8_t *buff;
size_t len;
};
struct null_hash_ctx
{
size_t len;
};
static size_t null_hash_buff_sz = 0;
static uint8_t *null_hash_buff = NULL;
#define BUFF_INCR 128
static void null_hash_init(void *ctx)
{
((struct null_hash_ctx *)ctx)->len = 0;
}
static void null_hash_update(void *ctxv, size_t length, const uint8_t *src)
{
struct null_hash_ctx *ctx = ctxv;
size_t new_len = ctx->len + length;
if (new_len > null_hash_buff_sz)
{
uint8_t *new;
if (!(new = whine_malloc(new_len + BUFF_INCR)))
return;
if (null_hash_buff)
{
if (ctx->len != 0)
memcpy(new, null_hash_buff, ctx->len);
free(null_hash_buff);
}
null_hash_buff_sz = new_len + BUFF_INCR;
null_hash_buff = new;
}
memcpy(null_hash_buff + ctx->len, src, length);
ctx->len += length;
}
static void null_hash_digest(void *ctx, size_t length, uint8_t *dst)
{
(void)length;
((struct null_hash_digest *)dst)->buff = null_hash_buff;
((struct null_hash_digest *)dst)->len = ((struct null_hash_ctx *)ctx)->len;
}
static struct nettle_hash null_hash = {
"null_hash",
sizeof(struct null_hash_ctx),
sizeof(struct null_hash_digest),
0,
(nettle_hash_init_func *) null_hash_init,
(nettle_hash_update_func *) null_hash_update,
(nettle_hash_digest_func *) null_hash_digest
};
/* Find pointer to correct hash function in nettle library */
const struct nettle_hash *hash_find(char *name)
{
int i;
if (!name)
return NULL;
/* We provide a "null" hash which returns the input data as digest. */
if (strcmp(null_hash.name, name) == 0)
return &null_hash;
/* libnettle >= 3.4 provides nettle_lookup_hash() which avoids nasty ABI
incompatibilities if sizeof(nettle_hashes) changes between library
versions. */
#if (NETTLE_VERSION_MAJOR>3) || ((NETTLE_VERSION_MAJOR==3) && (NETTLE_VERSION_MINOR >=4))
return nettle_lookup_hash(name);
#else
for (i = 0; nettle_hashes[i]; i++)
{
if (strcmp(nettle_hashes[i]->name, name) == 0)
return nettle_hashes[i];
}
return NULL;
#endif
}
/* expand ctx and digest memory allocations if necessary and init hash function */
int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp)
{
static void *ctx = NULL;
static unsigned char *digest = NULL;
static unsigned int ctx_sz = 0;
static unsigned int digest_sz = 0;
void *new;
if (ctx_sz < hash->context_size)
{
if (!(new = whine_malloc(hash->context_size)))
return 0;
if (ctx)
free(ctx);
ctx = new;
ctx_sz = hash->context_size;
}
if (digest_sz < hash->digest_size)
{
if (!(new = whine_malloc(hash->digest_size)))
return 0;
if (digest)
free(digest);
digest = new;
digest_sz = hash->digest_size;
}
*ctxp = ctx;
*digestp = digest;
hash->init(ctx);
return 1;
}
static int dnsmasq_rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
size_t exp_len;
static struct rsa_public_key *key = NULL;
static mpz_t sig_mpz;
(void)digest_len;
if (key == NULL)
{
if (!(key = whine_malloc(sizeof(struct rsa_public_key))))
return 0;
nettle_rsa_public_key_init(key);
mpz_init(sig_mpz);
}
if ((key_len < 3) || !(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
key_len--;
if ((exp_len = *p++) == 0)
{
GETSHORT(exp_len, p);
key_len -= 2;
}
if (exp_len >= key_len)
return 0;
key->size = key_len - exp_len;
mpz_import(key->e, exp_len, 1, 1, 0, 0, p);
mpz_import(key->n, key->size, 1, 1, 0, 0, p + exp_len);
mpz_import(sig_mpz, sig_len, 1, 1, 0, 0, sig);
switch (algo)
{
case 1:
return nettle_rsa_md5_verify_digest(key, digest, sig_mpz);
case 5: case 7:
return nettle_rsa_sha1_verify_digest(key, digest, sig_mpz);
case 8:
return nettle_rsa_sha256_verify_digest(key, digest, sig_mpz);
case 10:
return nettle_rsa_sha512_verify_digest(key, digest, sig_mpz);
}
return 0;
}
static int dnsmasq_dsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
unsigned int t;
static struct dsa_public_key *key = NULL;
static struct dsa_signature *sig_struct;
(void)digest_len;
if (key == NULL)
{
if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))) ||
!(key = whine_malloc(sizeof(struct dsa_public_key))))
return 0;
nettle_dsa_public_key_init(key);
nettle_dsa_signature_init(sig_struct);
}
if ((sig_len < 41) || !(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
t = *p++;
if (key_len < (213 + (t * 24)))
return 0;
mpz_import(key->q, 20, 1, 1, 0, 0, p); p += 20;
mpz_import(key->p, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(key->g, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(key->y, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(sig_struct->r, 20, 1, 1, 0, 0, sig+1);
mpz_import(sig_struct->s, 20, 1, 1, 0, 0, sig+21);
(void)algo;
return nettle_dsa_sha1_verify_digest(key, digest, sig_struct);
}
#ifndef NO_NETTLE_ECC
static int dnsmasq_ecdsa_verify(struct blockdata *key_data, unsigned int key_len,
unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
unsigned int t;
struct ecc_point *key;
static struct ecc_point *key_256 = NULL, *key_384 = NULL;
static mpz_t x, y;
static struct dsa_signature *sig_struct;
if (!sig_struct)
{
if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))))
return 0;
nettle_dsa_signature_init(sig_struct);
mpz_init(x);
mpz_init(y);
}
switch (algo)
{
case 13:
if (!key_256)
{
if (!(key_256 = whine_malloc(sizeof(struct ecc_point))))
return 0;
nettle_ecc_point_init(key_256, &nettle_secp_256r1);
}
key = key_256;
t = 32;
break;
case 14:
if (!key_384)
{
if (!(key_384 = whine_malloc(sizeof(struct ecc_point))))
return 0;
nettle_ecc_point_init(key_384, &nettle_secp_384r1);
}
key = key_384;
t = 48;
break;
default:
return 0;
}
if (sig_len != 2*t || key_len != 2*t ||
!(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
mpz_import(x, t , 1, 1, 0, 0, p);
mpz_import(y, t , 1, 1, 0, 0, p + t);
if (!ecc_point_set(key, x, y))
return 0;
mpz_import(sig_struct->r, t, 1, 1, 0, 0, sig);
mpz_import(sig_struct->s, t, 1, 1, 0, 0, sig + t);
return nettle_ecdsa_verify(key, digest_len, digest, sig_struct);
}
static int dnsmasq_eddsa_verify(struct blockdata *key_data, unsigned int key_len,
unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
unsigned char *p;
if (key_len != ED25519_KEY_SIZE ||
sig_len != ED25519_SIGNATURE_SIZE ||
digest_len != sizeof(struct null_hash_digest) ||
!(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
/* The "digest" returned by the null_hash function is simply a struct null_hash_digest
which has a pointer to the actual data and a length, because the buffer
may need to be extended during "hashing". */
switch (algo)
{
case 15:
return ed25519_sha512_verify(p,
((struct null_hash_digest *)digest)->len,
((struct null_hash_digest *)digest)->buff,
sig);
case 16:
/* Ed448 when available */
return 0;
}
return 0;
}
#endif
static int (*verify_func(int algo))(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
/* Enure at runtime that we have support for this digest */
if (!hash_find(algo_digest_name(algo)))
return NULL;
/* This switch defines which sig algorithms we support, can't introspect Nettle for that. */
switch (algo)
{
case 1: case 5: case 7: case 8: case 10:
return dnsmasq_rsa_verify;
case 3: case 6:
return dnsmasq_dsa_verify;
#ifndef NO_NETTLE_ECC
case 13: case 14:
return dnsmasq_ecdsa_verify;
case 15: case 16:
return dnsmasq_eddsa_verify;
#endif
}
return NULL;
}
int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo)
{
int (*func)(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, size_t digest_len, int algo);
func = verify_func(algo);
if (!func)
return 0;
return (*func)(key_data, key_len, sig, sig_len, digest, digest_len, algo);
}
/* Note the ds_digest_name(), algo_digest_name() and nsec3_digest_name()
define which algo numbers we support. If algo_digest_name() returns
non-NULL for an algorithm number, we assume that algorithm is
supported by verify(). */
/* http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
char *ds_digest_name(int digest)
{
switch (digest)
{
case 1: return "sha1";
case 2: return "sha256";
case 3: return "gosthash94";
case 4: return "sha384";
default: return NULL;
}
}
/* http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
char *algo_digest_name(int algo)
{
switch (algo)
{
case 1: return NULL; /* RSA/MD5 - Must Not Implement. RFC 6944 para 2.3. */
case 2: return NULL; /* Diffie-Hellman */
case 3: return "sha1"; /* DSA/SHA1 */
case 5: return "sha1"; /* RSA/SHA1 */
case 6: return "sha1"; /* DSA-NSEC3-SHA1 */
case 7: return "sha1"; /* RSASHA1-NSEC3-SHA1 */
case 8: return "sha256"; /* RSA/SHA-256 */
case 10: return "sha512"; /* RSA/SHA-512 */
case 12: return NULL; /* ECC-GOST */
case 13: return "sha256"; /* ECDSAP256SHA256 */
case 14: return "sha384"; /* ECDSAP384SHA384 */
case 15: return "null_hash"; /* ED25519 */
case 16: return NULL; /* ED448 */
default: return NULL;
}
}
/* http://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml */
char *nsec3_digest_name(int digest)
{
switch (digest)
{
case 1: return "sha1";
default: return NULL;
}
}
#endif