Cache EVP_MD_CTX to avoid many alloc/frees around digest ops.
bk: 65e5c9b533PYzhgaJVkZLrtViqzfmA
#ifndef NTP_MD5_H
#define NTP_MD5_H
+/* Use the system MD5 or fall back on libisc's */
+# if defined HAVE_MD5_H && defined HAVE_MD5INIT
+# include <md5.h>
+# else
+# include "isc/md5.h"
+ typedef isc_md5_t MD5_CTX;
+# define MD5_DIGEST_LENGTH ISC_MD5_DIGESTLENGTH
+# define MD5Init(c) isc_md5_init(c)
+# define MD5Update(c, p, s) isc_md5_update(c, (const void *)p, s)
+# define MD5Final(d, c) isc_md5_final((c), (d)) /* swapped */
+# endif
+
# define KEY_TYPE_MD5 NID_md5
#ifdef OPENSSL
/*
* Provide OpenSSL-alike MD5 API if we're not using OpenSSL
*/
-# if defined HAVE_MD5_H && defined HAVE_MD5INIT
-# include <md5.h>
-# else
-# include "isc/md5.h"
- typedef isc_md5_t MD5_CTX;
-# define MD5Init(c) isc_md5_init(c)
-# define MD5Update(c, p, s) isc_md5_update(c, p, s)
-# define MD5Final(d, c) isc_md5_final((c), (d)) /* swapped */
-# endif
typedef MD5_CTX EVP_MD_CTX;
# define NID_md5 4 /* from openssl/objects.h */
-# define MD5_LENGTH 16
-# define EVP_MAX_MD_SIZE MD5_LENGTH
+# define EVP_MAX_MD_SIZE MD5_DIGEST_LENGTH
# define EVP_MD_CTX_free(c) free(c)
# define EVP_MD_CTX_new() calloc(1, sizeof(MD5_CTX))
# define EVP_get_digestbynid(t) NULL
#ifdef OPENSSL
extern void ssl_init (void);
extern void ssl_check_version (void);
-extern int ssl_init_done;
+extern EVP_MD_CTX* digest_ctx; /* also ssl_init_done */
#define INIT_SSL() \
do { \
- if (!ssl_init_done) \
+ if (NULL == digest_ctx) { \
ssl_init(); \
- } while (0)
+ } \
+ } while (FALSE)
#else /* !OPENSSL follows */
#define ssl_check_version() do {} while (0)
#define INIT_SSL() do {} while (0)
size_t len;
} rwbuffT;
-
#if defined(OPENSSL) && defined(ENABLE_CMAC)
static size_t
cmac_ctx_size(
#endif /* OPENSSL && ENABLE_CMAC */
+/*
+ * Allocate and initialize a digest context. As a speed optimization,
+ * take an idea from ntpsec and cache the context to avoid malloc/free
+ * overhead in time-critical paths. ntpsec also caches the algorithms
+ * with each key.
+ * This is not thread-safe, but that is
+ * not a problem at present.
+ */
static EVP_MD_CTX *
get_md_ctx(
- int type,
- int/*BOOL*/ for_auth
+ int nid
)
{
#ifndef OPENSSL
- return emalloc(sizeof(MD5_CTX));
-#else
- EVP_MD_CTX * ctx;
-# if OPENSSL_VERSION_NUMBER >= 0x30000000L
- EVP_MD * md;
-# endif
- const char * md_name;
+ static MD5_CTX md5_ctx;
- ctx = EVP_MD_CTX_new();
- md_name = OBJ_nid2sn(type);
+ DEBUG_INSIST(NID_md5 == nid);
+ MD5Init(&md5_ctx);
-# if OPENSSL_VERSION_NUMBER >= 0x30000000L
- /*
- * See section FIPS Provider:
- * https://www.openssl.org/docs/man3.0/man7/crypto.html
- * for property query strings
- */
- if (!for_auth && NID_md5 == type) {
- md = EVP_MD_fetch(NULL, md_name, "fips=no");
- } else {
- md = EVP_MD_fetch(NULL, md_name, "");
- }
- if (NULL == md) {
- msyslog(LOG_ERR, "Could not load digest %s", md_name);
- exit(1);
- }
- if (!EVP_DigestInit_ex(ctx, md, NULL)) {
- msyslog(LOG_ERR, "%s init failed", md_name);
- exit(1);
- }
- EVP_MD_free(md);
-# else /* OPENSSL_VERSION_NUMBER < 0x30000000L follows */
-# ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
- if (!for_auth && NID_md5 == type) {
- EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
- }
-# endif
- if (!EVP_DigestInit_ex(ctx, EVP_get_digestbynid(type), NULL)) {
- msyslog(LOG_ERR, "%s init failed", md_name);
+ return &md5_ctx;
+#else
+ if (!EVP_DigestInit(digest_ctx, EVP_get_digestbynid(nid))) {
+ msyslog(LOG_ERR, "%s init failed", OBJ_nid2sn(nid));
exit(1);
}
-# endif /* OPENSSL_VERSION_NUMBER */
- return ctx;
+
+ return digest_ctx;
#endif /* OPENSSL */
}
EVP_MD_CTX * ctx;
u_int uilen = 0;
- ctx = get_md_ctx(ktype, TRUE);
+ ctx = get_md_ctx(ktype);
if (NULL == ctx) {
goto mac_fail;
}
}
mac_fail:
retlen = (size_t)uilen;
-
- if (ctx)
- EVP_MD_CTX_free(ctx);
}
#else /* !OPENSSL follows */
if (NID_md5 == ktype) {
- EVP_MD_CTX * ctx = emalloc(sizeof(MD5_CTX));
- size_t len = 0;
+ EVP_MD_CTX * ctx;
+ ctx = get_md_ctx(ktype);
if (digest->len < MD5_LENGTH) {
msyslog(LOG_ERR, "%s", "MAC encrypt: MAC md5 buf too small.");
} else {
MD5Update(ctx, (const void *)key->buf, key->len);
MD5Update(ctx, (const void *)msg->buf, msg->len);
MD5Final(digest->buf, ctx);
- len = MD5_LENGTH;
+ retlen = MD5_LENGTH;
}
- free(ctx);
- retlen = len;
} else {
msyslog(LOG_ERR, "MAC encrypt: invalid key type %d", ktype);
}
* match the little-endian hash. This is ugly but it seems better
* than changing the IPv6 refid calculation on the more-common
* systems.
+ * This is not thread safe, not a problem so far.
*/
u_int32
addr2refid(sockaddr_u *addr)
{
- u_char digest[EVP_MAX_MD_SIZE];
- u_int32 addr_refid;
- EVP_MD_CTX * ctx;
- u_int len;
+ static MD5_CTX md5_ctx;
+ union u_tag {
+ u_char digest[MD5_DIGEST_LENGTH];
+ u_int32 addr_refid;
+ } u;
if (IS_IPV4(addr)) {
return (NSRCADR(addr));
}
- INIT_SSL();
/* MD5 is not used for authentication here. */
- ctx = get_md_ctx(NID_md5, FALSE);
- EVP_DigestUpdate(ctx, (u_char *)&SOCK_ADDR6(addr),
- sizeof(SOCK_ADDR6(addr)));
- EVP_DigestFinal(ctx, digest, &len);
- EVP_MD_CTX_free(ctx);
- memcpy(&addr_refid, digest, sizeof(addr_refid));
+ MD5Init(&md5_ctx);
+ MD5Update(&md5_ctx, (void *)&SOCK_ADDR6(addr), sizeof(SOCK_ADDR6(addr)));
+ MD5Final(u.digest, &md5_ctx);
#ifdef WORDS_BIGENDIAN
- addr_refid = BYTESWAP32(addr_refid);
+ u.addr_refid = BYTESWAP32(u.addr_refid);
#endif
- return addr_refid;
+ return u.addr_refid;
}
# define CMAC_LENGTH 16
# define CMAC "AES128CMAC"
# endif /*HAVE_OPENSSL_CMAC_H*/
-int ssl_init_done;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+EVP_MD_CTX *digest_ctx;
+
+
static void
atexit_ssl_cleanup(void)
{
- if (!ssl_init_done) {
+ if (NULL == digest_ctx) {
return;
}
-
- ssl_init_done = FALSE;
+ EVP_MD_CTX_free(digest_ctx);
+ digest_ctx = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
EVP_cleanup();
ERR_free_strings();
-}
#endif /* OpenSSL < 1.1 */
+}
+
void
ssl_init(void)
{
init_lib();
- if ( ! ssl_init_done) {
+ if (NULL == digest_ctx) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
- atexit(&atexit_ssl_cleanup);
#endif /* OpenSSL < 1.1 */
- ssl_init_done = TRUE;
+ digest_ctx = EVP_MD_CTX_new();
+ INSIST(digest_ctx != NULL);
+ atexit(&atexit_ssl_cleanup);
}
}
/*
- * derive_nonce - generate client-address-specific nonce value
- * associated with a given timestamp.
+ * derive_nonce - generate 32-bit nonce value derived from the client
+ * address and a request-specific timestamp.
+ *
+ * This uses MD5 for a non-authentication purpose -- the nonce is used
+ * analogous to the TCP 3-way handshake to confirm the UDP client can
+ * receive traffic from which it claims to originate, that is, to
+ * prevent spoofed requests leading to reflected amplification.
*/
static u_int32 derive_nonce(
sockaddr_u * addr,
{
static u_int32 salt[4];
static u_long last_salt_update;
+ MD5_CTX ctx;
union d_tag {
- u_char digest[EVP_MAX_MD_SIZE];
+ u_char digest[MD5_DIGEST_LENGTH];
u_int32 extract;
} d;
- EVP_MD_CTX *ctx;
- u_int len;
- int rc;
while (!salt[0] || current_time - last_salt_update >= 3600) {
salt[0] = ntp_random();
last_salt_update = current_time;
}
- ctx = EVP_MD_CTX_new();
-# if defined(OPENSSL) && defined(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW)
- /* [Bug 3457] set flags and don't kill them again */
- EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
- rc = EVP_DigestInit_ex(ctx, EVP_get_digestbynid(NID_md5), NULL);
-# else
- rc = EVP_DigestInit(ctx, EVP_get_digestbynid(NID_md5));
-# endif
- if (!rc) {
- msyslog(LOG_ERR, "EVP_DigestInit failed in '%s'", __func__);
- return (0);
+ MD5Init(&ctx);
+ MD5Update(&ctx, salt, sizeof(salt));
+ MD5Update(&ctx, &ts_i, sizeof(ts_i));
+ MD5Update(&ctx, &ts_f, sizeof(ts_f));
+ if (IS_IPV4(addr)) {
+ MD5Update(&ctx, &SOCK_ADDR4(addr), sizeof(SOCK_ADDR4(addr)));
+ } else {
+ MD5Update(&ctx, &SOCK_ADDR6(addr), sizeof(SOCK_ADDR6(addr)));
}
-
- EVP_DigestUpdate(ctx, salt, sizeof(salt));
- EVP_DigestUpdate(ctx, &ts_i, sizeof(ts_i));
- EVP_DigestUpdate(ctx, &ts_f, sizeof(ts_f));
- if (IS_IPV4(addr))
- EVP_DigestUpdate(ctx, &SOCK_ADDR4(addr),
- sizeof(SOCK_ADDR4(addr)));
- else
- EVP_DigestUpdate(ctx, &SOCK_ADDR6(addr),
- sizeof(SOCK_ADDR6(addr)));
- EVP_DigestUpdate(ctx, &NSRCPORT(addr), sizeof(NSRCPORT(addr)));
- EVP_DigestUpdate(ctx, salt, sizeof(salt));
- EVP_DigestFinal(ctx, d.digest, &len);
- EVP_MD_CTX_free(ctx);
+ MD5Update(&ctx, &NSRCPORT(addr), sizeof(NSRCPORT(addr)));
+ MD5Update(&ctx, salt, sizeof(salt));
+ MD5Final(d.digest, &ctx);
return d.extract;
}
hdlen = 10 * sizeof(u_int32);
break;
}
- ctx = EVP_MD_CTX_new();
-# if defined(OPENSSL) && defined(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW)
- /* [Bug 3457] set flags and don't kill them again */
- EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
- EVP_DigestInit_ex(ctx, EVP_get_digestbynid(crypto_nid), NULL);
-# else
+ ctx = digest_ctx;
EVP_DigestInit(ctx, EVP_get_digestbynid(crypto_nid));
-# endif
EVP_DigestUpdate(ctx, (u_char *)header, hdlen);
EVP_DigestFinal(ctx, dgst, &len);
- EVP_MD_CTX_free(ctx);
memcpy(&keyid, dgst, 4);
keyid = ntohl(keyid);
if (lifetime != 0) {
if (tstamp != 0) {
if (vp->sig == NULL)
vp->sig = emalloc(sign_siglen);
- ctx = EVP_MD_CTX_new();
+ ctx = digest_ctx;
EVP_SignInit(ctx, sign_digest);
EVP_SignUpdate(ctx, (u_char *)vp, 12);
EVP_SignUpdate(ctx, vp->ptr, sizeof(struct autokey));
vp->siglen = htonl(len);
peer->flags |= FLAG_ASSOC;
}
- EVP_MD_CTX_free(ctx);
}
DPRINTF(1, ("make_keys: %d %08x %08x ts %u fs %u poll %d\n",
peer->keynumber, keyid, cookie, ntohl(vp->tstamp),
* signature. If the identity exchange is verified, light the
* proventic bit. What a relief.
*/
- ctx = EVP_MD_CTX_new();
+ ctx = digest_ctx;
EVP_VerifyInit(ctx, peer->digest);
- /* XXX: the "+ 12" needs to be at least documented... */
- EVP_VerifyUpdate(ctx, (u_char *)&ep->tstamp, vallen + 12);
+ EVP_VerifyUpdate(ctx, (u_char *)&ep->tstamp, vallen +
+ sizeof(ep->tstamp) + sizeof(ep->fstamp) +
+ sizeof(ep->vallen));
if (EVP_VerifyFinal(ctx, (u_char *)&ep->pkt[i], siglen,
pkey) <= 0) {
- EVP_MD_CTX_free(ctx);
return (XEVNT_SIG);
}
- EVP_MD_CTX_free(ctx);
if (peer->crypto & CRYPTO_FLAG_VRFY)
peer->crypto |= CRYPTO_FLAG_PROV;
return (XEVNT_OK);
vp->sig = emalloc(sign_siglen);
- ctx = EVP_MD_CTX_new();
+ ctx = digest_ctx;
EVP_SignInit(ctx, sign_digest);
EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12);
EVP_SignUpdate(ctx, vp->ptr, vallen);
INSIST(vallen <= sign_siglen);
vp->siglen = htonl(vallen);
}
- EVP_MD_CTX_free(ctx);
return (XEVNT_OK);
}
if (hostval.tstamp == 0)
return;
- ctx = EVP_MD_CTX_new();
+ ctx = digest_ctx;
/*
* Sign public key and timestamps. The filestamp is derived from
ntohl(hostval.tstamp));
record_crypto_stats(NULL, statstr);
DPRINTF(1, ("crypto_update: %s\n", statstr));
- EVP_MD_CTX_free(ctx);
}
/*
len = BN_num_bytes(bn);
ptr = emalloc(len);
BN_bn2bin(bn, ptr);
- ctx = EVP_MD_CTX_new();
-# if defined(OPENSSL) && defined(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW)
- /* [Bug 3457] set flags and don't kill them again */
- EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
- EVP_DigestInit_ex(ctx, EVP_md5(), NULL);
-# else
+ ctx = digest_ctx;
EVP_DigestInit(ctx, EVP_md5());
-# endif
EVP_DigestUpdate(ctx, ptr, len);
EVP_DigestFinal(ctx, dgst, &len);
- EVP_MD_CTX_free(ctx);
BN_bin2bn(dgst, len, bk);
free(ptr);
}
return (XEVNT_OK);
vp->sig = emalloc(sign_siglen);
- ctx = EVP_MD_CTX_new();
+ ctx = digest_ctx;
EVP_SignInit(ctx, sign_digest);
EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12);
EVP_SignUpdate(ctx, vp->ptr, len);
INSIST(len <= sign_siglen);
vp->siglen = htonl(len);
}
- EVP_MD_CTX_free(ctx);
return (XEVNT_OK);
}
/* XXX: more validation to make sure the sign fits... */
vp->sig = emalloc(sign_siglen);
- ctx = EVP_MD_CTX_new();
+ ctx = digest_ctx;
EVP_SignInit(ctx, sign_digest);
EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12);
EVP_SignUpdate(ctx, vp->ptr, len);
INSIST(len <= sign_siglen);
vp->siglen = htonl(len);
}
- EVP_MD_CTX_free(ctx);
retv = XEVNT_OK;
cleanup:
return (XEVNT_OK);
vp->sig = emalloc(sign_siglen);
- ctx = EVP_MD_CTX_new();
+ ctx = digest_ctx;
EVP_SignInit(ctx, sign_digest);
EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12);
EVP_SignUpdate(ctx, vp->ptr, len);
INSIST(len <= sign_siglen);
vp->siglen = htonl(len);
}
- EVP_MD_CTX_free(ctx);
return (XEVNT_OK);
}
return (XEVNT_OK);
vp->sig = emalloc(sign_siglen);
- ctx = EVP_MD_CTX_new();
+ ctx = digest_ctx;
EVP_SignInit(ctx, sign_digest);
EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12);
EVP_SignUpdate(ctx, vp->ptr, len);
INSIST(len <= sign_siglen);
vp->siglen = htonl(len);
}
- EVP_MD_CTX_free(ctx);
return (XEVNT_OK);
}
return (XEVNT_OK);
vp->sig = emalloc(sign_siglen);
- ctx = EVP_MD_CTX_new();
+ ctx = digest_ctx;
EVP_SignInit(ctx, sign_digest);
EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12);
EVP_SignUpdate(ctx, vp->ptr, len);
INSIST(len <= sign_siglen);
vp->siglen = htonl(len);
}
- EVP_MD_CTX_free(ctx);
return (XEVNT_OK);
}
return (XEVNT_OK);
vp->sig = emalloc(sign_siglen);
- ctx = EVP_MD_CTX_new();
+ ctx = digest_ctx;
EVP_SignInit(ctx, sign_digest);
EVP_SignUpdate(ctx, (u_char *)&vp->tstamp, 12);
EVP_SignUpdate(ctx, vp->ptr, len);
INSIST(len <= sign_siglen);
vp->siglen = htonl(len);
}
- EVP_MD_CTX_free(ctx);
return (XEVNT_OK);
}
vp->siglen = 0;
if (tstamp != 0) {
vp->sig = emalloc(sign_siglen);
- ctx = EVP_MD_CTX_new();
+ ctx = digest_ctx;
EVP_SignInit(ctx, sign_digest);
EVP_SignUpdate(ctx, (u_char *)vp, 12);
EVP_SignUpdate(ctx, vp->ptr, len);
INSIST(len <= sign_siglen);
vp->siglen = htonl(len);
}
- EVP_MD_CTX_free(ctx);
}
#ifdef DEBUG
if (debug > 1)
hisleap = PKT_LEAP(pkt->li_vn_mode);
hisstratum = PKT_TO_STRATUM(pkt->stratum);
- INSIST(0 != hisstratum); /* paranoia check PKT_TO_STRATUM result */
-
+ DEBUG_INSIST(0 != hisstratum); /* paranoia check PKT_TO_STRATUM result */
+ /* TODO: this should be in a unit test */
DPRINTF(1, ("receive: at %ld %s<-%s ippeerlimit %d mode %d iflags %s "
- "restrict %s org 0x%8x.%08x xmt 0x%8x.%08x\n",
+ "restrict %s org 0x%x.%08x xmt 0x%x.%08x\n",
current_time, stoa(&rbufp->dstadr->sin),
stoa(&rbufp->recv_srcadr), r4a.ippeerlimit, hismode,
iflags_str(rbufp->dstadr->flags),
* https://slproweb.com/products/Win32OpenSSL.html
*
* If 'OPENSSL_AUTOLINK_STRICT' is defined, then target bit width,
- * runtime model and debug/release info are incoded into the library
+ * runtime model and debug/release info are encoded into the library
* file name, according to this scheme:
*
* basename<width><RT><DebRel>.lib