Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions doc/crypt.tex
Original file line number Diff line number Diff line change
Expand Up @@ -3143,6 +3143,21 @@ \subsection{TurboSHAKE}
The init function \code{turbo\_shake\_init()} is implemented as a macro which calls \code{sha3\_shake\_init()}.


\subsection{KangarooTwelve}
Another variation of SHA3 SHAKE is KangarooTwelve, which has been specified in \href{https://datatracker.ietf.org/doc/rfc9861/}{\texttt{RFC 9861}}.

The API works equivalent to the one of SHA3 SHAKE, where the APIs only have a different name. Additionally, KangarooTwelve supports customization. You can append any or none customization bytes after all input bytes and before squeezing any output digest bytes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can one also append customization bytes in multiple calls?

I'm just asking.

And is the result of kt_customization("foobar"); the same as of kt_customization("foo"); kt_customization("bar");?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so the RFC specifies that we MAY propose an incremental interface, for either M or C, so we should also document that somewhere what we support.

I just had a look at the RFC for the first time ... the D isn't fixed to 0x1F, maybe it'd make sense to add support for variable D in a future PR. But now we focus on KT.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the customization could be appended multiple times. And yes, about the foobar==foo+bar. You can append unlimited amount of customization bytes, up to unsigned long count, if you want to append more, we should change the variable type holding the number of customization bytes added. To 128 bit number or even larger one. The project https://github.com/kerukuro/digestpp is quite limited in this regard, it holds the entire customization string in memory at once. My implementation holds only the SHAKE state. They also hold a 8 kB buffer, I hold a SHAKE state instead.


\begin{small}
\begin{verbatim}
int kangaroo_twelve_init(hash_state *md, int num);
int kangaroo_twelve_process(hash_state *md, const unsigned char *in, unsigned long inlen);
int kangaroo_twelve_customization(hash_state *md, const unsigned char *in, unsigned long inlen);
int kangaroo_twelve_done(hash_state *md, unsigned char *out, unsigned long outlen);
\end{verbatim}
\end{small}


\mysection{Extended Tiger API}

The Tiger and Tiger2 hash algorithms \url{http://www.cs.technion.ac.il/~biham/Reports/Tiger/} specify the possibility to run the algorithm with
Expand Down
158 changes: 153 additions & 5 deletions src/hashes/sha3.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,19 +353,19 @@ int keccak_done(hash_state *md, unsigned char *out)
#endif

#ifdef LTC_SHA3
static LTC_INLINE int s_sha3_shake_done(hash_state *md, unsigned char *out, unsigned long outlen, process_fn proc_f)
static LTC_INLINE int s_sha3_shake_done(hash_state *md, unsigned char *out, unsigned long outlen, unsigned char domain, process_fn proc_f)
{
/* IMPORTANT NOTE: sha3_shake_done can be called many times */
unsigned long idx;
unsigned i;

if (outlen == 0) return CRYPT_OK; /* nothing to do */
LTC_ARGCHK(md != NULL);
LTC_ARGCHK(md != NULL);
LTC_ARGCHK(out != NULL);

if (!md->sha3.xof_flag) {
/* shake_xof operation must be done only once */
md->sha3.s[md->sha3.word_index] ^= (md->sha3.saved ^ (CONST64(0x1F) << (md->sha3.byte_index * 8)));
md->sha3.s[md->sha3.word_index] ^= (md->sha3.saved ^ (((ulong64)(domain)) << (md->sha3.byte_index * 8)));
md->sha3.s[SHA3_KECCAK_SPONGE_WORDS - md->sha3.capacity_words - 1] ^= CONST64(0x8000000000000000);
proc_f(md->sha3.s);
/* store sha3.s[] as little-endian bytes into sha3.sb */
Expand All @@ -392,13 +392,13 @@ static LTC_INLINE int s_sha3_shake_done(hash_state *md, unsigned char *out, unsi

int sha3_shake_done(hash_state *md, unsigned char *out, unsigned long outlen)
{
return s_sha3_shake_done(md, out, outlen, s_keccakf);
return s_sha3_shake_done(md, out, outlen, 0x1f, s_keccakf);
}

#if defined LTC_TURBO_SHAKE
int turbo_shake_done(hash_state *md, unsigned char *out, unsigned long outlen)
{
return s_sha3_shake_done(md, out, outlen, s_keccak_turbo_f);
return s_sha3_shake_done(md, out, outlen, 0x1f, s_keccak_turbo_f);
}
#endif

Expand All @@ -416,4 +416,152 @@ int sha3_shake_memory(int num, const unsigned char *in, unsigned long inlen, uns
}
#endif

#ifdef LTC_KANGAROO_TWELVE

static const unsigned char kangaroo_twelve_filler[] = { 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

int kangaroo_twelve_init(hash_state *md, int num)
{
int err;

LTC_ARGCHK(md != NULL);
LTC_ARGCHK(num == 128 || num == 256);

if ((err = sha3_shake_init((hash_state*)&md->kt.outer, num)) != CRYPT_OK) return err;
if ((err = sha3_shake_init((hash_state*)&md->kt.inner, num)) != CRYPT_OK) return err;
md->kt.blocks_count = 0;
md->kt.customization_len = 0;
md->kt.remaining = 8 * 1024;
md->kt.phase = 0;
md->kt.finished = 0;
return CRYPT_OK;
}

static LTC_INLINE int s_kangaroo_twelve_process(hash_state *md, const unsigned char *in, unsigned long inlen)
{
unsigned long rem;
unsigned long amount;
int err;
int variant;
int digest_len;
unsigned char digest_buf[64];

LTC_ARGCHK(md != NULL);
LTC_ARGCHK(in != NULL || inlen == 0);

if (md->kt.phase == 0)
{
rem = md->kt.remaining;
amount = rem < inlen ? rem : inlen;
md->kt.remaining -= amount;
if ((err = turbo_shake_process((hash_state*)&md->kt.outer, in, amount)) != CRYPT_OK) return err;
in += amount;
inlen -= amount;
if (md->kt.remaining == 0 && inlen != 0)
{
md->kt.remaining = 8 * 1024;
md->kt.phase = 1;
md->kt.blocks_count += 1;
if ((err = turbo_shake_process((hash_state*)&md->kt.outer, kangaroo_twelve_filler, sizeof(kangaroo_twelve_filler))) != CRYPT_OK) return err;
}
}
if (md->kt.phase == 1)
{
do
{
rem = md->kt.remaining;
amount = rem < inlen ? rem : inlen;
md->kt.remaining -= amount;
if ((err = turbo_shake_process((hash_state*)&md->kt.inner, in, amount)) != CRYPT_OK) return err;
in += amount;
inlen -= amount;
if (md->kt.remaining == 0 && inlen != 0)
{
md->kt.remaining = 8 * 1024;
md->kt.blocks_count += 1;
assert(md->kt.outer.capacity_words == 4 || md->kt.outer.capacity_words == 8);
variant = md->kt.outer.capacity_words == 4 ? 128 : 256;
digest_len = variant == 128 ? 32 : 64;
if ((err = s_sha3_shake_done((hash_state*)&md->kt.inner, digest_buf, digest_len, 0x0b, s_keccak_turbo_f)) != CRYPT_OK) return err;
if ((err = turbo_shake_init((hash_state*)&md->kt.inner, variant)) != CRYPT_OK) return err;
if ((err = turbo_shake_process((hash_state*)&md->kt.outer, digest_buf, digest_len)) != CRYPT_OK) return err;
}
} while (inlen != 0);
}
return CRYPT_OK;
}

int kangaroo_twelve_process(hash_state *md, const unsigned char *in, unsigned long inlen)
{
LTC_ARGCHK(md != NULL);
LTC_ARGCHK(in != NULL || inlen == 0);
LTC_ARGCHK(md->kt.customization_len == 0);
LTC_ARGCHK(md->kt.finished == 0);

return s_kangaroo_twelve_process(md, in, inlen);
}

int kangaroo_twelve_customization(hash_state *md, const unsigned char *in, unsigned long inlen)
{
LTC_ARGCHK(md != NULL);
LTC_ARGCHK(in != NULL || inlen == 0);
LTC_ARGCHK(md->kt.finished == 0);

md->kt.customization_len += inlen;
return s_kangaroo_twelve_process(md, in, inlen);
}

int kangaroo_twelve_done(hash_state *md, unsigned char *out, unsigned long outlen)
{
int couner_len;
unsigned char couner_buf[sizeof(ulong64) + 1];
int err;
int variant;
int digest_len;
unsigned char digest_buf[64];
unsigned char ffff[2];
unsigned char domain;

LTC_ARGCHK(md != NULL);
LTC_ARGCHK(out != NULL || outlen == 0);

if (md->kt.finished == 0)
{
md->kt.finished = 1;
couner_len = 0;
while (md->kt.customization_len != 0)
{
couner_buf[LTC_ARRAY_SIZE(couner_buf) - 1 - 1 - couner_len] = md->kt.customization_len & 0xff;
md->kt.customization_len >>= 8;
++couner_len;
}
couner_buf[LTC_ARRAY_SIZE(couner_buf) - 1] = couner_len;
if ((err = s_kangaroo_twelve_process(md, &couner_buf[LTC_ARRAY_SIZE(couner_buf) - 1 - couner_len], couner_len + 1)) != CRYPT_OK) return err;
if(md->kt.phase != 0)
{
assert(md->kt.outer.capacity_words == 4 || md->kt.outer.capacity_words == 8);
variant = md->kt.outer.capacity_words == 4 ? 128 : 256;
digest_len = variant == 128 ? 32 : 64;
if ((err = s_sha3_shake_done((hash_state*)&md->kt.inner, digest_buf, digest_len, 0x0b, s_keccak_turbo_f)) != CRYPT_OK) return err;
if ((err = turbo_shake_process((hash_state*)&md->kt.outer, digest_buf, digest_len)) != CRYPT_OK) return err;
couner_len = 0;
while (md->kt.blocks_count != 0)
{
couner_buf[LTC_ARRAY_SIZE(couner_buf) - 1 - 1 - couner_len] = md->kt.blocks_count & 0xff;
md->kt.blocks_count >>= 8;
++couner_len;
}
couner_buf[LTC_ARRAY_SIZE(couner_buf) - 1] = couner_len;
if ((err = turbo_shake_process((hash_state*)&md->kt.outer, &couner_buf[LTC_ARRAY_SIZE(couner_buf) - 1 - couner_len], couner_len + 1)) != CRYPT_OK) return err;
ffff[0] = 0xff;
ffff[1] = 0xff;
if ((err = turbo_shake_process((hash_state*)&md->kt.outer, ffff, LTC_ARRAY_SIZE(ffff))) != CRYPT_OK) return err;
}
}
domain = md->kt.phase == 0 ? 0x07 : 0x06;
return s_sha3_shake_done(md, out, outlen, domain, s_keccak_turbo_f);
}

#endif

#endif
Loading