/*
 *	digest support for NTP, MD5 and with OpenSSL more
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "ntp_fp.h"
#include "ntp_string.h"
#include "ntp_stdlib.h"
#include "ntp.h"
#include "ntp_md5.h"	/* provides OpenSSL digest API */
#include "isc/string.h"
#include "libssl_compat.h"
/*
 * MD5authencrypt - generate message digest
 *
 * Returns length of MAC including key ID and digest.
 */
size_t
MD5authencrypt(
	int		type,	/* hash algorithm */
	const u_char *	key,	/* key pointer */
	u_int32 *	pkt,	/* packet pointer */
	size_t		length	/* packet length */
	)
{
	u_char	digest[EVP_MAX_MD_SIZE];
	u_int	len;
	EVP_MD_CTX *ctx;

	/*
	 * Compute digest of key concatenated with packet. Note: the
	 * key type and digest type have been verified when the key
	 * was creaded.
	 */
	INIT_SSL();
	ctx = EVP_MD_CTX_new();
	if (!(ctx && EVP_DigestInit(ctx, EVP_get_digestbynid(type)))) {
		msyslog(LOG_ERR,
		    "MAC encrypt: digest init failed");
		EVP_MD_CTX_free(ctx);
		return (0);
	}
	EVP_DigestUpdate(ctx, key, cache_secretsize);
	EVP_DigestUpdate(ctx, (u_char *)pkt, length);
	EVP_DigestFinal(ctx, digest, &len);
	EVP_MD_CTX_free(ctx);
	/* If the MAC is longer than the MAX then truncate it. */
	if (len > MAX_MAC_LEN - 4)
	    len = MAX_MAC_LEN - 4;
	memmove((u_char *)pkt + length + 4, digest, len);
	return (len + 4);
}


/*
 * MD5authdecrypt - verify MD5 message authenticator
 *
 * Returns one if digest valid, zero if invalid.
 */
int
MD5authdecrypt(
	int		type,	/* hash algorithm */
	const u_char *	key,	/* key pointer */
	u_int32	*	pkt,	/* packet pointer */
	size_t		length,	/* packet length */
	size_t		size	/* MAC size */
	)
{
	u_char	digest[EVP_MAX_MD_SIZE];
	u_int	len;
	EVP_MD_CTX *ctx;

	/*
	 * Compute digest of key concatenated with packet. Note: the
	 * key type and digest type have been verified when the key
	 * was created.
	 */
	INIT_SSL();
	ctx = EVP_MD_CTX_new();
	if (!(ctx && EVP_DigestInit(ctx, EVP_get_digestbynid(type)))) {
		msyslog(LOG_ERR,
		    "MAC decrypt: digest init failed");
		EVP_MD_CTX_free(ctx);
		return (0);
	}
	EVP_DigestUpdate(ctx, key, cache_secretsize);
	EVP_DigestUpdate(ctx, (u_char *)pkt, length);
	EVP_DigestFinal(ctx, digest, &len);
	EVP_MD_CTX_free(ctx);
	/* If the MAC is longer than the MAX then truncate it. */
	if (len > MAX_MAC_LEN - 4)
	    len = MAX_MAC_LEN - 4;
	if (size != (size_t)len + 4) {
		msyslog(LOG_ERR,
		    "MAC decrypt: MAC length error");
		return (0);
	}
	return !isc_tsmemcmp(digest, (u_char *)pkt + length + 4, len);
}

/*
 * Calculate the reference id from the address. If it is an IPv4
 * address, use it as is. If it is an IPv6 address, do a md5 on
 * it and use the bottom 4 bytes.
 * The result is in network byte order.
 */
u_int32
addr2refid(sockaddr_u *addr)
{
	u_char		digest[20];
	u_int32		addr_refid;
	EVP_MD_CTX	*ctx;
	u_int		len;

	if (IS_IPV4(addr))
		return (NSRCADR(addr));

	INIT_SSL();

	ctx = EVP_MD_CTX_new();
	EVP_MD_CTX_init(ctx);
#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
	/* MD5 is not used as a crypto hash here. */
	EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
#endif
	if (!EVP_DigestInit_ex(ctx, EVP_md5(), NULL)) {
		msyslog(LOG_ERR,
		    "MD5 init failed");
		EVP_MD_CTX_free(ctx);	/* pedantic... but safe */
		exit(1);
	}

	EVP_DigestUpdate(ctx, (u_char *)PSOCK_ADDR6(addr),
	    sizeof(struct in6_addr));
	EVP_DigestFinal(ctx, digest, &len);
	EVP_MD_CTX_free(ctx);
	memcpy(&addr_refid, digest, sizeof(addr_refid));
	return (addr_refid);
}
