Merge pull request #319 from libtom/add/SIV

Add SIV
This commit is contained in:
Steffen Jaeckel 2025-01-09 13:36:02 +01:00 committed by GitHub
commit 2e9f2b5e44
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 1020 additions and 30 deletions

View file

@ -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;
@ -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;
@ -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);

View file

@ -2576,6 +2576,166 @@ IMPORTANT NOTICE 2: As mentioned in \ref{chacha20poly1305} there exists a discre
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\_memory()}
\begin{verbatim}
int siv_encrypt_memory( 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.
The buffer of \textit{ct} shall be at least \texttt{ptlen + 16} bytes wide.
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 of the AAD is stopped.
\index{siv\_decrypt\_memory()}
\begin{verbatim}
int siv_decrypt_memory( 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{ct} is the ciphertext of length \textit{ctlen} and \textit{pt} is the plaintext of length \textit{ptlen}.
\textit{ptlen} shall contain the max buffer size allocated at \textit{pt} on input, and will be updated with the
written lenth on successful decryption.
The buffer of \textit{pt} shall be at least \texttt{ctlen - 16} bytes wide.
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_memory(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_memory(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);
/* Note that constant length values must be suffixed by `uL` in order
* to operate correctly cross-platform. */
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

View file

@ -835,6 +835,14 @@
>
</File>
</Filter>
<Filter
Name="siv"
>
<File
RelativePath="src\encauth\siv\siv.c"
>
</File>
</Filter>
</Filter>
<Filter
Name="hashes"

View file

@ -64,8 +64,8 @@ src/encauth/ocb3/ocb3_add_aad.o src/encauth/ocb3/ocb3_decrypt.o src/encauth/ocb3
src/encauth/ocb3/ocb3_decrypt_verify_memory.o src/encauth/ocb3/ocb3_done.o \
src/encauth/ocb3/ocb3_encrypt.o src/encauth/ocb3/ocb3_encrypt_authenticate_memory.o \
src/encauth/ocb3/ocb3_encrypt_last.o src/encauth/ocb3/ocb3_init.o src/encauth/ocb3/ocb3_int_ntz.o \
src/encauth/ocb3/ocb3_int_xor_blocks.o src/encauth/ocb3/ocb3_test.o src/hashes/blake2b.o \
src/hashes/blake2s.o src/hashes/chc/chc.o src/hashes/helper/hash_file.o \
src/encauth/ocb3/ocb3_int_xor_blocks.o src/encauth/ocb3/ocb3_test.o src/encauth/siv/siv.o \
src/hashes/blake2b.o src/hashes/blake2s.o src/hashes/chc/chc.o src/hashes/helper/hash_file.o \
src/hashes/helper/hash_filehandle.o src/hashes/helper/hash_memory.o \
src/hashes/helper/hash_memory_multi.o src/hashes/md2.o src/hashes/md4.o src/hashes/md5.o \
src/hashes/rmd128.o src/hashes/rmd160.o src/hashes/rmd256.o src/hashes/rmd320.o src/hashes/sha1.o \

View file

@ -57,8 +57,8 @@ src/encauth/ocb3/ocb3_add_aad.obj src/encauth/ocb3/ocb3_decrypt.obj src/encauth/
src/encauth/ocb3/ocb3_decrypt_verify_memory.obj src/encauth/ocb3/ocb3_done.obj \
src/encauth/ocb3/ocb3_encrypt.obj src/encauth/ocb3/ocb3_encrypt_authenticate_memory.obj \
src/encauth/ocb3/ocb3_encrypt_last.obj src/encauth/ocb3/ocb3_init.obj src/encauth/ocb3/ocb3_int_ntz.obj \
src/encauth/ocb3/ocb3_int_xor_blocks.obj src/encauth/ocb3/ocb3_test.obj src/hashes/blake2b.obj \
src/hashes/blake2s.obj src/hashes/chc/chc.obj src/hashes/helper/hash_file.obj \
src/encauth/ocb3/ocb3_int_xor_blocks.obj src/encauth/ocb3/ocb3_test.obj src/encauth/siv/siv.obj \
src/hashes/blake2b.obj src/hashes/blake2s.obj src/hashes/chc/chc.obj src/hashes/helper/hash_file.obj \
src/hashes/helper/hash_filehandle.obj src/hashes/helper/hash_memory.obj \
src/hashes/helper/hash_memory_multi.obj src/hashes/md2.obj src/hashes/md4.obj src/hashes/md5.obj \
src/hashes/rmd128.obj src/hashes/rmd160.obj src/hashes/rmd256.obj src/hashes/rmd320.obj src/hashes/sha1.obj \

View file

@ -78,8 +78,8 @@ src/encauth/ocb3/ocb3_add_aad.o src/encauth/ocb3/ocb3_decrypt.o src/encauth/ocb3
src/encauth/ocb3/ocb3_decrypt_verify_memory.o src/encauth/ocb3/ocb3_done.o \
src/encauth/ocb3/ocb3_encrypt.o src/encauth/ocb3/ocb3_encrypt_authenticate_memory.o \
src/encauth/ocb3/ocb3_encrypt_last.o src/encauth/ocb3/ocb3_init.o src/encauth/ocb3/ocb3_int_ntz.o \
src/encauth/ocb3/ocb3_int_xor_blocks.o src/encauth/ocb3/ocb3_test.o src/hashes/blake2b.o \
src/hashes/blake2s.o src/hashes/chc/chc.o src/hashes/helper/hash_file.o \
src/encauth/ocb3/ocb3_int_xor_blocks.o src/encauth/ocb3/ocb3_test.o src/encauth/siv/siv.o \
src/hashes/blake2b.o src/hashes/blake2s.o src/hashes/chc/chc.o src/hashes/helper/hash_file.o \
src/hashes/helper/hash_filehandle.o src/hashes/helper/hash_memory.o \
src/hashes/helper/hash_memory_multi.o src/hashes/md2.o src/hashes/md4.o src/hashes/md5.o \
src/hashes/rmd128.o src/hashes/rmd160.o src/hashes/rmd256.o src/hashes/rmd320.o src/hashes/sha1.o \

View file

@ -239,8 +239,8 @@ src/encauth/ocb3/ocb3_add_aad.o src/encauth/ocb3/ocb3_decrypt.o src/encauth/ocb3
src/encauth/ocb3/ocb3_decrypt_verify_memory.o src/encauth/ocb3/ocb3_done.o \
src/encauth/ocb3/ocb3_encrypt.o src/encauth/ocb3/ocb3_encrypt_authenticate_memory.o \
src/encauth/ocb3/ocb3_encrypt_last.o src/encauth/ocb3/ocb3_init.o src/encauth/ocb3/ocb3_int_ntz.o \
src/encauth/ocb3/ocb3_int_xor_blocks.o src/encauth/ocb3/ocb3_test.o src/hashes/blake2b.o \
src/hashes/blake2s.o src/hashes/chc/chc.o src/hashes/helper/hash_file.o \
src/encauth/ocb3/ocb3_int_xor_blocks.o src/encauth/ocb3/ocb3_test.o src/encauth/siv/siv.o \
src/hashes/blake2b.o src/hashes/blake2s.o src/hashes/chc/chc.o src/hashes/helper/hash_file.o \
src/hashes/helper/hash_filehandle.o src/hashes/helper/hash_memory.o \
src/hashes/helper/hash_memory_multi.o src/hashes/md2.o src/hashes/md4.o src/hashes/md5.o \
src/hashes/rmd128.o src/hashes/rmd160.o src/hashes/rmd256.o src/hashes/rmd320.o src/hashes/sha1.o \

View file

@ -85,6 +85,7 @@ src/encauth/ocb3/ocb3_init.c
src/encauth/ocb3/ocb3_int_ntz.c
src/encauth/ocb3/ocb3_int_xor_blocks.c
src/encauth/ocb3/ocb3_test.c
src/encauth/siv/siv.c
src/hashes/blake2b.c
src/hashes/blake2s.c
src/hashes/chc/chc.c

747
src/encauth/siv/siv.c Normal file
View file

@ -0,0 +1,747 @@
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
/* SPDX-License-Identifier: Unlicense */
#include "tomcrypt_private.h"
#include <stdarg.h>
/**
@file siv.c
RFC 5297 SIV - Synthetic Initialization Vector, Steffen Jaeckel
*/
#ifdef LTC_SIV_MODE
/* RFC 5297 - Chapter 7 - Security Considerations
*
* [...] S2V must not be
* passed more than 127 components. Since SIV includes the plaintext as
* a component to S2V, that limits the number of components of
* associated data that can be safely passed to SIV to 126.
*/
static const unsigned long s_siv_max_aad_components = 126;
static LTC_INLINE void s_siv_dbl(unsigned char *inout)
{
int y, mask, msb, len;
/* setup the system */
mask = 0x87;
len = 16;
/* if msb(L * u^(x+1)) = 0 then just shift, otherwise shift and xor constant mask */
msb = inout[0] >> 7;
/* shift left */
for (y = 0; y < (len - 1); y++) {
inout[y] = ((inout[y] << 1) | (inout[y + 1] >> 7)) & 255;
}
inout[len - 1] = ((inout[len - 1] << 1) ^ (msb ? mask : 0)) & 255;
}
static LTC_INLINE int s_siv_S2V_one(int cipher,
const unsigned char *key, unsigned long keylen,
unsigned char *V, unsigned long *Vlen)
{
/* if n = 0 then
* return V = AES-CMAC(K, <one>)
*/
unsigned char zero_or_one[16] = {0};
zero_or_one[0] = 1;
return omac_memory(cipher, key, keylen, zero_or_one, sizeof(zero_or_one), V, Vlen);
}
typedef struct siv_omac_ctx_t {
omac_state omac;
int cipher;
} siv_omac_ctx_t;
static LTC_INLINE int s_siv_ctx_init(int cipher,
const unsigned char *key, unsigned long keylen,
siv_omac_ctx_t *ctx)
{
ctx->cipher = cipher;
return omac_init(&ctx->omac, cipher, key, keylen);
}
static LTC_INLINE int s_siv_omac_memory(siv_omac_ctx_t *ctx,
const unsigned char *in, unsigned long inlen,
unsigned char *out, unsigned long *outlen)
{
int err;
omac_state omac = ctx->omac;
if ((err = omac_process(&omac, in, inlen)) != CRYPT_OK) {
return err;
}
err = omac_done(&omac, out, outlen);
zeromem(&omac, sizeof(omac));
return err;
}
static LTC_INLINE int s_siv_S2V_zero(siv_omac_ctx_t *ctx,
unsigned char *D, unsigned long *Dlen)
{
/* D = AES-CMAC(K, <zero>) */
const unsigned char zero_or_one[16] = {0};
return s_siv_omac_memory(ctx, zero_or_one, sizeof(zero_or_one), D, Dlen);
}
static LTC_INLINE int s_siv_S2V_dbl_xor_cmac(siv_omac_ctx_t *ctx,
const unsigned char *aad, unsigned long aadlen,
unsigned char *D, unsigned long Dlen)
{
/* for i = 1 to n-1 do
* D = dbl(D) xor AES-CMAC(K, Si)
* done
*/
int err;
unsigned char TMP[16];
unsigned long i, TMPlen = sizeof(TMP);
s_siv_dbl(D);
if ((err = s_siv_omac_memory(ctx, aad, aadlen, TMP, &TMPlen)) != CRYPT_OK) {
return err;
}
for (i = 0; i < Dlen; ++i) {
D[i] ^= TMP[i];
}
return err;
}
static LTC_INLINE int s_siv_omac_memory_multi(siv_omac_ctx_t *ctx,
unsigned char *out, unsigned long *outlen,
const unsigned char *in, unsigned long inlen,
...)
{
int err;
va_list args;
omac_state omac = ctx->omac;
va_start(args, inlen);
if ((err = omac_vprocess(&omac, in, inlen, args)) != CRYPT_OK) {
return err;
}
err = omac_done(&omac, out, outlen);
zeromem(&omac, sizeof(omac));
return err;
}
static LTC_INLINE int s_siv_S2V_T(siv_omac_ctx_t *ctx,
const unsigned char *in, unsigned long inlen,
unsigned char *D,
unsigned char *V, unsigned long *Vlen)
{
int err;
unsigned long i;
unsigned char T[16];
/* if len(Sn) >= 128 then
* T = Sn xorend D
* else
* T = dbl(D) xor pad(Sn)
* fi
*/
if (inlen >= 16) {
XMEMCPY(T, &in[inlen - 16], 16);
for(i = 0; i < 16; ++i) {
T[i] ^= D[i];
}
err = s_siv_omac_memory_multi(ctx, V, Vlen, in, inlen - 16, T, 16uL, NULL);
} else {
s_siv_dbl(D);
XMEMCPY(T, in, inlen);
T[inlen] = 0x80;
for (i = inlen + 1; i < 16; ++i) {
T[i] = 0x0;
}
for(i = 0; i < 16; ++i) {
T[i] ^= D[i];
}
err = s_siv_omac_memory(ctx, T, 16, V, Vlen);
}
return err;
}
static int s_siv_S2V(int cipher,
const unsigned char *key, unsigned long keylen,
const unsigned char **ad, unsigned long *adlen,
const unsigned char *in, unsigned long inlen,
unsigned char *V, unsigned long *Vlen)
{
int err;
unsigned char D[16];
unsigned long Dlen = sizeof(D), n = 0;
siv_omac_ctx_t ctx;
if(ad == NULL || adlen == NULL || ad[0] == NULL || adlen[0] == 0) {
err = s_siv_S2V_one(cipher, key, keylen, V, Vlen);
} else {
if ((err = s_siv_ctx_init(cipher, key, keylen, &ctx)) != CRYPT_OK) {
return err;
}
Dlen = sizeof(D);
if ((err = s_siv_S2V_zero(&ctx, D, &Dlen)) != CRYPT_OK) {
return err;
}
while(ad[n] != NULL && adlen[n] != 0) {
if (n >= s_siv_max_aad_components) {
return CRYPT_INPUT_TOO_LONG;
}
if ((err = s_siv_S2V_dbl_xor_cmac(&ctx, ad[n], adlen[n], D, Dlen)) != CRYPT_OK) {
return err;
}
n++;
}
err = s_siv_S2V_T(&ctx, in, inlen, D, V, Vlen);
}
return err;
}
static LTC_INLINE void s_siv_bitand(const unsigned char* V, unsigned char* Q)
{
int n;
XMEMSET(Q, 0xff, 16);
Q[8] = Q[12] = 0x7f;
for (n = 0; n < 16; ++n) {
Q[n] &= V[n];
}
}
static LTC_INLINE int s_ctr_crypt_memory(int cipher,
const unsigned char *IV,
const unsigned char *key, int keylen,
const unsigned char *in,
unsigned char *out, unsigned long len)
{
int err;
symmetric_CTR ctr;
if ((err = ctr_start(cipher, IV, key, keylen, 0, CTR_COUNTER_BIG_ENDIAN | 16, &ctr)) != CRYPT_OK) {
goto out;
}
if ((err = ctr_encrypt(in, out, len, &ctr)) != CRYPT_OK) {
goto out;
}
if ((err = ctr_done(&ctr)) != CRYPT_OK) {
goto out;
}
out:
zeromem(&ctr, sizeof(ctr));
return err;
}
typedef struct {
unsigned char Q[16], V[16];
} siv_state;
/**
SIV encrypt
@param cipher The index of the cipher desired
@param key The secret key to use
@param keylen The length of the secret key (octets)
@param ad An array of Associated Data pointers (must be NULL terminated)
@param adlen An array with the lengths of the Associated Data
@param pt The plaintext
@param ptlen The length of the plaintext
@param ct The ciphertext
@param ctlen [in/out] The length of the ciphertext
@return CRYPT_OK if successful
*/
int siv_encrypt_memory( 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)
{
int err;
const unsigned char *K1, *K2;
unsigned long Vlen;
siv_state siv;
LTC_ARGCHK(key != NULL);
LTC_ARGCHK(ad != NULL);
LTC_ARGCHK(adlen != NULL);
LTC_ARGCHK(pt != NULL);
LTC_ARGCHK(ct != NULL);
LTC_ARGCHK(ctlen != NULL);
if (ptlen + 16 < ptlen) {
return CRYPT_OVERFLOW;
}
if (*ctlen < ptlen + 16) {
*ctlen = ptlen + 16;
return CRYPT_BUFFER_OVERFLOW;
}
if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
return err;
}
K1 = key;
K2 = &key[keylen/2];
Vlen = sizeof(siv.V);
err = s_siv_S2V(cipher, K1, keylen/2, ad, adlen, pt, ptlen, siv.V, &Vlen);
#ifdef LTC_CLEAN_STACK
burn_stack(3 * 16 + 7 * sizeof(unsigned long) + 1 * sizeof(void*));
#endif
if (err != CRYPT_OK) {
return err;
}
s_siv_bitand(siv.V, siv.Q);
XMEMCPY(ct, siv.V, 16);
ct += 16;
if ((err = s_ctr_crypt_memory(cipher, siv.Q, K2, keylen/2, pt, ct, ptlen)) != CRYPT_OK) {
zeromem(ct, ptlen + 16);
goto out;
}
*ctlen = ptlen + 16;
out:
#ifdef LTC_CLEAN_STACK
zeromem(&siv, sizeof(siv));
#endif
return err;
}
/**
SIV decrypt
@param cipher The index of the cipher desired
@param key The secret key to use
@param keylen The length of the secret key (octets)
@param ad An array of Associated Data pointers (must be NULL terminated)
@param adlen An array with the lengths of the Associated Data
@param ct The ciphertext
@param ctlen The length of the ciphertext
@param pt The plaintext
@param ptlen [in/out] The length of the plaintext
@return CRYPT_OK if successful
*/
int siv_decrypt_memory( 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)
{
int err;
unsigned char *pt_work;
const unsigned char *K1, *K2, *ct_work;
unsigned long Vlen;
siv_state siv;
LTC_ARGCHK(key != NULL);
LTC_ARGCHK(ad != NULL);
LTC_ARGCHK(adlen != NULL);
LTC_ARGCHK(ct != NULL);
LTC_ARGCHK(pt != NULL);
LTC_ARGCHK(ptlen != NULL);
if (ctlen < 16) {
return CRYPT_INVALID_ARG;
}
if (*ptlen < (ctlen - 16)) {
*ptlen = ctlen - 16;
return CRYPT_BUFFER_OVERFLOW;
}
if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
return err;
}
*ptlen = ctlen - 16;
pt_work = XMALLOC(*ptlen);
if (pt_work == NULL) {
return CRYPT_MEM;
}
K1 = key;
K2 = &key[keylen/2];
ct_work = ct;
s_siv_bitand(ct_work, siv.Q);
ct_work += 16;
if ((err = s_ctr_crypt_memory(cipher, siv.Q, K2, keylen/2, ct_work, pt_work, *ptlen)) != CRYPT_OK) {
goto out;
}
Vlen = sizeof(siv.V);
if ((err = s_siv_S2V(cipher, K1, keylen/2, ad, adlen, pt_work, *ptlen, siv.V, &Vlen)) != CRYPT_OK) {
goto out;
}
err = XMEM_NEQ(siv.V, ct, Vlen);
copy_or_zeromem(pt_work, pt, *ptlen, err);
out:
#ifdef LTC_CLEAN_STACK
zeromem(&siv, sizeof(siv));
#endif
zeromem(pt_work, *ptlen);
XFREE(pt_work);
return err;
}
/**
Process an entire SIV packet in one call.
@param cipher The index of the cipher desired
@param direction Encrypt or Decrypt mode (LTC_ENCRYPT or LTC_DECRYPT)
@param key The secret key to use
@param keylen The length of the secret key (octets)
@param in The input
@param inlen The length of the input
@param out The output
@param outlen [in/out] The max size and resulting size of the output
@remark <...> is of the form <pointer, length> (void*, unsigned long) and contains the Associated Data
@return CRYPT_OK on success
*/
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,
...)
{
int err;
va_list args;
siv_state siv;
unsigned char D[16], *in_buf = NULL, *out_work;
const unsigned char *aad, *K1, *K2, *in_work;
unsigned long n = 0, aadlen, Dlen = sizeof(D), Vlen = sizeof(siv.V), in_work_len;
LTC_ARGCHK(key != NULL);
LTC_ARGCHK(in != NULL);
LTC_ARGCHK(out != NULL);
LTC_ARGCHK(outlen != NULL);
if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
return err;
}
if (direction == LTC_ENCRYPT && *outlen < inlen + 16) {
*outlen = inlen + 16;
return CRYPT_BUFFER_OVERFLOW;
} else if (direction == LTC_DECRYPT && (inlen < 16 || *outlen < inlen - 16)) {
*outlen = inlen - 16;
return CRYPT_BUFFER_OVERFLOW;
}
K1 = key;
K2 = &key[keylen/2];
in_work = in;
in_work_len = inlen;
out_work = out;
if (direction == LTC_DECRYPT) {
in_work_len -= 16;
in_buf = XMALLOC(in_work_len);
if (in_buf == NULL)
return CRYPT_MEM;
s_siv_bitand(in_work, siv.Q);
in_work += 16;
if ((err = s_ctr_crypt_memory(cipher, siv.Q, K2, keylen/2, in_work, in_buf, in_work_len)) != CRYPT_OK) {
goto err_out;
}
in_work = in_buf;
}
va_start(args, outlen);
aad = va_arg(args, const unsigned char*);
aadlen = aad ? va_arg(args, unsigned long) : 0;
if (aad == NULL || aadlen == 0) {
if ((err = s_siv_S2V_one(cipher, K1, keylen/2, siv.V, &Vlen)) != CRYPT_OK) {
goto err_out;
}
} else {
siv_omac_ctx_t ctx;
if ((err = s_siv_ctx_init(cipher, K1, keylen/2, &ctx)) != CRYPT_OK) {
goto err_out;
}
if ((err = s_siv_S2V_zero(&ctx, D, &Dlen)) != CRYPT_OK) {
goto err_out;
}
do {
if (n >= s_siv_max_aad_components) {
err = CRYPT_INPUT_TOO_LONG;
goto err_out;
}
if ((err = s_siv_S2V_dbl_xor_cmac(&ctx, aad, aadlen, D, Dlen)) != CRYPT_OK) {
goto err_out;
}
aad = va_arg(args, const unsigned char*);
if (aad == NULL)
break;
aadlen = va_arg(args, unsigned long);
n++;
} while (aadlen);
if ((err = s_siv_S2V_T(&ctx, in_work, in_work_len, D, siv.V, &Vlen)) != CRYPT_OK) {
goto err_out;
}
}
if (direction == LTC_DECRYPT) {
err = XMEM_NEQ(siv.V, in, Vlen);
copy_or_zeromem(in_work, out, in_work_len, err);
*outlen = in_work_len;
} else {
s_siv_bitand(siv.V, siv.Q);
XMEMCPY(out_work, siv.V, 16);
out_work += 16;
if ((err = s_ctr_crypt_memory(cipher, siv.Q, K2, keylen/2, in, out_work, inlen)) != CRYPT_OK) {
zeromem(out, inlen + 16);
goto err_out;
}
*outlen = inlen + 16;
}
err_out:
if (in_buf) {
zeromem(in_buf, in_work_len);
XFREE(in_buf);
}
va_end(args);
#ifdef LTC_CLEAN_STACK
zeromem(D, sizeof(D));
zeromem(&siv, sizeof(siv));
#endif
return err;
}
int siv_test(void)
{
#ifndef LTC_TEST
return CRYPT_NOP;
#else
/*
* RFC5297 - A.1. Deterministic Authenticated Encryption Example
*/
const unsigned char Key_A1[] =
{ 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff };
const unsigned char AD_A1[] =
{ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27 };
const unsigned char Plaintext_A1[] =
{ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee };
const unsigned char output_A1[] =
{ 0x85, 0x63, 0x2d, 0x07, 0xc6, 0xe8, 0xf3, 0x7f,
0x95, 0x0a, 0xcd, 0x32, 0x0a, 0x2e, 0xcc, 0x93,
0x40, 0xc0, 0x2b, 0x96, 0x90, 0xc4, 0xdc, 0x04,
0xda, 0xef, 0x7f, 0x6a, 0xfe, 0x5c };
const unsigned char *ad_A1[] =
{ AD_A1, NULL };
unsigned long adlen_A1[] =
{ sizeof(AD_A1), 0 };
const unsigned char Key_A2[] =
{ 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78,
0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f };
const unsigned char AD1_A2[] =
{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
0xde, 0xad, 0xda, 0xda, 0xde, 0xad, 0xda, 0xda,
0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88,
0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 };
const unsigned char AD2_A2[] =
{ 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
0x90, 0xa0 };
const unsigned char AD3_A2[] =
{ 0x09, 0xf9, 0x11, 0x02, 0x9d, 0x74, 0xe3, 0x5b,
0xd8, 0x41, 0x56, 0xc5, 0x63, 0x56, 0x88, 0xc0 };
const unsigned char Plaintext_A2[] =
{ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20,
0x73, 0x6f, 0x6d, 0x65, 0x20, 0x70, 0x6c, 0x61,
0x69, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x20, 0x74,
0x6f, 0x20, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70,
0x74, 0x20, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20,
0x53, 0x49, 0x56, 0x2d, 0x41, 0x45, 0x53 };
const unsigned char output_A2[] =
{ 0x7b, 0xdb, 0x6e, 0x3b, 0x43, 0x26, 0x67, 0xeb,
0x06, 0xf4, 0xd1, 0x4b, 0xff, 0x2f, 0xbd, 0x0f,
0xcb, 0x90, 0x0f, 0x2f, 0xdd, 0xbe, 0x40, 0x43,
0x26, 0x60, 0x19, 0x65, 0xc8, 0x89, 0xbf, 0x17,
0xdb, 0xa7, 0x7c, 0xeb, 0x09, 0x4f, 0xa6, 0x63,
0xb7, 0xa3, 0xf7, 0x48, 0xba, 0x8a, 0xf8, 0x29,
0xea, 0x64, 0xad, 0x54, 0x4a, 0x27, 0x2e, 0x9c,
0x48, 0x5b, 0x62, 0xa3, 0xfd, 0x5c, 0x0d };
const unsigned char *ad_A2[] =
{ AD1_A2, AD2_A2, AD3_A2, NULL };
unsigned long adlen_A2[] =
{ sizeof(AD1_A2), sizeof(AD2_A2), sizeof(AD3_A2), 0 };
#define PL_PAIR(n) n, sizeof(n)
struct {
const unsigned char* Key;
unsigned long Keylen;
const unsigned char* Plaintext;
unsigned long Plaintextlen;
const void* ADs;
void* ADlens;
const unsigned char* output;
unsigned long outputlen;
const char* name;
} siv_tests[] = {
{ PL_PAIR(Key_A1), PL_PAIR(Plaintext_A1), &ad_A1, &adlen_A1, PL_PAIR(output_A1), "RFC5297 - A.1. Deterministic Authenticated Encryption Example" },
{ PL_PAIR(Key_A2), PL_PAIR(Plaintext_A2), &ad_A2, &adlen_A2, PL_PAIR(output_A2), "RFC5297 - A.2. Nonce-Based Authenticated Encryption Example" }
};
#undef PL_PAIR
int err, cipher;
unsigned n;
unsigned long buflen, tmplen;
unsigned char buf[MAX(sizeof(output_A1), sizeof(output_A2))];
const unsigned long niter = 1000;
unsigned char *tmpe, *tmpd;
const unsigned long tmpmax = 16 + niter * 16;
cipher = find_cipher("aes");
for (n = 0; n < sizeof(siv_tests)/sizeof(siv_tests[0]); ++n) {
buflen = sizeof(buf);
if ((err = siv_encrypt_memory(cipher,
siv_tests[n].Key, siv_tests[n].Keylen,
(const unsigned char **)siv_tests[n].ADs, siv_tests[n].ADlens,
siv_tests[n].Plaintext, siv_tests[n].Plaintextlen,
buf, &buflen)) != CRYPT_OK) {
return err;
}
if (compare_testvector(buf, buflen, siv_tests[n].output, siv_tests[n].outputlen, siv_tests[n].name, n) != 0) {
return CRYPT_FAIL_TESTVECTOR;
}
buflen = sizeof(buf);
if ((err = siv_decrypt_memory(cipher,
siv_tests[n].Key, siv_tests[n].Keylen,
(const unsigned char **)siv_tests[n].ADs, siv_tests[n].ADlens,
siv_tests[n].output, siv_tests[n].outputlen,
buf, &buflen)) != CRYPT_OK) {
return err;
}
if (compare_testvector(buf, buflen, siv_tests[n].Plaintext, siv_tests[n].Plaintextlen, siv_tests[n].name, n + 0x1000) != 0) {
return CRYPT_FAIL_TESTVECTOR;
}
}
/* Testcase 0x2 */
buflen = sizeof(buf);
if ((err = siv_memory(cipher, LTC_ENCRYPT,
siv_tests[0].Key, siv_tests[0].Keylen,
siv_tests[0].Plaintext, siv_tests[0].Plaintextlen,
buf, &buflen,
AD_A1, sizeof(AD_A1),
NULL)) != CRYPT_OK) {
return err;
}
if (compare_testvector(buf, buflen, siv_tests[0].output, siv_tests[0].outputlen, siv_tests[0].name, n) != 0) {
return CRYPT_FAIL_TESTVECTOR;
}
/* Testcase 0x1002 */
buflen = sizeof(buf);
if ((err = siv_memory(cipher, LTC_DECRYPT,
siv_tests[0].Key, siv_tests[0].Keylen,
siv_tests[0].output, siv_tests[0].outputlen,
buf, &buflen,
AD_A1, sizeof(AD_A1),
NULL)) != CRYPT_OK) {
return err;
}
if (compare_testvector(buf, buflen, siv_tests[0].Plaintext, siv_tests[0].Plaintextlen, siv_tests[0].name, n + 0x1000) != 0) {
return CRYPT_FAIL_TESTVECTOR;
}
n++;
/* Testcase 0x3 */
buflen = sizeof(buf);
if ((err = siv_memory(cipher, LTC_ENCRYPT,
siv_tests[1].Key, siv_tests[1].Keylen,
siv_tests[1].Plaintext, siv_tests[1].Plaintextlen,
buf, &buflen,
ad_A2[0], adlen_A2[0],
ad_A2[1], adlen_A2[1],
ad_A2[2], adlen_A2[2],
NULL)) != CRYPT_OK) {
return err;
}
if (compare_testvector(buf, buflen, siv_tests[1].output, siv_tests[1].outputlen, siv_tests[1].name, n) != 0) {
return CRYPT_FAIL_TESTVECTOR;
}
/* Testcase 0x1003 */
buflen = sizeof(buf);
if ((err = siv_memory(cipher, LTC_DECRYPT,
siv_tests[1].Key, siv_tests[1].Keylen,
siv_tests[1].output, siv_tests[1].outputlen,
buf, &buflen,
ad_A2[0], adlen_A2[0],
ad_A2[1], adlen_A2[1],
ad_A2[2], adlen_A2[2],
NULL)) != CRYPT_OK) {
return err;
}
if (compare_testvector(buf, buflen, siv_tests[1].Plaintext, siv_tests[1].Plaintextlen, siv_tests[1].name, n + 0x1000) != 0) {
return CRYPT_FAIL_TESTVECTOR;
}
tmpe = XCALLOC(1, tmpmax);
if (tmpe == NULL) {
return CRYPT_MEM;
}
tmpd = XCALLOC(1, tmpmax);
if (tmpd == NULL) {
err = CRYPT_MEM;
goto out_tmpd;
}
tmplen = 16;
for (n = 0; n < niter; ++n) {
buflen = tmpmax;
if ((err = siv_memory(cipher, LTC_ENCRYPT,
siv_tests[0].Key, siv_tests[0].Keylen,
tmpe, tmplen,
tmpe, &buflen,
NULL)) != CRYPT_OK) {
goto out;
}
tmplen = buflen;
}
if (compare_testvector(&buflen, sizeof(buflen), &tmpmax, sizeof(tmpmax), "Multiple encrypt length", -(int)niter)) {
err = CRYPT_FAIL_TESTVECTOR;
goto out;
}
XMEMCPY(tmpd, tmpe, buflen);
for (n = 0; n < niter; ++n) {
buflen = tmpmax;
if ((err = siv_memory(cipher, LTC_DECRYPT,
siv_tests[0].Key, siv_tests[0].Keylen,
tmpd, tmplen,
tmpd, &buflen,
NULL)) != CRYPT_OK) {
goto out;
}
tmplen = buflen;
}
if (compare_testvector(tmpd, tmplen, tmpe, tmplen, "Multi decrypt", niter + 0x2000)) {
err = CRYPT_FAIL_TESTVECTOR;
}
out:
XFREE(tmpd);
out_tmpd:
XFREE(tmpe);
return err;
#endif
}
#endif

View file

@ -301,6 +301,7 @@
#define LTC_CCM_MODE
#define LTC_GCM_MODE
#define LTC_CHACHA20POLY1305_MODE
#define LTC_SIV_MODE
/* Use 64KiB tables */
#ifndef LTC_NO_TABLES

View file

@ -565,3 +565,24 @@ int chacha20poly1305_memory(const unsigned char *key, unsigned long keylen,
int chacha20poly1305_test(void);
#endif /* LTC_CHACHA20POLY1305_MODE */
#ifdef LTC_SIV_MODE
int siv_encrypt_memory( 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);
int siv_decrypt_memory( 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);
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,
...) LTC_NULL_TERMINATED;
int siv_test(void);
#endif

View file

@ -168,6 +168,7 @@ int func_name (hash_state * md, const unsigned char *in, unsigned long inlen)
int ocb3_int_ntz(unsigned long x);
void ocb3_int_xor_blocks(unsigned char *out, const unsigned char *block_a, const unsigned char *block_b, unsigned long block_len);
int omac_vprocess(omac_state *omac, const unsigned char *in, unsigned long inlen, va_list args);
/* tomcrypt_math.h */

View file

@ -10,6 +10,31 @@
#ifdef LTC_OMAC
static LTC_INLINE int s_omac_vprocess(omac_state *omac, const unsigned char *in, unsigned long inlen, va_list args)
{
const unsigned char * curptr = in;
unsigned long curlen = inlen;
int err;
for (;;) {
/* process buf */
if ((err = omac_process(omac, curptr, curlen)) != CRYPT_OK) {
return err;
}
/* step to next */
curptr = va_arg(args, const unsigned char*);
if (curptr == NULL) {
break;
}
curlen = va_arg(args, unsigned long);
}
return CRYPT_OK;
}
int omac_vprocess(omac_state *omac, const unsigned char *in, unsigned long inlen, va_list args)
{
return s_omac_vprocess(omac, in, inlen, args);
}
/**
OMAC multiple blocks of memory
@param cipher The index of the desired cipher
@ -30,8 +55,6 @@ int omac_memory_multi(int cipher,
int err;
omac_state *omac;
va_list args;
const unsigned char *curptr;
unsigned long curlen;
LTC_ARGCHK(key != NULL);
LTC_ARGCHK(in != NULL);
@ -49,23 +72,10 @@ int omac_memory_multi(int cipher,
goto LBL_ERR;
}
va_start(args, inlen);
curptr = in;
curlen = inlen;
for (;;) {
/* process buf */
if ((err = omac_process(omac, curptr, curlen)) != CRYPT_OK) {
goto LBL_ERR;
}
/* step to next */
curptr = va_arg(args, const unsigned char*);
if (curptr == NULL) {
break;
}
curlen = va_arg(args, unsigned long);
}
if ((err = omac_done(omac, out, outlen)) != CRYPT_OK) {
if ((err = s_omac_vprocess(omac, in, inlen, args)) != CRYPT_OK) {
goto LBL_ERR;
}
err = omac_done(omac, out, outlen);
LBL_ERR:
#ifdef LTC_CLEAN_STACK
zeromem(omac, sizeof(omac_state));

View file

@ -303,6 +303,9 @@ const char *crypt_build_settings =
#if defined(LTC_CHACHA20POLY1305_MODE)
" CHACHA20POLY1305\n"
#endif
#if defined(LTC_SIV_MODE)
" SIV\n"
#endif
"\nPRNG:\n"
#if defined(LTC_YARROW)

View file

@ -49,6 +49,9 @@ int mac_test(void)
#endif
#ifdef LTC_BLAKE2BMAC
DO(blake2bmac_test());
#endif
#ifdef LTC_SIV_MODE
DO(siv_test());
#endif
return 0;
}