diff --git a/doc/crypt.tex b/doc/crypt.tex
index 982103389..256fdfbd6 100644
--- a/doc/crypt.tex
+++ b/doc/crypt.tex
@@ -3980,6 +3980,112 @@ \subsection{F9--MAC Functions}
Which will BLAKE2s/b--MAC the entire contents of the file specified by \textit{fname} using the key \textit{key} of
length \textit{keylen} bytes. It will store the MAC in \textit{mac} with the same rules as blake2smac\_done().
+\mysection{KMAC}
+
+KMAC is a keyed message authentication code based on the Keccak permutation, standardised by NIST in
+SP 800--185. The library provides KMAC128 and KMAC256, each available in fixed--length and
+extendable--output (XOF) variants.
+
+The desired flavour is selected through the \textit{variant} argument, which must be exactly one of the
+following four named constants:
+
+\begin{center}
+\begin{tabular}{ll}
+\hline
+\textit{variant} & resulting algorithm \\
+\hline
+\texttt{LTC\_KMAC128} & KMAC128 \\
+\texttt{LTC\_KMAC256} & KMAC256 \\
+\texttt{LTC\_KMAC128\_XOF} & KMACXOF128 \\
+\texttt{LTC\_KMAC256\_XOF} & KMACXOF256 \\
+\hline
+\end{tabular}
+\end{center}
+
+Any other value passed in \textit{variant} causes the init function to return \textbf{CRYPT\_INVALID\_ARG}.
+
+A KMAC state is initialised with the following function:
+\index{kmac\_init()}
+\begin{verbatim}
+int kmac_init( kmac_state *st,
+ int variant,
+ const unsigned char *key,
+ unsigned long keylen,
+ const unsigned char *cust,
+ unsigned long custlen);
+\end{verbatim}
+This will initialise the KMAC state \textit{st} with the key in \textit{key} of length \textit{keylen} octets
+and the optional customisation string in \textit{cust} of length \textit{custlen} octets. The customisation
+string may be empty: pass \textbf{NULL} as \textit{cust} together with \textit{custlen} equal to zero.
+
+To process data through KMAC use the following function:
+\index{kmac\_process()}
+\begin{verbatim}
+int kmac_process( kmac_state *st,
+ const unsigned char *in,
+ unsigned long inlen);
+\end{verbatim}
+This will add the message octets pointed to by \textit{in} of length \textit{inlen} to the KMAC state pointed
+to by \textit{st}.
+
+To compute the MAC tag use:
+\index{kmac\_done()}
+\begin{verbatim}
+int kmac_done( kmac_state *st,
+ unsigned char *out,
+ unsigned long *outlen);
+\end{verbatim}
+On entry \textit{outlen} holds the requested output size in octets; on exit it holds the number of octets
+actually written to \textit{out}. For the fixed--length variants the requested length is bound into the
+authentication tag, so callers must commit to it before calling kmac\_done(). For the XOF variants the
+requested length is independent of the tag computation and any output length may be asked for.
+
+Helper functions are provided to make hashing memory buffers and files easier:
+\index{kmac\_memory()}
+\begin{verbatim}
+int kmac_memory( int variant,
+ const unsigned char *key,
+ unsigned long keylen,
+ const unsigned char *cust,
+ unsigned long custlen,
+ const unsigned char *in,
+ unsigned long inlen,
+ unsigned char *out,
+ unsigned long *outlen);
+\end{verbatim}
+This will compute the KMAC of \textit{inlen} bytes of \textit{in} using the key \textit{key} of length
+\textit{keylen} bytes and the customisation string \textit{cust} of length \textit{custlen} bytes. The result
+is stored in \textit{out} following the same rules as kmac\_done().
+
+\index{kmac\_memory\_multi()}
+\begin{verbatim}
+int kmac_memory_multi( int variant,
+ const unsigned char *key,
+ unsigned long keylen,
+ const unsigned char *cust,
+ unsigned long custlen,
+ unsigned char *out,
+ unsigned long *outlen,
+ const unsigned char *in,
+ unsigned long inlen, ...);
+\end{verbatim}
+The variadic form accepts an arbitrary number of (data, length) pairs, terminated by a \textbf{NULL} pointer.
+
+To KMAC a file use:
+\index{kmac\_file()}
+\begin{verbatim}
+int kmac_file( int variant,
+ const unsigned char *key,
+ unsigned long keylen,
+ const unsigned char *cust,
+ unsigned long custlen,
+ const char *fname,
+ unsigned char *out,
+ unsigned long *outlen);
+\end{verbatim}
+Which will KMAC the entire contents of the file named \textit{fname} and store the tag in \textit{out}
+following the same rules as kmac\_done().
+
\chapter{Pseudo-Random Number Generators}
\mysection{Core Functions}
The library provides an array of core functions for Pseudo-Random Number Generators (PRNGs) as well. A cryptographic PRNG is
diff --git a/libtomcrypt_VS2008.vcproj b/libtomcrypt_VS2008.vcproj
index 11cfac1d5..5927eaadc 100644
--- a/libtomcrypt_VS2008.vcproj
+++ b/libtomcrypt_VS2008.vcproj
@@ -1155,6 +1155,30 @@
>
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/makefile.mingw b/makefile.mingw
index df39d6fbc..1552c4331 100644
--- a/makefile.mingw
+++ b/makefile.mingw
@@ -77,11 +77,12 @@ src/mac/blake2/blake2smac_memory_multi.o src/mac/blake2/blake2smac_test.o src/ma
src/mac/f9/f9_file.o src/mac/f9/f9_init.o src/mac/f9/f9_memory.o src/mac/f9/f9_memory_multi.o \
src/mac/f9/f9_process.o src/mac/f9/f9_test.o src/mac/hmac/hmac_done.o src/mac/hmac/hmac_file.o \
src/mac/hmac/hmac_init.o src/mac/hmac/hmac_memory.o src/mac/hmac/hmac_memory_multi.o \
-src/mac/hmac/hmac_process.o src/mac/hmac/hmac_test.o src/mac/omac/omac_done.o src/mac/omac/omac_file.o \
-src/mac/omac/omac_init.o src/mac/omac/omac_memory.o src/mac/omac/omac_memory_multi.o \
-src/mac/omac/omac_process.o src/mac/omac/omac_test.o src/mac/pelican/pelican.o \
-src/mac/pelican/pelican_memory.o src/mac/pelican/pelican_test.o src/mac/pmac/pmac_done.o \
-src/mac/pmac/pmac_file.o src/mac/pmac/pmac_init.o src/mac/pmac/pmac_memory.o \
+src/mac/hmac/hmac_process.o src/mac/hmac/hmac_test.o src/mac/kmac/kmac.o src/mac/kmac/kmac_file.o \
+src/mac/kmac/kmac_memory.o src/mac/kmac/kmac_memory_multi.o src/mac/kmac/kmac_test.o \
+src/mac/omac/omac_done.o src/mac/omac/omac_file.o src/mac/omac/omac_init.o src/mac/omac/omac_memory.o \
+src/mac/omac/omac_memory_multi.o src/mac/omac/omac_process.o src/mac/omac/omac_test.o \
+src/mac/pelican/pelican.o src/mac/pelican/pelican_memory.o src/mac/pelican/pelican_test.o \
+src/mac/pmac/pmac_done.o src/mac/pmac/pmac_file.o src/mac/pmac/pmac_init.o src/mac/pmac/pmac_memory.o \
src/mac/pmac/pmac_memory_multi.o src/mac/pmac/pmac_ntz.o src/mac/pmac/pmac_process.o \
src/mac/pmac/pmac_shift_xor.o src/mac/pmac/pmac_test.o src/mac/poly1305/poly1305.o \
src/mac/poly1305/poly1305_file.o src/mac/poly1305/poly1305_memory.o \
diff --git a/makefile.msvc b/makefile.msvc
index e9028e8ab..c7bc53e27 100644
--- a/makefile.msvc
+++ b/makefile.msvc
@@ -70,11 +70,12 @@ src/mac/blake2/blake2smac_memory_multi.obj src/mac/blake2/blake2smac_test.obj sr
src/mac/f9/f9_file.obj src/mac/f9/f9_init.obj src/mac/f9/f9_memory.obj src/mac/f9/f9_memory_multi.obj \
src/mac/f9/f9_process.obj src/mac/f9/f9_test.obj src/mac/hmac/hmac_done.obj src/mac/hmac/hmac_file.obj \
src/mac/hmac/hmac_init.obj src/mac/hmac/hmac_memory.obj src/mac/hmac/hmac_memory_multi.obj \
-src/mac/hmac/hmac_process.obj src/mac/hmac/hmac_test.obj src/mac/omac/omac_done.obj src/mac/omac/omac_file.obj \
-src/mac/omac/omac_init.obj src/mac/omac/omac_memory.obj src/mac/omac/omac_memory_multi.obj \
-src/mac/omac/omac_process.obj src/mac/omac/omac_test.obj src/mac/pelican/pelican.obj \
-src/mac/pelican/pelican_memory.obj src/mac/pelican/pelican_test.obj src/mac/pmac/pmac_done.obj \
-src/mac/pmac/pmac_file.obj src/mac/pmac/pmac_init.obj src/mac/pmac/pmac_memory.obj \
+src/mac/hmac/hmac_process.obj src/mac/hmac/hmac_test.obj src/mac/kmac/kmac.obj src/mac/kmac/kmac_file.obj \
+src/mac/kmac/kmac_memory.obj src/mac/kmac/kmac_memory_multi.obj src/mac/kmac/kmac_test.obj \
+src/mac/omac/omac_done.obj src/mac/omac/omac_file.obj src/mac/omac/omac_init.obj src/mac/omac/omac_memory.obj \
+src/mac/omac/omac_memory_multi.obj src/mac/omac/omac_process.obj src/mac/omac/omac_test.obj \
+src/mac/pelican/pelican.obj src/mac/pelican/pelican_memory.obj src/mac/pelican/pelican_test.obj \
+src/mac/pmac/pmac_done.obj src/mac/pmac/pmac_file.obj src/mac/pmac/pmac_init.obj src/mac/pmac/pmac_memory.obj \
src/mac/pmac/pmac_memory_multi.obj src/mac/pmac/pmac_ntz.obj src/mac/pmac/pmac_process.obj \
src/mac/pmac/pmac_shift_xor.obj src/mac/pmac/pmac_test.obj src/mac/poly1305/poly1305.obj \
src/mac/poly1305/poly1305_file.obj src/mac/poly1305/poly1305_memory.obj \
diff --git a/makefile.unix b/makefile.unix
index b07b43b8c..1214ba9c9 100644
--- a/makefile.unix
+++ b/makefile.unix
@@ -91,11 +91,12 @@ src/mac/blake2/blake2smac_memory_multi.o src/mac/blake2/blake2smac_test.o src/ma
src/mac/f9/f9_file.o src/mac/f9/f9_init.o src/mac/f9/f9_memory.o src/mac/f9/f9_memory_multi.o \
src/mac/f9/f9_process.o src/mac/f9/f9_test.o src/mac/hmac/hmac_done.o src/mac/hmac/hmac_file.o \
src/mac/hmac/hmac_init.o src/mac/hmac/hmac_memory.o src/mac/hmac/hmac_memory_multi.o \
-src/mac/hmac/hmac_process.o src/mac/hmac/hmac_test.o src/mac/omac/omac_done.o src/mac/omac/omac_file.o \
-src/mac/omac/omac_init.o src/mac/omac/omac_memory.o src/mac/omac/omac_memory_multi.o \
-src/mac/omac/omac_process.o src/mac/omac/omac_test.o src/mac/pelican/pelican.o \
-src/mac/pelican/pelican_memory.o src/mac/pelican/pelican_test.o src/mac/pmac/pmac_done.o \
-src/mac/pmac/pmac_file.o src/mac/pmac/pmac_init.o src/mac/pmac/pmac_memory.o \
+src/mac/hmac/hmac_process.o src/mac/hmac/hmac_test.o src/mac/kmac/kmac.o src/mac/kmac/kmac_file.o \
+src/mac/kmac/kmac_memory.o src/mac/kmac/kmac_memory_multi.o src/mac/kmac/kmac_test.o \
+src/mac/omac/omac_done.o src/mac/omac/omac_file.o src/mac/omac/omac_init.o src/mac/omac/omac_memory.o \
+src/mac/omac/omac_memory_multi.o src/mac/omac/omac_process.o src/mac/omac/omac_test.o \
+src/mac/pelican/pelican.o src/mac/pelican/pelican_memory.o src/mac/pelican/pelican_test.o \
+src/mac/pmac/pmac_done.o src/mac/pmac/pmac_file.o src/mac/pmac/pmac_init.o src/mac/pmac/pmac_memory.o \
src/mac/pmac/pmac_memory_multi.o src/mac/pmac/pmac_ntz.o src/mac/pmac/pmac_process.o \
src/mac/pmac/pmac_shift_xor.o src/mac/pmac/pmac_test.o src/mac/poly1305/poly1305.o \
src/mac/poly1305/poly1305_file.o src/mac/poly1305/poly1305_memory.o \
diff --git a/makefile_include.mk b/makefile_include.mk
index 0577b5bda..924e0b432 100644
--- a/makefile_include.mk
+++ b/makefile_include.mk
@@ -262,11 +262,12 @@ src/mac/blake2/blake2smac_memory_multi.o src/mac/blake2/blake2smac_test.o src/ma
src/mac/f9/f9_file.o src/mac/f9/f9_init.o src/mac/f9/f9_memory.o src/mac/f9/f9_memory_multi.o \
src/mac/f9/f9_process.o src/mac/f9/f9_test.o src/mac/hmac/hmac_done.o src/mac/hmac/hmac_file.o \
src/mac/hmac/hmac_init.o src/mac/hmac/hmac_memory.o src/mac/hmac/hmac_memory_multi.o \
-src/mac/hmac/hmac_process.o src/mac/hmac/hmac_test.o src/mac/omac/omac_done.o src/mac/omac/omac_file.o \
-src/mac/omac/omac_init.o src/mac/omac/omac_memory.o src/mac/omac/omac_memory_multi.o \
-src/mac/omac/omac_process.o src/mac/omac/omac_test.o src/mac/pelican/pelican.o \
-src/mac/pelican/pelican_memory.o src/mac/pelican/pelican_test.o src/mac/pmac/pmac_done.o \
-src/mac/pmac/pmac_file.o src/mac/pmac/pmac_init.o src/mac/pmac/pmac_memory.o \
+src/mac/hmac/hmac_process.o src/mac/hmac/hmac_test.o src/mac/kmac/kmac.o src/mac/kmac/kmac_file.o \
+src/mac/kmac/kmac_memory.o src/mac/kmac/kmac_memory_multi.o src/mac/kmac/kmac_test.o \
+src/mac/omac/omac_done.o src/mac/omac/omac_file.o src/mac/omac/omac_init.o src/mac/omac/omac_memory.o \
+src/mac/omac/omac_memory_multi.o src/mac/omac/omac_process.o src/mac/omac/omac_test.o \
+src/mac/pelican/pelican.o src/mac/pelican/pelican_memory.o src/mac/pelican/pelican_test.o \
+src/mac/pmac/pmac_done.o src/mac/pmac/pmac_file.o src/mac/pmac/pmac_init.o src/mac/pmac/pmac_memory.o \
src/mac/pmac/pmac_memory_multi.o src/mac/pmac/pmac_ntz.o src/mac/pmac/pmac_process.o \
src/mac/pmac/pmac_shift_xor.o src/mac/pmac/pmac_test.o src/mac/poly1305/poly1305.o \
src/mac/poly1305/poly1305_file.o src/mac/poly1305/poly1305_memory.o \
diff --git a/sources.cmake b/sources.cmake
index 0d3ef35e0..4658c1b02 100644
--- a/sources.cmake
+++ b/sources.cmake
@@ -145,6 +145,11 @@ src/mac/hmac/hmac_memory.c
src/mac/hmac/hmac_memory_multi.c
src/mac/hmac/hmac_process.c
src/mac/hmac/hmac_test.c
+src/mac/kmac/kmac.c
+src/mac/kmac/kmac_file.c
+src/mac/kmac/kmac_memory.c
+src/mac/kmac/kmac_memory_multi.c
+src/mac/kmac/kmac_test.c
src/mac/omac/omac_done.c
src/mac/omac/omac_file.c
src/mac/omac/omac_init.c
diff --git a/src/hashes/sha3.c b/src/hashes/sha3.c
index 37373fb68..07965f404 100644
--- a/src/hashes/sha3.c
+++ b/src/hashes/sha3.c
@@ -446,6 +446,13 @@ int sha3_shake_done(hash_state *md, unsigned char *out, unsigned long outlen)
return s_sha3_shake_done(&md->sha3, out, outlen, 0x1f, s_keccakf);
}
+#ifdef LTC_KMAC
+int sha3_shake_done_ex(hash_state *md, unsigned char *out, unsigned long outlen, unsigned char domain)
+{
+ return s_sha3_shake_done(&md->sha3, out, outlen, domain, s_keccakf);
+}
+#endif
+
int sha3_shake128_done(hash_state *md, unsigned char *out)
{
return sha3_shake_done(md, out, 32);
diff --git a/src/headers/tomcrypt_custom.h b/src/headers/tomcrypt_custom.h
index 75e738fc9..20d0586a1 100644
--- a/src/headers/tomcrypt_custom.h
+++ b/src/headers/tomcrypt_custom.h
@@ -300,6 +300,7 @@
#define LTC_POLY1305
#define LTC_BLAKE2SMAC
#define LTC_BLAKE2BMAC
+#define LTC_KMAC
/* ---> Encrypt + Authenticate Modes <--- */
@@ -734,6 +735,10 @@
#error LTC_BLAKE2BMAC requires LTC_BLAKE2B
#endif
+#if defined(LTC_KMAC) && !defined(LTC_SHA3)
+ #error LTC_KMAC requires LTC_SHA3
+#endif
+
#if defined(LTC_SPRNG) && !defined(LTC_RNG_GET_BYTES)
#error LTC_SPRNG requires LTC_RNG_GET_BYTES
#endif
diff --git a/src/headers/tomcrypt_mac.h b/src/headers/tomcrypt_mac.h
index 039600137..49cc1ccd8 100644
--- a/src/headers/tomcrypt_mac.h
+++ b/src/headers/tomcrypt_mac.h
@@ -135,6 +135,42 @@ int blake2smac_file(const char *fname, const unsigned char *key, unsigned long k
int blake2smac_test(void);
#endif /* LTC_BLAKE2SMAC */
+#ifdef LTC_KMAC
+/* values for the `variant` argument of kmac_init() and friends */
+#define LTC_KMAC128 1
+#define LTC_KMAC256 2
+#define LTC_KMAC128_XOF 3
+#define LTC_KMAC256_XOF 4
+
+typedef struct Kmac_state {
+ hash_state sha3;
+ int xof;
+} kmac_state;
+
+int kmac_init(kmac_state *st, int variant,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *cust, unsigned long custlen);
+int kmac_process(kmac_state *st, const unsigned char *in, unsigned long inlen);
+int kmac_done(kmac_state *st, unsigned char *out, unsigned long *outlen);
+int kmac_memory(int variant,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *cust, unsigned long custlen,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen);
+int kmac_memory_multi(int variant,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *cust, unsigned long custlen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *in, unsigned long inlen, ...)
+ LTC_NULL_TERMINATED;
+int kmac_file(int variant,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *cust, unsigned long custlen,
+ const char *fname,
+ unsigned char *out, unsigned long *outlen);
+int kmac_test(void);
+#endif /* LTC_KMAC */
+
#ifdef LTC_BLAKE2BMAC
typedef hash_state blake2bmac_state;
int blake2bmac_init(blake2bmac_state *st, unsigned long outlen, const unsigned char *key, unsigned long keylen);
diff --git a/src/headers/tomcrypt_private.h b/src/headers/tomcrypt_private.h
index 6df57ef9f..a823e9b4e 100644
--- a/src/headers/tomcrypt_private.h
+++ b/src/headers/tomcrypt_private.h
@@ -197,6 +197,10 @@ int sha224_test_desc(const struct ltc_hash_descriptor *desc, const char *name);
int sha256_test_desc(const struct ltc_hash_descriptor *desc, const char *name);
#endif
+#ifdef LTC_KMAC
+int sha3_shake_done_ex(hash_state *md, unsigned char *out, unsigned long outlen, unsigned char domain);
+#endif
+
/* tomcrypt_mac.h */
int ocb3_int_ntz(unsigned long x);
diff --git a/src/mac/kmac/kmac.c b/src/mac/kmac/kmac.c
new file mode 100644
index 000000000..1a0af38ea
--- /dev/null
+++ b/src/mac/kmac/kmac.c
@@ -0,0 +1,171 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+
+#include "tomcrypt_private.h"
+
+/**
+ @file kmac.c
+ KMAC as defined in NIST SP 800-185
+*/
+
+#ifdef LTC_KMAC
+
+/* Encoding helpers from NIST SP 800-185 2.3 - encoded output never exceeds 9 bytes (8 byte payload + 1 length byte) */
+static unsigned long s_left_encode(ulong64 x, unsigned char *out)
+{
+ int i, n = 1;
+ for (i = 7; i > 0; --i) {
+ if ((x >> (i * 8)) & 0xff) {
+ n = i + 1;
+ break;
+ }
+ }
+ out[0] = (unsigned char)n;
+ for (i = 0; i < n; ++i) {
+ out[1 + i] = (unsigned char)(x >> ((n - 1 - i) * 8));
+ }
+ return (unsigned long)(n + 1);
+}
+
+static unsigned long s_right_encode(ulong64 x, unsigned char *out)
+{
+ int i, n = 1;
+ for (i = 7; i > 0; --i) {
+ if ((x >> (i * 8)) & 0xff) {
+ n = i + 1;
+ break;
+ }
+ }
+ for (i = 0; i < n; ++i) {
+ out[i] = (unsigned char)(x >> ((n - 1 - i) * 8));
+ }
+ out[n] = (unsigned char)n;
+ return (unsigned long)(n + 1);
+}
+
+static int s_feed_encode_string(hash_state *md, const unsigned char *s, unsigned long slen, unsigned long *total)
+{
+ unsigned char enc[9];
+ unsigned long enclen;
+ int err;
+ enclen = s_left_encode((ulong64)slen * 8, enc);
+ if ((err = sha3_process(md, enc, enclen)) != CRYPT_OK) return err;
+ *total += enclen;
+ if (slen != 0) {
+ if ((err = sha3_process(md, s, slen)) != CRYPT_OK) return err;
+ *total += slen;
+ }
+ return CRYPT_OK;
+}
+
+static int s_feed_bytepad_zero_fill(hash_state *md, unsigned long rate, unsigned long total)
+{
+ unsigned long pad = (rate - (total % rate)) % rate;
+ if (pad != 0) {
+ unsigned char zeros[168]; /* >= max SHAKE rate */
+ XMEMSET(zeros, 0, sizeof(zeros));
+ return sha3_process(md, zeros, pad);
+ }
+ return CRYPT_OK;
+}
+
+static int s_feed_bytepad_prefix(hash_state *md, unsigned long rate, unsigned long *total)
+{
+ unsigned char enc[9];
+ unsigned long enclen = s_left_encode(rate, enc);
+ int err = sha3_process(md, enc, enclen);
+ if (err == CRYPT_OK) *total = enclen;
+ return err;
+}
+
+/**
+ Initialize a KMAC context
+
+ @param st The KMAC state
+ @param variant one of LTC_KMAC128, LTC_KMAC256, LTC_KMAC128_XOF, LTC_KMAC256_XOF
+ @param key The secret key
+ @param keylen The length of the secret key (octets)
+ @param cust Optional customization string (may be NULL when custlen == 0)
+ @param custlen Length of the customization string (octets)
+ @return CRYPT_OK if successful
+*/
+int kmac_init(kmac_state *st, int variant,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *cust, unsigned long custlen)
+{
+ static const unsigned char kmac_name[4] = { 'K', 'M', 'A', 'C' };
+ int err, num;
+ unsigned long rate, total;
+
+ LTC_ARGCHK(st != NULL);
+ LTC_ARGCHK(key != NULL || keylen == 0);
+ LTC_ARGCHK(cust != NULL || custlen == 0);
+
+ switch (variant) {
+ case LTC_KMAC128: num = 128; st->xof = 0; rate = 168; break;
+ case LTC_KMAC256: num = 256; st->xof = 0; rate = 136; break;
+ case LTC_KMAC128_XOF: num = 128; st->xof = 1; rate = 168; break;
+ case LTC_KMAC256_XOF: num = 256; st->xof = 1; rate = 136; break;
+ default: return CRYPT_INVALID_ARG;
+ }
+
+ if ((err = sha3_shake_init(&st->sha3, num)) != CRYPT_OK) return err;
+
+ /* bytepad(encode_string("KMAC") || encode_string(cust), rate) */
+ if ((err = s_feed_bytepad_prefix(&st->sha3, rate, &total)) != CRYPT_OK) return err;
+ if ((err = s_feed_encode_string(&st->sha3, kmac_name, sizeof(kmac_name), &total)) != CRYPT_OK) return err;
+ if ((err = s_feed_encode_string(&st->sha3, cust, custlen, &total)) != CRYPT_OK) return err;
+ if ((err = s_feed_bytepad_zero_fill(&st->sha3, rate, total)) != CRYPT_OK) return err;
+
+ /* bytepad(encode_string(key), rate) */
+ if ((err = s_feed_bytepad_prefix(&st->sha3, rate, &total)) != CRYPT_OK) return err;
+ if ((err = s_feed_encode_string(&st->sha3, key, keylen, &total)) != CRYPT_OK) return err;
+ if ((err = s_feed_bytepad_zero_fill(&st->sha3, rate, total)) != CRYPT_OK) return err;
+
+ return CRYPT_OK;
+}
+
+/**
+ Process data through KMAC
+
+ @param st The KMAC state
+ @param in The data to authenticate
+ @param inlen The length of the data (octets)
+ @return CRYPT_OK if successful
+*/
+int kmac_process(kmac_state *st, const unsigned char *in, unsigned long inlen)
+{
+ if (inlen == 0) return CRYPT_OK;
+ LTC_ARGCHK(st != NULL);
+ LTC_ARGCHK(in != NULL);
+ return sha3_process(&st->sha3, in, inlen);
+}
+
+/**
+ Terminate a KMAC session
+
+ @param st The KMAC state
+ @param out [out] The destination of the MAC
+ @param outlen [in/out] The requested length on entry, the produced length on return
+ @return CRYPT_OK if successful
+*/
+int kmac_done(kmac_state *st, unsigned char *out, unsigned long *outlen)
+{
+ unsigned char enc[9];
+ unsigned long enclen;
+ int err;
+ ulong64 L;
+
+ LTC_ARGCHK(st != NULL);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ /* SP 800-185 4: append right_encode(L) where L is the requested output length in bits or right_encode(0) for the XOF flavour */
+ L = st->xof ? 0 : (ulong64)(*outlen) * 8;
+ enclen = s_right_encode(L, enc);
+ if ((err = sha3_process(&st->sha3, enc, enclen)) != CRYPT_OK) return err;
+
+ return sha3_shake_done_ex(&st->sha3, out, *outlen, 0x04);
+}
+
+#endif
diff --git a/src/mac/kmac/kmac_file.c b/src/mac/kmac/kmac_file.c
new file mode 100644
index 000000000..c3cc1fafb
--- /dev/null
+++ b/src/mac/kmac/kmac_file.c
@@ -0,0 +1,90 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+
+#include "tomcrypt_private.h"
+
+#ifdef LTC_KMAC
+
+/**
+ KMAC a file
+
+ @param variant 128 or 256 (optionally OR-ed with LTC_KMAC_XOF)
+ @param key The secret key
+ @param keylen The length of the secret key (octets)
+ @param cust Optional customization string (may be NULL when custlen == 0)
+ @param custlen Length of the customization string (octets)
+ @param fname The name of the file you wish to MAC
+ @param out [out] Destination of the MAC
+ @param outlen [in/out] Requested length on entry, produced length on return
+ @return CRYPT_OK if successful, CRYPT_NOP if file support has been disabled
+*/
+int kmac_file(int variant,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *cust, unsigned long custlen,
+ const char *fname,
+ unsigned char *out, unsigned long *outlen)
+{
+#ifdef LTC_NO_FILE
+ LTC_UNUSED_PARAM(variant);
+ LTC_UNUSED_PARAM(key);
+ LTC_UNUSED_PARAM(keylen);
+ LTC_UNUSED_PARAM(cust);
+ LTC_UNUSED_PARAM(custlen);
+ LTC_UNUSED_PARAM(fname);
+ LTC_UNUSED_PARAM(out);
+ LTC_UNUSED_PARAM(outlen);
+ return CRYPT_NOP;
+#else
+ kmac_state st;
+ FILE *in;
+ unsigned char *buf;
+ size_t x;
+ int err;
+
+ LTC_ARGCHK(fname != NULL);
+ LTC_ARGCHK(key != NULL || keylen == 0);
+ LTC_ARGCHK(cust != NULL || custlen == 0);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ if ((buf = XMALLOC(LTC_FILE_READ_BUFSIZE)) == NULL) {
+ return CRYPT_MEM;
+ }
+
+ if ((err = kmac_init(&st, variant, key, keylen, cust, custlen)) != CRYPT_OK) {
+ goto LBL_ERR;
+ }
+
+ in = fopen(fname, "rb");
+ if (in == NULL) {
+ err = CRYPT_FILE_NOTFOUND;
+ goto LBL_ERR;
+ }
+
+ do {
+ x = fread(buf, 1, LTC_FILE_READ_BUFSIZE, in);
+ if ((err = kmac_process(&st, buf, (unsigned long)x)) != CRYPT_OK) {
+ fclose(in);
+ goto LBL_CLEANBUF;
+ }
+ } while (x == LTC_FILE_READ_BUFSIZE);
+
+ if (fclose(in) != 0) {
+ err = CRYPT_ERROR;
+ goto LBL_CLEANBUF;
+ }
+
+ err = kmac_done(&st, out, outlen);
+
+LBL_CLEANBUF:
+ zeromem(buf, LTC_FILE_READ_BUFSIZE);
+LBL_ERR:
+#ifdef LTC_CLEAN_STACK
+ zeromem(&st, sizeof(st));
+#endif
+ XFREE(buf);
+ return err;
+#endif
+}
+
+#endif
diff --git a/src/mac/kmac/kmac_memory.c b/src/mac/kmac/kmac_memory.c
new file mode 100644
index 000000000..0b950460f
--- /dev/null
+++ b/src/mac/kmac/kmac_memory.c
@@ -0,0 +1,47 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+
+#include "tomcrypt_private.h"
+
+#ifdef LTC_KMAC
+
+/**
+ KMAC a block of memory to produce the authentication tag
+
+ @param variant 128 or 256 (optionally OR-ed with LTC_KMAC_XOF)
+ @param key The secret key
+ @param keylen The length of the secret key (octets)
+ @param cust Optional customization string (may be NULL when custlen == 0)
+ @param custlen Length of the customization string (octets)
+ @param in The data to authenticate
+ @param inlen The length of the data (octets)
+ @param out [out] Destination of the MAC
+ @param outlen [in/out] Requested length on entry, produced length on return
+ @return CRYPT_OK if successful
+*/
+int kmac_memory(int variant,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *cust, unsigned long custlen,
+ const unsigned char *in, unsigned long inlen,
+ unsigned char *out, unsigned long *outlen)
+{
+ kmac_state st;
+ int err;
+
+ LTC_ARGCHK(key != NULL || keylen == 0);
+ LTC_ARGCHK(cust != NULL || custlen == 0);
+ LTC_ARGCHK(in != NULL || inlen == 0);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ if ((err = kmac_init(&st, variant, key, keylen, cust, custlen)) != CRYPT_OK) goto LBL_ERR;
+ if ((err = kmac_process(&st, in, inlen)) != CRYPT_OK) goto LBL_ERR;
+ err = kmac_done(&st, out, outlen);
+LBL_ERR:
+#ifdef LTC_CLEAN_STACK
+ zeromem(&st, sizeof(st));
+#endif
+ return err;
+}
+
+#endif
diff --git a/src/mac/kmac/kmac_memory_multi.c b/src/mac/kmac/kmac_memory_multi.c
new file mode 100644
index 000000000..f5ba283c4
--- /dev/null
+++ b/src/mac/kmac/kmac_memory_multi.c
@@ -0,0 +1,60 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+
+#include "tomcrypt_private.h"
+
+#ifdef LTC_KMAC
+
+/**
+ KMAC multiple blocks of memory to produce the authentication tag
+
+ @param variant 128 or 256 (optionally OR-ed with LTC_KMAC_XOF)
+ @param key The secret key
+ @param keylen The length of the secret key (octets)
+ @param cust Optional customization string (may be NULL when custlen == 0)
+ @param custlen Length of the customization string (octets)
+ @param out [out] Destination of the MAC
+ @param outlen [in/out] Requested length on entry, produced length on return
+ @param in The data to authenticate
+ @param inlen The length of the data (octets)
+ @param ... tuples of (data, len) pairs to authenticate, terminated with (NULL, x)
+ @return CRYPT_OK if successful
+*/
+int kmac_memory_multi(int variant,
+ const unsigned char *key, unsigned long keylen,
+ const unsigned char *cust, unsigned long custlen,
+ unsigned char *out, unsigned long *outlen,
+ const unsigned char *in, unsigned long inlen, ...)
+{
+ kmac_state st;
+ int err;
+ va_list args;
+ const unsigned char *curptr;
+ unsigned long curlen;
+
+ LTC_ARGCHK(key != NULL || keylen == 0);
+ LTC_ARGCHK(cust != NULL || custlen == 0);
+ LTC_ARGCHK(in != NULL || inlen == 0);
+ LTC_ARGCHK(out != NULL);
+ LTC_ARGCHK(outlen != NULL);
+
+ va_start(args, inlen);
+ curptr = in;
+ curlen = inlen;
+ if ((err = kmac_init(&st, variant, key, keylen, cust, custlen)) != CRYPT_OK) goto LBL_ERR;
+ for (;;) {
+ if ((err = kmac_process(&st, curptr, curlen)) != CRYPT_OK) goto LBL_ERR;
+ curptr = va_arg(args, const unsigned char*);
+ if (curptr == NULL) break;
+ curlen = va_arg(args, unsigned long);
+ }
+ err = kmac_done(&st, out, outlen);
+LBL_ERR:
+#ifdef LTC_CLEAN_STACK
+ zeromem(&st, sizeof(st));
+#endif
+ va_end(args);
+ return err;
+}
+
+#endif
diff --git a/src/mac/kmac/kmac_test.c b/src/mac/kmac/kmac_test.c
new file mode 100644
index 000000000..cf8a003df
--- /dev/null
+++ b/src/mac/kmac/kmac_test.c
@@ -0,0 +1,123 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
+/* SPDX-License-Identifier: Unlicense */
+
+#include "tomcrypt_private.h"
+
+#ifdef LTC_KMAC
+
+/* Test vectors from NIST SP 800-185 sample document (KMAC_samples.pdf) */
+int kmac_test(void)
+{
+#ifndef LTC_TEST
+ return CRYPT_NOP;
+#else
+ static const unsigned char key[32] = {
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F
+ };
+ static const unsigned char data4[4] = { 0x00, 0x01, 0x02, 0x03 };
+ static unsigned char data200[200];
+ static const char tag_app[] = "My Tagged Application";
+
+ static const struct {
+ int variant;
+ unsigned long custlen;
+ unsigned long inlen;
+ unsigned long outlen;
+ const unsigned char *in;
+ unsigned char out[64];
+ } tests[] = {
+ /* sample #1: KMAC128, S="" */
+ { LTC_KMAC128, 0, 4, 32, data4, {
+ 0xE5, 0x78, 0x0B, 0x0D, 0x3E, 0xA6, 0xF7, 0xD3, 0xA4, 0x29, 0xC5, 0x70, 0x6A, 0xA4, 0x3A, 0x00,
+ 0xFA, 0xDB, 0xD7, 0xD4, 0x96, 0x28, 0x83, 0x9E, 0x31, 0x87, 0x24, 0x3F, 0x45, 0x6E, 0xE1, 0x4E } },
+ /* sample #2: KMAC128, S="My Tagged Application" */
+ { LTC_KMAC128, 21, 4, 32, data4, {
+ 0x3B, 0x1F, 0xBA, 0x96, 0x3C, 0xD8, 0xB0, 0xB5, 0x9E, 0x8C, 0x1A, 0x6D, 0x71, 0x88, 0x8B, 0x71,
+ 0x43, 0x65, 0x1A, 0xF8, 0xBA, 0x0A, 0x70, 0x70, 0xC0, 0x97, 0x9E, 0x28, 0x11, 0x32, 0x4A, 0xA5 } },
+ /* sample #3: KMAC128, 200-byte input, S="My Tagged Application" */
+ { LTC_KMAC128, 21, 200, 32, data200, {
+ 0x1F, 0x5B, 0x4E, 0x6C, 0xCA, 0x02, 0x20, 0x9E, 0x0D, 0xCB, 0x5C, 0xA6, 0x35, 0xB8, 0x9A, 0x15,
+ 0xE2, 0x71, 0xEC, 0xC7, 0x60, 0x07, 0x1D, 0xFD, 0x80, 0x5F, 0xAA, 0x38, 0xF9, 0x72, 0x92, 0x30 } },
+ /* sample #4: KMAC256, S="My Tagged Application" */
+ { LTC_KMAC256, 21, 4, 64, data4, {
+ 0x20, 0xC5, 0x70, 0xC3, 0x13, 0x46, 0xF7, 0x03, 0xC9, 0xAC, 0x36, 0xC6, 0x1C, 0x03, 0xCB, 0x64,
+ 0xC3, 0x97, 0x0D, 0x0C, 0xFC, 0x78, 0x7E, 0x9B, 0x79, 0x59, 0x9D, 0x27, 0x3A, 0x68, 0xD2, 0xF7,
+ 0xF6, 0x9D, 0x4C, 0xC3, 0xDE, 0x9D, 0x10, 0x4A, 0x35, 0x16, 0x89, 0xF2, 0x7C, 0xF6, 0xF5, 0x95,
+ 0x1F, 0x01, 0x03, 0xF3, 0x3F, 0x4F, 0x24, 0x87, 0x10, 0x24, 0xD9, 0xC2, 0x77, 0x73, 0xA8, 0xDD } },
+ /* sample #5: KMAC256, 200-byte input, S="" */
+ { LTC_KMAC256, 0, 200, 64, data200, {
+ 0x75, 0x35, 0x8C, 0xF3, 0x9E, 0x41, 0x49, 0x4E, 0x94, 0x97, 0x07, 0x92, 0x7C, 0xEE, 0x0A, 0xF2,
+ 0x0A, 0x3F, 0xF5, 0x53, 0x90, 0x4C, 0x86, 0xB0, 0x8F, 0x21, 0xCC, 0x41, 0x4B, 0xCF, 0xD6, 0x91,
+ 0x58, 0x9D, 0x27, 0xCF, 0x5E, 0x15, 0x36, 0x9C, 0xBB, 0xFF, 0x8B, 0x9A, 0x4C, 0x2E, 0xB1, 0x78,
+ 0x00, 0x85, 0x5D, 0x02, 0x35, 0xFF, 0x63, 0x5D, 0xA8, 0x25, 0x33, 0xEC, 0x6B, 0x75, 0x9B, 0x69 } },
+ /* sample #7: KMACXOF128, S="" */
+ { LTC_KMAC128_XOF, 0, 4, 32, data4, {
+ 0xCD, 0x83, 0x74, 0x0B, 0xBD, 0x92, 0xCC, 0xC8, 0xCF, 0x03, 0x2B, 0x14, 0x81, 0xA0, 0xF4, 0x46,
+ 0x0E, 0x7C, 0xA9, 0xDD, 0x12, 0xB0, 0x8A, 0x0C, 0x40, 0x31, 0x17, 0x8B, 0xAC, 0xD6, 0xEC, 0x35 } },
+ /* sample #8: KMACXOF128, S="My Tagged Application" */
+ { LTC_KMAC128_XOF, 21, 4, 32, data4, {
+ 0x31, 0xA4, 0x45, 0x27, 0xB4, 0xED, 0x9F, 0x5C, 0x61, 0x01, 0xD1, 0x1D, 0xE6, 0xD2, 0x6F, 0x06,
+ 0x20, 0xAA, 0x5C, 0x34, 0x1D, 0xEF, 0x41, 0x29, 0x96, 0x57, 0xFE, 0x9D, 0xF1, 0xA3, 0xB1, 0x6C } },
+ /* sample #9: KMACXOF128, 200-byte input, S="My Tagged Application" */
+ { LTC_KMAC128_XOF, 21, 200, 32, data200, {
+ 0x47, 0x02, 0x6C, 0x7C, 0xD7, 0x93, 0x08, 0x4A, 0xA0, 0x28, 0x3C, 0x25, 0x3E, 0xF6, 0x58, 0x49,
+ 0x0C, 0x0D, 0xB6, 0x14, 0x38, 0xB8, 0x32, 0x6F, 0xE9, 0xBD, 0xDF, 0x28, 0x1B, 0x83, 0xAE, 0x0F } },
+ /* sample #10: KMACXOF256, S="My Tagged Application" */
+ { LTC_KMAC256_XOF, 21, 4, 64, data4, {
+ 0x17, 0x55, 0x13, 0x3F, 0x15, 0x34, 0x75, 0x2A, 0xAD, 0x07, 0x48, 0xF2, 0xC7, 0x06, 0xFB, 0x5C,
+ 0x78, 0x45, 0x12, 0xCA, 0xB8, 0x35, 0xCD, 0x15, 0x67, 0x6B, 0x16, 0xC0, 0xC6, 0x64, 0x7F, 0xA9,
+ 0x6F, 0xAA, 0x7A, 0xF6, 0x34, 0xA0, 0xBF, 0x8F, 0xF6, 0xDF, 0x39, 0x37, 0x4F, 0xA0, 0x0F, 0xAD,
+ 0x9A, 0x39, 0xE3, 0x22, 0xA7, 0xC9, 0x20, 0x65, 0xA6, 0x4E, 0xB1, 0xFB, 0x08, 0x01, 0xEB, 0x2B } },
+ /* sample #11: KMACXOF256, 200-byte input, S="" */
+ { LTC_KMAC256_XOF, 0, 200, 64, data200, {
+ 0xFF, 0x7B, 0x17, 0x1F, 0x1E, 0x8A, 0x2B, 0x24, 0x68, 0x3E, 0xED, 0x37, 0x83, 0x0E, 0xE7, 0x97,
+ 0x53, 0x8B, 0xA8, 0xDC, 0x56, 0x3F, 0x6D, 0xA1, 0xE6, 0x67, 0x39, 0x1A, 0x75, 0xED, 0xC0, 0x2C,
+ 0xA6, 0x33, 0x07, 0x9F, 0x81, 0xCE, 0x12, 0xA2, 0x5F, 0x45, 0x61, 0x5E, 0xC8, 0x99, 0x72, 0x03,
+ 0x1D, 0x18, 0x33, 0x73, 0x31, 0xD2, 0x4C, 0xEB, 0x8F, 0x8C, 0xA8, 0xE6, 0xA1, 0x9F, 0xD9, 0x8B } }
+ };
+ unsigned int i;
+ unsigned char buf[64];
+ unsigned long buflen;
+ int err;
+
+ for (i = 0; i < sizeof(data200); ++i) data200[i] = (unsigned char)i;
+
+ for (i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) {
+ const unsigned char *cust = (tests[i].custlen == 0) ? NULL : (const unsigned char*)tag_app;
+ buflen = tests[i].outlen;
+ if ((err = kmac_memory(tests[i].variant, key, sizeof(key), cust, tests[i].custlen,
+ tests[i].in, tests[i].inlen, buf, &buflen)) != CRYPT_OK) return err;
+ if (ltc_compare_testvector(buf, buflen, tests[i].out, tests[i].outlen, "kmac", (int)i) != 0) {
+ return CRYPT_FAIL_TESTVECTOR;
+ }
+ }
+
+ /* sample #6: KMAC256, 200-byte input, S="My Tagged Application", processed one byte at a time */
+ {
+ static const unsigned char sample6[64] = {
+ 0xB5, 0x86, 0x18, 0xF7, 0x1F, 0x92, 0xE1, 0xD5, 0x6C, 0x1B, 0x8C, 0x55, 0xDD, 0xD7, 0xCD, 0x18,
+ 0x8B, 0x97, 0xB4, 0xCA, 0x4D, 0x99, 0x83, 0x1E, 0xB2, 0x69, 0x9A, 0x83, 0x7D, 0xA2, 0xE4, 0xD9,
+ 0x70, 0xFB, 0xAC, 0xFD, 0xE5, 0x00, 0x33, 0xAE, 0xA5, 0x85, 0xF1, 0xA2, 0x70, 0x85, 0x10, 0xC3,
+ 0x2D, 0x07, 0x88, 0x08, 0x01, 0xBD, 0x18, 0x28, 0x98, 0xFE, 0x47, 0x68, 0x76, 0xFC, 0x89, 0x65
+ };
+ kmac_state st;
+ unsigned int j;
+ if ((err = kmac_init(&st, LTC_KMAC256, key, sizeof(key), (const unsigned char*)tag_app, 21)) != CRYPT_OK) return err;
+ for (j = 0; j < sizeof(data200); ++j) {
+ if ((err = kmac_process(&st, &data200[j], 1)) != CRYPT_OK) return err;
+ }
+ buflen = 64;
+ if ((err = kmac_done(&st, buf, &buflen)) != CRYPT_OK) return err;
+ if (ltc_compare_testvector(buf, buflen, sample6, sizeof(sample6), "kmac stream", 0) != 0) {
+ return CRYPT_FAIL_TESTVECTOR;
+ }
+ }
+
+ return CRYPT_OK;
+#endif
+}
+
+#endif
diff --git a/src/misc/crypt/crypt.c b/src/misc/crypt/crypt.c
index a670d69bb..0466e61d8 100644
--- a/src/misc/crypt/crypt.c
+++ b/src/misc/crypt/crypt.c
@@ -285,6 +285,9 @@ const char *crypt_build_settings =
#if defined(LTC_BLAKE2BMAC)
" BLAKE2B MAC\n"
#endif
+#if defined(LTC_KMAC)
+ " KMAC\n"
+#endif
"\nENC + AUTH modes:\n"
#if defined(LTC_EAX_MODE)
diff --git a/tests/mac_test.c b/tests/mac_test.c
index d1c0307c6..1bb6f2cff 100644
--- a/tests/mac_test.c
+++ b/tests/mac_test.c
@@ -47,6 +47,9 @@ int mac_test(void)
#ifdef LTC_BLAKE2BMAC
DO(blake2bmac_test());
#endif
+#ifdef LTC_KMAC
+ DO(kmac_test());
+#endif
#ifdef LTC_SIV_MODE
DO(siv_test());
DO(siv_wycheproof_test());