Skip to content

Commit

Permalink
Update SIV a bit
Browse files Browse the repository at this point in the history
* The number of AAD components per SIV operation must not exceed 126.
* Init OMAC only once per SIV operation.
  All OMAC operations start off with the same key. Instead of
  re-initializing the OMAC context for each operation, init once and
  store the context.
* Add SIV to timing demo.
* Add 1000-times-encrypt-then-decrypt test for SIV.
* Update docs.

Signed-off-by: Steffen Jaeckel <[email protected]>
  • Loading branch information
sjaeckel committed Dec 28, 2024
1 parent 8bdc093 commit be8c4ea
Show file tree
Hide file tree
Showing 4 changed files with 397 additions and 78 deletions.
43 changes: 39 additions & 4 deletions demos/timing.c
Original file line number Diff line number Diff line change
Expand Up @@ -1153,8 +1153,13 @@ static void time_macs(void)

static void time_encmacs_(unsigned long MAC_SIZE)
{
#if defined(LTC_EAX_MODE) || defined(LTC_OCB_MODE) || defined(LTC_OCB3_MODE) || defined(LTC_CCM_MODE) || defined(LTC_GCM_MODE)
unsigned char *buf, IV[16], key[16], tag[16];
#if defined(LTC_EAX_MODE) || defined(LTC_OCB_MODE) || defined(LTC_OCB3_MODE) || \
defined(LTC_CCM_MODE) || defined(LTC_GCM_MODE) || defined(LTC_SIV_MODE)
#if defined(LTC_SIV_MODE)
unsigned char *aad[4];
unsigned long buflen;
#endif
unsigned char *buf, IV[16], key[32], tag[16];
ulong64 t1, t2;
unsigned long x, z;
int err, cipher_idx;
Expand All @@ -1171,8 +1176,8 @@ static void time_encmacs_(unsigned long MAC_SIZE)
cipher_idx = find_cipher("aes");

yarrow_read(buf, MAC_SIZE*1024, &yarrow_prng);
yarrow_read(key, 16, &yarrow_prng);
yarrow_read(IV, 16, &yarrow_prng);
yarrow_read(key, sizeof(key), &yarrow_prng);
yarrow_read(IV, sizeof(IV), &yarrow_prng);

#ifdef LTC_EAX_MODE
t2 = -1;
Expand Down Expand Up @@ -1308,8 +1313,38 @@ __attribute__ ((aligned (16)))
}
fprintf(stderr, "GCM (precomp)\t\t%9"PRI64"u\n", t2/(ulong64)(MAC_SIZE*1024));
}
#endif

#ifdef LTC_SIV_MODE
for(z = 0; z < 4; z++) {
aad[z] = IV + z * 4;
}
for(z = 0; z < 4; z++) {
t2 = -1;
for (x = 0; x < 10000; x++) {
buflen = MAC_SIZE*1024;
t_start();
t1 = t_read();
if ((err = siv_memory(cipher_idx, LTC_ENCRYPT,
key, 32,
buf, MAC_SIZE*1024 - 16,
buf, &buflen,
aad[0], 16,
aad[1], 12,
aad[2], 8,
aad[3], 4,
NULL)) != CRYPT_OK) {
fprintf(stderr, "\nSIV error... %s\n", error_to_string(err));
exit(EXIT_FAILURE);
}
t1 = t_read() - t1;
if (t1 < t2) t2 = t1;
}
aad[3-z] = NULL;
fprintf(stderr, "SIV (%lu x AAD)\t\t%9"PRI64"u\n", 4-z, t2/(ulong64)(MAC_SIZE*1024));
}
#endif

XFREE(buf);
#else
LTC_UNUSED_PARAM(MAC_SIZE);
Expand Down
154 changes: 154 additions & 0 deletions doc/crypt.tex
Original file line number Diff line number Diff line change
Expand Up @@ -2576,6 +2576,160 @@ \subsection{One--Shot Packet}
In order to enable OpenSSH compatibility, the flag \textit{CHACHA20POLY1305\_OPENSSH\_COMPAT} has to be \textbf{OR}'ed into
the \textit{direction} parameter.


\mysection{SIV}
\label{SIV}

The SIV (Synthetic Initialization Vector) authenticated encryption is a block cipher mode of encryption
defined by \url{https://tools.ietf.org/html/rfc5297}.

In contrast to all the other AEAD modes, SIV provides no iterative API. Instead it only provides one--shot APIs.

AEAD algorithm design usually suggests using a separate Nonce (also called IV) and additional authenticated Data (AAD).
SIV treats this slightly different and does not enforce any of the two, but leaves it up to the user.
Also SIV allows passing multiple sets of data as AAD, up to a maximum of \texttt{126} elements.
In case one wants to use a Nonce in a classical style it is suggested to pass it as the last of the AAD elements,
thereby limiting the number of AAD to \texttt{125}.

\subsection{Encryption / Decryption}
To encrypt and create a tag resp. decrypt and check the tag, the following API functions can be used.

\index{siv\_encrypt()}
\begin{verbatim}
int siv_encrypt( int cipher,
const unsigned char *key, unsigned long keylen,
const unsigned char *ad[], unsigned long adlen[],
const unsigned char *pt, unsigned long ptlen,
unsigned char *ct, unsigned long *ctlen);
\end{verbatim}
This encrypts the data where \textit{pt} is the plaintext and \textit{ct} is the ciphertext.
The length of the plaintext is given in \textit{ptlen} and the length of the ciphertext is given in \textit{ctlen}.
\textit{ctlen} shall contain the max buffer size allocated at \textit{ct} on input, and will be updated with the
written length on successful encryption.

\textit{ctlen} must be at least \texttt{16} octets larger that \textit{ptlen}.

The key to the encrypt operation is passed in \textit{key} of length \textit{keylen}.

The AAD is passed as array of pointers in \textit{ad}. The length of each AAD is passed as array of
\textit{unsigned long} in \textit{adlen}.
As soon as an array element of \textit{ad} is hit which equals \texttt{NULL} or an array element of \textit{adlen}
is hit which equals \texttt{0}, processing is stopped.
\index{siv\_decrypt()}
\begin{verbatim}
int siv_decrypt( int cipher,
const unsigned char *key, unsigned long keylen,
const unsigned char *ad[], unsigned long adlen[],
const unsigned char *ct, unsigned long ctlen,
unsigned char *pt, unsigned long *ptlen);
\end{verbatim}
This decrypts the data where \textit{in} is the ciphertext and \textit{out} is the plaintext. The length of both are
equal and stored in \textit{inlen}.

The AAD is processed in the same way as in the encrypt function.

An example of encryption and decryption with SIV using multiple AAD and a Nonce is given below.

\begin{small}
\begin{verbatim}
#include <tomcrypt.h>
int main(void)
{
int err;
unsigned char plain[16] = {0};
unsigned char ct[sizeof(plain) + 16] = {0};
unsigned long plainlen = sizeof(plain), ctlen = sizeof(ct);
register_cipher(&aes_desc);
/* We need to cast the AAD strings because the API asks for an `unsigned char*`
* but a string is on most platforms defined as a "signed" `char*`. */
if ((err = siv_encrypt(find_cipher("aes"),
((unsigned char[32]) {0x0}), 32,
((const unsigned char*[]) {(void*)"aad0", (void*)"aad1",
(void*)"NONCE", NULL}),
((unsigned long[]) {4, 4, 5, 0}),
plain, plainlen,
ct, &ctlen)) != CRYPT_OK) {
whine_and_pout(err);
}
if ((err = siv_decrypt(find_cipher("aes"),
((unsigned char[32]) {0x0}), 32,
((const unsigned char*[]) {(void*)"aad0", (void*)"aad1",
(void*)"NONCE", NULL}),
((unsigned long[]) {4, 4, 5, 0}),
ct, ctlen,
plain, &plainlen)) != CRYPT_OK) {
whine_and_pout(err);
}
return EXIT_SUCCESS;
}
\end{verbatim}
\end{small}

\subsection{One--Shot Packet}
To process a single packet under any given key the following helper function can be used.

\index{siv\_memory()}
\begin{verbatim}
int siv_memory( int cipher, int direction,
const unsigned char *key, unsigned long keylen,
const unsigned char *in, unsigned long inlen,
unsigned char *out, unsigned long *outlen,
...);
\end{verbatim}

This will execute a SIV operation of the \textit{direction} (\texttt{LTC\_ENCRYPT} resp. \texttt{LTC\_DECRYPT})
using the \textit{cipher} with the \textit{key} of len \textit{keylen}.
The AAD is optionally passed as varargs of the form \textit{(const unsigned char*, unsigned long)}, which musst be
NULL terminated.
The input is passed via the \textit{in} argument of length \textit{inlen}.
The output is stored in the buffer pointer to by the \textit{out} argument where the length is passed as \textit{outlen}.
\textit{outlen} shall contain the initial size of the buffer behind \textit{out} when calling the function and on
return it will contain the written size.

In case the operation is \textit{encryption} the buffer of \textit{out} shall be at least \texttt{inlen + 16} bytes wide.
In the case of \textit{decryption} the buffer of \textit{out} shall be at least \texttt{inlen - 16} bytes wide.

An example of encryption and decryption with the one--shot API of SIV using multiple AAD is given below.

\begin{small}
\begin{verbatim}
#include <tomcrypt.h>
int main(void)
{
int err;
unsigned char plain[16] = {0};
unsigned char ct[sizeof(plain) + 16] = {0};
unsigned long plainlen = sizeof(plain), ctlen = sizeof(ct);
register_cipher(&aes_desc);
if ((err = siv_memory(find_cipher("aes"), LTC_ENCRYPT,
((unsigned char[32]) {0x0}), 32,
plain, plainlen,
ct, &ctlen,
"aad0", 4uL, "aad1", 4uL, "NONCE", 5uL, NULL)) != CRYPT_OK) {
whine_and_pout(err);
}
if ((err = siv_memory(find_cipher("aes"), LTC_DECRYPT,
((unsigned char[32]) {0x0}), 32,
ct, ctlen,
plain, &plainlen,
"aad0", 4uL, "aad1", 4uL, "NONCE", 5uL, NULL)) != CRYPT_OK) {
whine_and_pout(err);
}
return EXIT_SUCCESS;
}
\end{verbatim}
\end{small}

\chapter{One-Way Cryptographic Hash Functions}
\mysection{Core Functions}
Like the ciphers, there are hash core functions and a universal data type to hold the hash state called \textit{hash\_state}. To initialize hash
Expand Down
Loading

0 comments on commit be8c4ea

Please sign in to comment.