mirror of
https://github.com/libtom/libtomcrypt
synced 2026-08-25 20:26:07 -04:00
Merge pull request #777 from libtom/pr/siv-zero-ad
SIV properly handle zero AD components
This commit is contained in:
commit
79f2b5762d
2 changed files with 97 additions and 19 deletions
|
|
@ -2556,6 +2556,7 @@ To encrypt and create a tag resp. decrypt and check the tag, the following API f
|
|||
\begin{verbatim}
|
||||
int siv_encrypt_memory( int cipher,
|
||||
const unsigned char *key, unsigned long keylen,
|
||||
unsigned long adnum,
|
||||
const unsigned char *ad[], unsigned long adlen[],
|
||||
const unsigned char *pt, unsigned long ptlen,
|
||||
unsigned char *ct, unsigned long *ctlen);
|
||||
|
|
@ -2571,13 +2572,13 @@ The key to the encrypt operation is passed in \textit{key} of length \textit{ke
|
|||
|
||||
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.
|
||||
The number of AAD elements is passed in \textit{adnum}, when it equals \texttt{0} both \textit{ad} and \textit{adlen} may be \texttt{NULL}.
|
||||
|
||||
\index{siv\_decrypt\_memory()}
|
||||
\begin{verbatim}
|
||||
int siv_decrypt_memory( int cipher,
|
||||
const unsigned char *key, unsigned long keylen,
|
||||
unsigned long adnum,
|
||||
const unsigned char *ad[], unsigned long adlen[],
|
||||
const unsigned char *ct, unsigned long ctlen,
|
||||
unsigned char *pt, unsigned long *ptlen);
|
||||
|
|
@ -2609,9 +2610,10 @@ int main(void)
|
|||
* 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,
|
||||
3,
|
||||
((const unsigned char*[]) {(void*)"aad0", (void*)"aad1",
|
||||
(void*)"NONCE", NULL}),
|
||||
((unsigned long[]) {4, 4, 5, 0}),
|
||||
(void*)"NONCE"}),
|
||||
((unsigned long[]) {4, 4, 5}),
|
||||
plain, plainlen,
|
||||
ct, &ctlen)) != CRYPT_OK) {
|
||||
whine_and_pout(err);
|
||||
|
|
@ -2619,9 +2621,10 @@ int main(void)
|
|||
|
||||
if ((err = siv_decrypt_memory(find_cipher("aes"),
|
||||
((unsigned char[32]) {0x0}), 32,
|
||||
3,
|
||||
((const unsigned char*[]) {(void*)"aad0", (void*)"aad1",
|
||||
(void*)"NONCE", NULL}),
|
||||
((unsigned long[]) {4, 4, 5, 0}),
|
||||
(void*)"NONCE"}),
|
||||
((unsigned long[]) {4, 4, 5}),
|
||||
ct, ctlen,
|
||||
plain, &plainlen)) != CRYPT_OK) {
|
||||
whine_and_pout(err);
|
||||
|
|
@ -2641,6 +2644,7 @@ 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,
|
||||
unsigned long adnum,
|
||||
...);
|
||||
\end{verbatim}
|
||||
|
||||
|
|
@ -2648,6 +2652,7 @@ This will execute a SIV operation of the \textit{direction} (\texttt{LTC\_ENCRYP
|
|||
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.
|
||||
Exactly \textit{adnum} such pairs are read; the terminating \texttt{NULL} does not delimit the list; see \ref{SIV_zero_vs_empty_ad}.
|
||||
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
|
||||
|
|
@ -2677,7 +2682,7 @@ int main(void)
|
|||
((unsigned char[32]) {0x0}), 32,
|
||||
plain, plainlen,
|
||||
ct, &ctlen,
|
||||
"aad0", 4uL, "aad1", 4uL, "NONCE", 5uL, NULL)) != CRYPT_OK) {
|
||||
3, "aad0", 4uL, "aad1", 4uL, "NONCE", 5uL, NULL)) != CRYPT_OK) {
|
||||
whine_and_pout(err);
|
||||
}
|
||||
|
||||
|
|
@ -2685,7 +2690,7 @@ int main(void)
|
|||
((unsigned char[32]) {0x0}), 32,
|
||||
ct, ctlen,
|
||||
plain, &plainlen,
|
||||
"aad0", 4uL, "aad1", 4uL, "NONCE", 5uL, NULL)) != CRYPT_OK) {
|
||||
3, "aad0", 4uL, "aad1", 4uL, "NONCE", 5uL, NULL)) != CRYPT_OK) {
|
||||
whine_and_pout(err);
|
||||
}
|
||||
|
||||
|
|
@ -2694,6 +2699,29 @@ int main(void)
|
|||
\end{verbatim}
|
||||
\end{small}
|
||||
|
||||
\subsection{No Associated Data vs. Empty Associated Data}
|
||||
\label{SIV_zero_vs_empty_ad}
|
||||
|
||||
RFC 5297 computes the synthetic IV as $S2V(K_1, AD_1, \ldots, AD_m, P)$ i.e. S2V is fed a \textit{list} of strings
|
||||
and the result depends on the number of components not only on their contents.
|
||||
|
||||
Passing \textbf{no} associated data ($m = 0$) is therefore \textbf{not} the same as passing \textbf{one empty}
|
||||
associated data string ($m = 1$). For the same key and plaintext the two produce a different ciphertext and one
|
||||
will not decrypt as the other. This is also why \textit{adnum} has to be passed explicitly: neither a \texttt{NULL}
|
||||
pointer nor a length of \texttt{0} can terminate the list as both are valid contents of a component.
|
||||
|
||||
\begin{small}
|
||||
\begin{verbatim}
|
||||
/* no associated data, m = 0 */
|
||||
siv_encrypt_memory(cipher, key, keylen, 0, NULL, NULL, pt, ptlen, ct, &ctlen);
|
||||
siv_memory(cipher, LTC_ENCRYPT, key, keylen, pt, ptlen, ct, &ctlen, 0, NULL);
|
||||
|
||||
/* one empty associated data string, m = 1 */
|
||||
siv_encrypt_memory(cipher, key, keylen, 1, ((const unsigned char*[]) {NULL}), ((unsigned long[]) {0}), pt, ptlen, ct, &ctlen);
|
||||
siv_memory(cipher, LTC_ENCRYPT, key, keylen, pt, ptlen, ct, &ctlen, 1, NULL, 0, NULL);
|
||||
\end{verbatim}
|
||||
\end{small}
|
||||
|
||||
\mysection{GCM-SIV}
|
||||
\label{GCM-SIV}
|
||||
|
||||
|
|
|
|||
|
|
@ -191,7 +191,8 @@ static int s_siv_S2V(int cipher,
|
|||
return err;
|
||||
}
|
||||
|
||||
do {
|
||||
/* With zero AD components the loop runs zero times. */
|
||||
while (n < adnum) {
|
||||
if (n >= s_siv_max_aad_components) {
|
||||
return CRYPT_INPUT_TOO_LONG;
|
||||
}
|
||||
|
|
@ -199,7 +200,7 @@ static int s_siv_S2V(int cipher,
|
|||
return err;
|
||||
}
|
||||
n++;
|
||||
} while(n < adnum);
|
||||
}
|
||||
|
||||
return s_siv_S2V_T(&ctx, in, inlen, &D, V);
|
||||
}
|
||||
|
|
@ -470,23 +471,19 @@ int siv_memory( int cipher, int direction,
|
|||
goto err_out;
|
||||
}
|
||||
va_start(args, adnum);
|
||||
do {
|
||||
if (adnum) {
|
||||
aad = va_arg(args, const unsigned char*);
|
||||
aadlen = va_arg(args, unsigned long);
|
||||
} else {
|
||||
aad = NULL;
|
||||
aadlen = 0;
|
||||
}
|
||||
/* With zero AD components the loop runs zero times. */
|
||||
while (n < adnum) {
|
||||
if (n >= s_siv_max_aad_components) {
|
||||
err = CRYPT_INPUT_TOO_LONG;
|
||||
goto err_out2;
|
||||
}
|
||||
aad = va_arg(args, const unsigned char*);
|
||||
aadlen = va_arg(args, unsigned long);
|
||||
if ((err = s_siv_S2V_dbl_xor_cmac(&ctx, aad, aadlen, &D)) != CRYPT_OK) {
|
||||
goto err_out2;
|
||||
}
|
||||
n++;
|
||||
} while (n < adnum);
|
||||
}
|
||||
|
||||
if ((err = s_siv_S2V_T(&ctx, in_work, in_work_len, &D, &siv.V)) != CRYPT_OK) {
|
||||
goto err_out2;
|
||||
|
|
@ -614,6 +611,19 @@ int siv_test(void)
|
|||
0x3d, 0x58, 0x8e, 0x85, 0x64, 0xa5, 0xfe };
|
||||
|
||||
|
||||
/* Zero AD components vs. a single *empty* AD component (vectors were cross-checked against OpenSSL 3.5.6) */
|
||||
const unsigned char output_A1_zeroad[] =
|
||||
{ 0xf1, 0xc5, 0xfd, 0xea, 0xc1, 0xf1, 0x5a, 0x26,
|
||||
0x77, 0x9c, 0x15, 0x01, 0xf9, 0xfb, 0x75, 0x88,
|
||||
0x27, 0xe9, 0x46, 0xc6, 0x69, 0x08, 0x8a, 0xb0,
|
||||
0x6d, 0xa5, 0x8c, 0x5c, 0x83, 0x1c };
|
||||
const unsigned char output_A1_emptyad[] =
|
||||
{ 0xd1, 0x02, 0x2f, 0x5b, 0x36, 0x64, 0xe5, 0xa4,
|
||||
0xdf, 0xaf, 0x90, 0xf8, 0x5b, 0xe6, 0xf2, 0x8a,
|
||||
0xb6, 0x6c, 0xff, 0x6b, 0x8e, 0xca, 0x0b, 0x79,
|
||||
0xf0, 0x83, 0xb3, 0x9a, 0x09, 0x01 };
|
||||
const unsigned char *ad_emptyad[] = { NULL, NULL };
|
||||
unsigned long adlen_emptyad[] = { 0, 0 };
|
||||
|
||||
#define PL_PAIR(n) n, sizeof(n)
|
||||
struct {
|
||||
|
|
@ -632,6 +642,8 @@ int siv_test(void)
|
|||
{ PL_PAIR(Key_A2), PL_PAIR(Plaintext_A2), 3, &ad_A2, &adlen_A2, PL_PAIR(output_A2), "RFC5297 - A.2. Nonce-Based Authenticated Encryption Example" },
|
||||
{ PL_PAIR(Key_A2), PL_PAIR(Plaintext_A2), 3, &ad_ossl0, &adlen_ossl0, PL_PAIR(output_ossl0), "OpenSSL based example 0" },
|
||||
{ PL_PAIR(Key_A2), PL_PAIR(Plaintext_A2), 3, &ad_ossl1, &adlen_ossl1, PL_PAIR(output_ossl1), "OpenSSL based example 1" },
|
||||
{ PL_PAIR(Key_A1), PL_PAIR(Plaintext_A1), 0, NULL, NULL, PL_PAIR(output_A1_zeroad), "Zero AD components (ad = adlen = NULL)" },
|
||||
{ PL_PAIR(Key_A1), PL_PAIR(Plaintext_A1), 1, &ad_emptyad, &adlen_emptyad, PL_PAIR(output_A1_emptyad), "One empty AD component" },
|
||||
};
|
||||
#undef PL_PAIR
|
||||
|
||||
|
|
@ -736,6 +748,44 @@ int siv_test(void)
|
|||
return CRYPT_FAIL_TESTVECTOR;
|
||||
}
|
||||
|
||||
n++;
|
||||
|
||||
/* Testcase 0x4 - siv_memory() - zero AD components - encrypt */
|
||||
buflen = sizeof(buf);
|
||||
if ((err = siv_memory(cipher, LTC_ENCRYPT, Key_A1, sizeof(Key_A1), Plaintext_A1, sizeof(Plaintext_A1), buf, &buflen, 0, NULL)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
if (ltc_compare_testvector(buf, buflen, output_A1_zeroad, sizeof(output_A1_zeroad), "siv_memory() - zero AD components", n) != 0) {
|
||||
return CRYPT_FAIL_TESTVECTOR;
|
||||
}
|
||||
/* Testcase 0x1004 - siv_memory() - zero AD components - decrypt */
|
||||
buflen = sizeof(buf);
|
||||
if ((err = siv_memory(cipher, LTC_DECRYPT, Key_A1, sizeof(Key_A1), output_A1_zeroad, sizeof(output_A1_zeroad), buf, &buflen, 0, NULL)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
if (ltc_compare_testvector(buf, buflen, Plaintext_A1, sizeof(Plaintext_A1), "siv_memory() - zero AD components", n + 0x1000) != 0) {
|
||||
return CRYPT_FAIL_TESTVECTOR;
|
||||
}
|
||||
|
||||
n++;
|
||||
|
||||
/* Testcase 0x5 - siv_memory() - one empty AD component - encrypt */
|
||||
buflen = sizeof(buf);
|
||||
if ((err = siv_memory(cipher, LTC_ENCRYPT, Key_A1, sizeof(Key_A1), Plaintext_A1, sizeof(Plaintext_A1), buf, &buflen, 1, NULL, 0UL, NULL)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
if (ltc_compare_testvector(buf, buflen, output_A1_emptyad, sizeof(output_A1_emptyad), "siv_memory() - one empty AD component", n) != 0) {
|
||||
return CRYPT_FAIL_TESTVECTOR;
|
||||
}
|
||||
/* Testcase 0x1005 - siv_memory() - one empty AD component - decrypt */
|
||||
buflen = sizeof(buf);
|
||||
if ((err = siv_memory(cipher, LTC_DECRYPT, Key_A1, sizeof(Key_A1), output_A1_emptyad, sizeof(output_A1_emptyad), buf, &buflen, 1, NULL, 0UL, NULL)) != CRYPT_OK) {
|
||||
return err;
|
||||
}
|
||||
if (ltc_compare_testvector(buf, buflen, Plaintext_A1, sizeof(Plaintext_A1), "siv_memory() - one empty AD component", n + 0x1000) != 0) {
|
||||
return CRYPT_FAIL_TESTVECTOR;
|
||||
}
|
||||
|
||||
for (n = 0; n < LTC_ARRAY_SIZE(tmp); ++n) {
|
||||
tmp[n] = XCALLOC(1, tmpmax);
|
||||
if (tmp[n] == NULL) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue