Update SIV a bit

* Rename `siv_{en,de}crypt()` to `siv_{en,de}crypt_memory()`.
* 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 <s@jaeckel.eu>
This commit is contained in:
Steffen Jaeckel 2024-12-19 16:17:16 +01:00
parent 8bdc093fae
commit ad2696f24f
4 changed files with 413 additions and 88 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

@ -10,6 +10,15 @@
#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;
@ -28,15 +37,6 @@ static LTC_INLINE void s_siv_dbl(unsigned char *inout)
inout[len - 1] = ((inout[len - 1] << 1) ^ (msb ? mask : 0)) & 255;
}
static LTC_INLINE int s_siv_S2V_zero(int cipher,
const unsigned char *key, unsigned long keylen,
unsigned char *D, unsigned long *Dlen)
{
/* D = AES-CMAC(K, <zero>) */
const unsigned char zero_or_one[16] = {0};
return omac_memory(cipher, key, keylen, zero_or_one, sizeof(zero_or_one), D, Dlen);
}
static LTC_INLINE int s_siv_S2V_one(int cipher,
const unsigned char *key, unsigned long keylen,
unsigned char *V, unsigned long *Vlen)
@ -48,10 +48,45 @@ static LTC_INLINE int s_siv_S2V_one(int cipher,
zero_or_one[0] = 1;
return omac_memory(cipher, key, keylen, zero_or_one, sizeof(zero_or_one), V, Vlen);
}
static LTC_INLINE int s_siv_S2V_dbl_xor_cmac(int cipher,
const unsigned char *key, unsigned long keylen,
const unsigned char *aad, unsigned long aadlen,
unsigned char *D, unsigned long Dlen)
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)
@ -61,7 +96,7 @@ static LTC_INLINE int s_siv_S2V_dbl_xor_cmac(int cipher,
unsigned char TMP[16];
unsigned long i, TMPlen = sizeof(TMP);
s_siv_dbl(D);
if ((err = omac_memory(cipher, key, keylen, aad, aadlen, TMP, &TMPlen)) != CRYPT_OK) {
if ((err = s_siv_omac_memory(ctx, aad, aadlen, TMP, &TMPlen)) != CRYPT_OK) {
return err;
}
for (i = 0; i < Dlen; ++i) {
@ -70,11 +105,28 @@ static LTC_INLINE int s_siv_S2V_dbl_xor_cmac(int cipher,
return err;
}
static LTC_INLINE int s_siv_S2V_T(int cipher,
const unsigned char *in, unsigned long inlen,
const unsigned char *key, unsigned long keylen,
unsigned char *D,
unsigned char *V, unsigned long *Vlen)
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;
@ -91,7 +143,7 @@ static LTC_INLINE int s_siv_S2V_T(int cipher,
for(i = 0; i < 16; ++i) {
T[i] ^= D[i];
}
err = omac_memory_multi(cipher, key, keylen, V, Vlen, in, inlen - 16, T, 16uL, NULL);
err = s_siv_omac_memory_multi(ctx, V, Vlen, in, inlen - 16, T, 16uL, NULL);
} else {
s_siv_dbl(D);
XMEMCPY(T, in, inlen);
@ -103,39 +155,44 @@ static LTC_INLINE int s_siv_S2V_T(int cipher,
T[i] ^= D[i];
}
err = omac_memory(cipher, key, keylen, T, 16, V, Vlen);
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, n;
int err;
unsigned char D[16];
unsigned long Dlen = sizeof(D);
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(cipher, key, keylen, D, &Dlen)) != CRYPT_OK) {
if ((err = s_siv_S2V_zero(&ctx, D, &Dlen)) != CRYPT_OK) {
return err;
}
n = 0;
while(ad[n] != NULL && adlen[n] != 0) {
if ((err = s_siv_S2V_dbl_xor_cmac(cipher, key, keylen, ad[n], adlen[n], D, Dlen)) != CRYPT_OK) {
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(cipher, in, inlen, key, keylen, D, V, Vlen);
err = s_siv_S2V_T(&ctx, in, inlen, D, V, Vlen);
}
return err;
@ -193,11 +250,11 @@ typedef struct {
@param ctlen [in/out] The length of the ciphertext
@return CRYPT_OK if successful
*/
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)
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;
@ -211,12 +268,17 @@ int siv_encrypt(int cipher,
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;
}
if (*ctlen < ptlen + 16) {
return CRYPT_BUFFER_OVERFLOW;
}
K1 = key;
K2 = &key[keylen/2];
@ -262,11 +324,11 @@ out:
@param ptlen [in/out] The length of the plaintext
@return CRYPT_OK if successful
*/
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)
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;
@ -281,13 +343,17 @@ int siv_decrypt(int cipher,
LTC_ARGCHK(pt != NULL);
LTC_ARGCHK(ptlen != NULL);
if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
return err;
if (ctlen < 16) {
return CRYPT_INVALID_ARG;
}
if (*ptlen < ctlen || ctlen < 16) {
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) {
@ -313,10 +379,9 @@ int siv_decrypt(int cipher,
copy_or_zeromem(pt_work, pt, *ptlen, err);
out:
#ifdef LTC_CLEAN_STACK
zeromem(Q, sizeof(Q));
zeromem(&siv, sizeof(siv));
zeromem(pt_work, *ptlen);
#endif
zeromem(pt_work, *ptlen);
XFREE(pt_work);
return err;
@ -336,18 +401,18 @@ out:
@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 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 aadlen, Dlen = sizeof(D), Vlen = sizeof(siv.V), in_work_len;
unsigned long n = 0, aadlen, Dlen = sizeof(D), Vlen = sizeof(siv.V), in_work_len;
LTC_ARGCHK(key != NULL);
LTC_ARGCHK(in != NULL);
@ -358,9 +423,11 @@ int siv_memory( int cipher, int direction,
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)) {
return CRYPT_INVALID_ARG;
*outlen = inlen - 16;
return CRYPT_BUFFER_OVERFLOW;
}
K1 = key;
@ -385,34 +452,43 @@ int siv_memory( int cipher, int direction,
va_start(args, outlen);
aad = va_arg(args, const unsigned char*);
aadlen = va_arg(args, unsigned long);
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 {
if ((err = s_siv_S2V_zero(cipher, K1, keylen/2, D, &Dlen)) != CRYPT_OK) {
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 ((err = s_siv_S2V_dbl_xor_cmac(cipher, K1, keylen/2, aad, aadlen, D, Dlen)) != CRYPT_OK) {
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(cipher, in_work, in_work_len, K1, keylen/2, D, siv.V, &Vlen)) != CRYPT_OK) {
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_buf, out, in_work_len, err);
copy_or_zeromem(in_work, out, in_work_len, err);
*outlen = in_work_len;
} else {
s_siv_bitand(siv.V, siv.Q);
@ -426,8 +502,10 @@ int siv_memory( int cipher, int direction,
*outlen = inlen + 16;
}
err_out:
if (in_buf)
if (in_buf) {
zeromem(in_buf, in_work_len);
XFREE(in_buf);
}
va_end(args);
#ifdef LTC_CLEAN_STACK
zeromem(D, sizeof(D));
@ -521,14 +599,19 @@ int siv_test(void)
};
#undef PL_PAIR
int err;
int err, cipher;
unsigned n;
unsigned long buflen;
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(find_cipher("aes"),
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,
@ -539,7 +622,7 @@ int siv_test(void)
return CRYPT_FAIL_TESTVECTOR;
}
buflen = sizeof(buf);
if ((err = siv_decrypt(find_cipher("aes"),
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,
@ -553,7 +636,7 @@ int siv_test(void)
/* Testcase 0x2 */
buflen = sizeof(buf);
if ((err = siv_memory(find_cipher("aes"), LTC_ENCRYPT,
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,
@ -566,7 +649,7 @@ int siv_test(void)
}
/* Testcase 0x1002 */
buflen = sizeof(buf);
if ((err = siv_memory(find_cipher("aes"), LTC_DECRYPT,
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,
@ -582,7 +665,7 @@ int siv_test(void)
/* Testcase 0x3 */
buflen = sizeof(buf);
if ((err = siv_memory(find_cipher("aes"), LTC_ENCRYPT,
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,
@ -597,7 +680,7 @@ int siv_test(void)
}
/* Testcase 0x1003 */
buflen = sizeof(buf);
if ((err = siv_memory(find_cipher("aes"), LTC_DECRYPT,
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,
@ -610,7 +693,54 @@ int siv_test(void)
if (compare_testvector(buf, buflen, siv_tests[1].Plaintext, siv_tests[1].Plaintextlen, siv_tests[1].name, n + 0x1000) != 0) {
return CRYPT_FAIL_TESTVECTOR;
}
return CRYPT_OK;
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
}

View file

@ -567,21 +567,21 @@ int chacha20poly1305_test(void);
#endif /* LTC_CHACHA20POLY1305_MODE */
#ifdef LTC_SIV_MODE
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);
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);
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_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