mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 22:30:49 +02:00
c905033acf
1. Unaligned Buffer Tests Verify correct output when input/output buffers are byte-offset by 1, 2, and 3 bytes. - AES-CBC, AES-CTS, AES-CTR, AES-GCM, AES-CCM, AES-XTS - ChaCha20, ChaCha20-Poly1305 2. In-Place (Overlapping) Buffer Tests Verify correct output when out == in (same pointer for input and output). - AES-CTS, AES-GCM, AES-CCM, AES-XTS - ChaCha20, ChaCha20-Poly1305 3. Cross-Cipher Verification Tests Verify that a higher-level mode produces identical output when manually reconstructed from a lower-level primitive (typically AES-ECB + XOR). - AES-CBC (= ECB + XOR chaining) - AES-CFB (= ECB(ciphertext feedback) + XOR) - AES-OFB (= ECB(output feedback) + XOR) - AES-CTR (= ECB(counter) + XOR with big-endian increment) - AES-GCM (ciphertext portion = CTR starting at counter J0+1) - ChaCha20-Poly1305 (ciphertext = raw ChaCha20 keystream XOR; tag = independent Poly1305) 4. Counter Overflow Tests Verify correct carry propagation when the internal block counter wraps around. - AES-CTR (32-bit big-endian carry across 4 bytes: 0xFFFFFFFE → wrap) - ChaCha20 (32-bit counter: 0xFFFFFFFF → 0x00000000) 5. AEAD Edge Case Tests Verify correct behavior for empty inputs, empty AAD, and invalid auth tag rejection. - Ascon-AEAD128 - AES-CCM - ChaCha20-Poly1305 6. Non-Standard Parameter Tests Verify behavior outside the common fast path. - AES-GCM: non-96-bit nonce lengths (1-byte, 60-byte, variable-length loop, zero-length rejection) 7. Streaming API State Tests Verify mid-stream state behavior and re-initialization after a final call. - AES-GCM stream, AES-XTS stream - ChaCha20-Poly1305 stream
949 lines
42 KiB
C
949 lines
42 KiB
C
/* test_chacha20_poly1305.c
|
|
*
|
|
* Copyright (C) 2006-2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSL.
|
|
*
|
|
* wolfSSL is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSL is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
|
*/
|
|
|
|
#include <tests/unit.h>
|
|
|
|
#ifdef NO_INLINE
|
|
#include <wolfssl/wolfcrypt/misc.h>
|
|
#else
|
|
#define WOLFSSL_MISC_INCLUDED
|
|
#include <wolfcrypt/src/misc.c>
|
|
#endif
|
|
|
|
#include <wolfssl/wolfcrypt/chacha20_poly1305.h>
|
|
#include <wolfssl/wolfcrypt/types.h>
|
|
#include <tests/api/api.h>
|
|
#include <tests/api/test_chacha20_poly1305.h>
|
|
|
|
/*
|
|
* Testing wc_ChaCha20Poly1305_Encrypt() and wc_ChaCha20Poly1305_Decrypt()
|
|
*/
|
|
int test_wc_ChaCha20Poly1305_aead(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
|
const byte key[] = {
|
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
|
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f
|
|
};
|
|
const byte plaintext[] = {
|
|
0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61,
|
|
0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
|
|
0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20,
|
|
0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
|
|
0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39,
|
|
0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
|
|
0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66,
|
|
0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f,
|
|
0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20,
|
|
0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20,
|
|
0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75,
|
|
0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73,
|
|
0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f,
|
|
0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
|
|
0x74, 0x2e
|
|
};
|
|
const byte iv[] = {
|
|
0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43,
|
|
0x44, 0x45, 0x46, 0x47
|
|
};
|
|
const byte aad[] = { /* additional data */
|
|
0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3,
|
|
0xc4, 0xc5, 0xc6, 0xc7
|
|
};
|
|
const byte cipher[] = { /* expected output from operation */
|
|
0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
|
|
0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
|
|
0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
|
|
0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
|
|
0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
|
|
0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
|
|
0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
|
|
0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
|
|
0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
|
|
0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
|
|
0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
|
|
0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
|
|
0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
|
|
0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
|
|
0x61, 0x16
|
|
};
|
|
const byte authTag[] = { /* expected output from operation */
|
|
0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a,
|
|
0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91
|
|
};
|
|
byte generatedCiphertext[272];
|
|
byte generatedPlaintext[272];
|
|
byte generatedAuthTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
|
|
/* Initialize stack variables. */
|
|
XMEMSET(generatedCiphertext, 0, 272);
|
|
XMEMSET(generatedPlaintext, 0, 272);
|
|
|
|
/* Test Encrypt */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv, aad, sizeof(aad),
|
|
plaintext, sizeof(plaintext), generatedCiphertext, generatedAuthTag),
|
|
0);
|
|
ExpectIntEQ(XMEMCMP(generatedCiphertext, cipher,
|
|
sizeof(cipher)/sizeof(byte)), 0);
|
|
|
|
/* Test bad args. */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(NULL, iv, aad, sizeof(aad),
|
|
plaintext, sizeof(plaintext), generatedCiphertext, generatedAuthTag),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, NULL, aad, sizeof(aad),
|
|
plaintext, sizeof(plaintext), generatedCiphertext, generatedAuthTag),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv, aad, sizeof(aad), NULL,
|
|
sizeof(plaintext), generatedCiphertext, generatedAuthTag),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv, aad, sizeof(aad),
|
|
NULL, sizeof(plaintext), generatedCiphertext, generatedAuthTag),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv, aad, sizeof(aad),
|
|
plaintext, sizeof(plaintext), NULL, generatedAuthTag),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv, aad, sizeof(aad),
|
|
plaintext, sizeof(plaintext), generatedCiphertext, NULL),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv, aad, sizeof(aad), cipher,
|
|
sizeof(cipher), authTag, generatedPlaintext), 0);
|
|
ExpectIntEQ(XMEMCMP(generatedPlaintext, plaintext,
|
|
sizeof(plaintext)/sizeof(byte)), 0);
|
|
|
|
/* Test bad args. */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(NULL, iv, aad, sizeof(aad), cipher,
|
|
sizeof(cipher), authTag, generatedPlaintext),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, NULL, aad, sizeof(aad),
|
|
cipher, sizeof(cipher), authTag, generatedPlaintext),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv, aad, sizeof(aad), NULL,
|
|
sizeof(cipher), authTag, generatedPlaintext),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv, aad, sizeof(aad), cipher,
|
|
sizeof(cipher), NULL, generatedPlaintext),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv, aad, sizeof(aad), cipher,
|
|
sizeof(cipher), authTag, NULL), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv, aad, sizeof(aad), NULL,
|
|
sizeof(cipher), authTag, generatedPlaintext),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
} /* END test_wc_ChaCha20Poly1305_aead */
|
|
|
|
/*
|
|
* Testing wc_XChaCha20Poly1305_Encrypt() and wc_XChaCha20Poly1305_Decrypt()
|
|
* Test vector from Draft IRTF CFRG XChaCha Appendix A.3
|
|
*/
|
|
int test_wc_XChaCha20Poly1305_aead(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_POLY1305) && defined(HAVE_XCHACHA)
|
|
const byte key[] = {
|
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
|
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f
|
|
};
|
|
/* XChaCha uses a 24-byte nonce */
|
|
const byte nonce[] = {
|
|
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
|
|
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
|
|
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57
|
|
};
|
|
const byte plaintext[] = {
|
|
0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61,
|
|
0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
|
|
0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20,
|
|
0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
|
|
0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39,
|
|
0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
|
|
0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66,
|
|
0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f,
|
|
0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20,
|
|
0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20,
|
|
0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75,
|
|
0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73,
|
|
0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f,
|
|
0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
|
|
0x74, 0x2e
|
|
};
|
|
const byte aad[] = {
|
|
0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3,
|
|
0xc4, 0xc5, 0xc6, 0xc7
|
|
};
|
|
/* Expected combined ciphertext + 16-byte tag */
|
|
const byte expected[] = {
|
|
0xbd, 0x6d, 0x17, 0x9d, 0x3e, 0x83, 0xd4, 0x3b, 0x95, 0x76, 0x57, 0x94,
|
|
0x93, 0xc0, 0xe9, 0x39, 0x57, 0x2a, 0x17, 0x00, 0x25, 0x2b, 0xfa, 0xcc,
|
|
0xbe, 0xd2, 0x90, 0x2c, 0x21, 0x39, 0x6c, 0xbb, 0x73, 0x1c, 0x7f, 0x1b,
|
|
0x0b, 0x4a, 0xa6, 0x44, 0x0b, 0xf3, 0xa8, 0x2f, 0x4e, 0xda, 0x7e, 0x39,
|
|
0xae, 0x64, 0xc6, 0x70, 0x8c, 0x54, 0xc2, 0x16, 0xcb, 0x96, 0xb7, 0x2e,
|
|
0x12, 0x13, 0xb4, 0x52, 0x2f, 0x8c, 0x9b, 0xa4, 0x0d, 0xb5, 0xd9, 0x45,
|
|
0xb1, 0x1b, 0x69, 0xb9, 0x82, 0xc1, 0xbb, 0x9e, 0x3f, 0x3f, 0xac, 0x2b,
|
|
0xc3, 0x69, 0x48, 0x8f, 0x76, 0xb2, 0x38, 0x35, 0x65, 0xd3, 0xff, 0xf9,
|
|
0x21, 0xf9, 0x66, 0x4c, 0x97, 0x63, 0x7d, 0xa9, 0x76, 0x88, 0x12, 0xf6,
|
|
0x15, 0xc6, 0x8b, 0x13, 0xb5, 0x2e,
|
|
/* Authentication Tag */
|
|
0xc0, 0x87, 0x59, 0x24, 0xc1, 0xc7, 0x98, 0x79, 0x47, 0xde, 0xaf, 0xd8,
|
|
0x78, 0x0a, 0xcf, 0x49
|
|
};
|
|
|
|
byte out[256];
|
|
byte plain_out[256];
|
|
word32 outLen = sizeof(plaintext) + 16;
|
|
|
|
XMEMSET(out, 0, sizeof(out));
|
|
XMEMSET(plain_out, 0, sizeof(plain_out));
|
|
|
|
/* Test Encrypt (One-shot) */
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Encrypt(out, sizeof(out), plaintext,
|
|
sizeof(plaintext), aad, sizeof(aad), nonce, sizeof(nonce),
|
|
key, sizeof(key)), 0);
|
|
ExpectIntEQ(XMEMCMP(out, expected, outLen), 0);
|
|
|
|
/* Test Decrypt (One-shot) */
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Decrypt(plain_out, sizeof(plain_out), out,
|
|
outLen, aad, sizeof(aad), nonce, sizeof(nonce),
|
|
key, sizeof(key)), 0);
|
|
ExpectIntEQ(XMEMCMP(plain_out, plaintext, sizeof(plaintext)), 0);
|
|
|
|
/* Test Encrypt bad args. */
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Encrypt(NULL, sizeof(out), plaintext,
|
|
sizeof(plaintext), aad, sizeof(aad), nonce, sizeof(nonce),
|
|
key, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Encrypt(out, sizeof(out), NULL,
|
|
sizeof(plaintext), aad, sizeof(aad), nonce, sizeof(nonce),
|
|
key, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Encrypt(out, sizeof(out), plaintext,
|
|
sizeof(plaintext), NULL, sizeof(aad), nonce, sizeof(nonce),
|
|
key, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Encrypt(out, sizeof(out), plaintext,
|
|
sizeof(plaintext), aad, sizeof(aad), NULL, sizeof(nonce),
|
|
key, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Encrypt(out, sizeof(out), plaintext,
|
|
sizeof(plaintext), aad, sizeof(aad), nonce, sizeof(nonce),
|
|
NULL, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* Wrong nonce size (12 instead of 24) */
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Encrypt(out, sizeof(out), plaintext,
|
|
sizeof(plaintext), aad, sizeof(aad), nonce, 12,
|
|
key, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* Wrong key size */
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Encrypt(out, sizeof(out), plaintext,
|
|
sizeof(plaintext), aad, sizeof(aad), nonce, sizeof(nonce),
|
|
key, 16), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
|
|
/* Test Decrypt bad args. */
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Decrypt(NULL, sizeof(plain_out), out,
|
|
outLen, aad, sizeof(aad), nonce, sizeof(nonce),
|
|
key, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Decrypt(plain_out, sizeof(plain_out), NULL,
|
|
outLen, aad, sizeof(aad), nonce, sizeof(nonce),
|
|
key, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Decrypt(plain_out, sizeof(plain_out), out,
|
|
outLen, NULL, sizeof(aad), nonce, sizeof(nonce),
|
|
key, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Decrypt(plain_out, sizeof(plain_out), out,
|
|
outLen, aad, sizeof(aad), NULL, sizeof(nonce),
|
|
key, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Decrypt(plain_out, sizeof(plain_out), out,
|
|
outLen, aad, sizeof(aad), nonce, sizeof(nonce),
|
|
NULL, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* Wrong nonce size (12 instead of 24) */
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Decrypt(plain_out, sizeof(plain_out), out,
|
|
outLen, aad, sizeof(aad), nonce, 12,
|
|
key, sizeof(key)), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* Wrong key size */
|
|
ExpectIntEQ(wc_XChaCha20Poly1305_Decrypt(plain_out, sizeof(plain_out), out,
|
|
outLen, aad, sizeof(aad), nonce, sizeof(nonce),
|
|
key, 16), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
} /* END test_wc_XChaCha20Poly1305_aead */
|
|
|
|
#include <wolfssl/wolfcrypt/random.h>
|
|
|
|
#define MC_CIPHER_TEST_COUNT 100
|
|
#define MC_CHACHA20P1305_MAX_SZ 1024
|
|
|
|
/* Monte Carlo test for ChaCha20-Poly1305: random key, nonce, and plaintext
|
|
* each iteration */
|
|
int test_wc_ChaCha20Poly1305_MonteCarlo(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
|
WC_RNG rng;
|
|
byte key[CHACHA20_POLY1305_AEAD_KEYSIZE];
|
|
byte nonce[CHACHA20_POLY1305_AEAD_IV_SIZE];
|
|
byte tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
word32 plainLen = 0;
|
|
int i;
|
|
WC_DECLARE_VAR(plain, byte, MC_CHACHA20P1305_MAX_SZ, NULL);
|
|
WC_DECLARE_VAR(cipher, byte, MC_CHACHA20P1305_MAX_SZ, NULL);
|
|
WC_DECLARE_VAR(decrypted, byte, MC_CHACHA20P1305_MAX_SZ, NULL);
|
|
|
|
WC_ALLOC_VAR(plain, byte, MC_CHACHA20P1305_MAX_SZ, NULL);
|
|
WC_ALLOC_VAR(cipher, byte, MC_CHACHA20P1305_MAX_SZ, NULL);
|
|
WC_ALLOC_VAR(decrypted, byte, MC_CHACHA20P1305_MAX_SZ, NULL);
|
|
#ifdef WC_DECLARE_VAR_IS_HEAP_ALLOC
|
|
ExpectNotNull(plain);
|
|
ExpectNotNull(cipher);
|
|
ExpectNotNull(decrypted);
|
|
#endif
|
|
|
|
XMEMSET(&rng, 0, sizeof(rng));
|
|
|
|
ExpectIntEQ(wc_InitRng(&rng), 0);
|
|
|
|
for (i = 0; i < MC_CIPHER_TEST_COUNT && EXPECT_SUCCESS(); i++) {
|
|
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, key, sizeof(key)), 0);
|
|
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, nonce, sizeof(nonce)), 0);
|
|
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, (byte*)&plainLen,
|
|
sizeof(plainLen)), 0);
|
|
plainLen = (plainLen % MC_CHACHA20P1305_MAX_SZ) + 1;
|
|
ExpectIntEQ(wc_RNG_GenerateBlock(&rng, plain, plainLen), 0);
|
|
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, nonce, NULL, 0,
|
|
plain, plainLen, cipher, tag), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, nonce, NULL, 0,
|
|
cipher, plainLen, tag, decrypted), 0);
|
|
ExpectBufEQ(decrypted, plain, plainLen);
|
|
}
|
|
|
|
wc_FreeRng(&rng);
|
|
WC_FREE_VAR(plain, NULL);
|
|
WC_FREE_VAR(cipher, NULL);
|
|
WC_FREE_VAR(decrypted, NULL);
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
}
|
|
|
|
/*
|
|
* Testing wc_ChaCha20Poly1305_Init(), wc_ChaCha20Poly1305_UpdateAad(),
|
|
* wc_ChaCha20Poly1305_UpdateData(), and wc_ChaCha20Poly1305_Final()
|
|
* streaming API using the RFC 8439 Section 2.8.2 test vector.
|
|
*/
|
|
int test_wc_ChaCha20Poly1305_Stream(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
|
/* RFC 8439 Section 2.8.2 test vector */
|
|
static const byte key[] = {
|
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
|
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f
|
|
};
|
|
static const byte iv[] = {
|
|
0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43,
|
|
0x44, 0x45, 0x46, 0x47
|
|
};
|
|
static const byte aad[] = {
|
|
0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3,
|
|
0xc4, 0xc5, 0xc6, 0xc7
|
|
};
|
|
static const byte plaintext[] = {
|
|
0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61,
|
|
0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
|
|
0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20,
|
|
0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
|
|
0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39,
|
|
0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
|
|
0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66,
|
|
0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f,
|
|
0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20,
|
|
0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20,
|
|
0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75,
|
|
0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73,
|
|
0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f,
|
|
0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
|
|
0x74, 0x2e
|
|
};
|
|
static const byte expCipher[] = {
|
|
0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb,
|
|
0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2,
|
|
0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe,
|
|
0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6,
|
|
0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12,
|
|
0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b,
|
|
0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
|
|
0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36,
|
|
0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c,
|
|
0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58,
|
|
0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94,
|
|
0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc,
|
|
0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d,
|
|
0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
|
|
0x61, 0x16
|
|
};
|
|
static const byte expAuthTag[] = {
|
|
0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a,
|
|
0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91
|
|
};
|
|
ChaChaPoly_Aead aead;
|
|
byte outCipher[sizeof(plaintext)];
|
|
byte outPlain[sizeof(plaintext)];
|
|
byte outTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
|
|
/* --- Streaming encrypt: AAD in two chunks, plaintext in three chunks --- */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, 6), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad + 6,
|
|
(word32)(sizeof(aad) - 6)), 0);
|
|
XMEMSET(outCipher, 0, sizeof(outCipher));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, plaintext,
|
|
outCipher, 38), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, plaintext + 38,
|
|
outCipher + 38, 38), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, plaintext + 76,
|
|
outCipher + 76, (word32)(sizeof(plaintext) - 76)), 0);
|
|
XMEMSET(outTag, 0, sizeof(outTag));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, outTag), 0);
|
|
ExpectBufEQ(outCipher, expCipher, sizeof(expCipher));
|
|
ExpectBufEQ(outTag, expAuthTag, sizeof(expAuthTag));
|
|
|
|
/* --- Streaming decrypt: single AAD chunk, ciphertext in three chunks --- */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv,
|
|
CHACHA20_POLY1305_AEAD_DECRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad,
|
|
(word32)sizeof(aad)), 0);
|
|
XMEMSET(outPlain, 0, sizeof(outPlain));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, expCipher,
|
|
outPlain, 38), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, expCipher + 38,
|
|
outPlain + 38, 38), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, expCipher + 76,
|
|
outPlain + 76, (word32)(sizeof(expCipher) - 76)), 0);
|
|
XMEMSET(outTag, 0, sizeof(outTag));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, outTag), 0);
|
|
ExpectBufEQ(outPlain, plaintext, sizeof(plaintext));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_CheckTag(outTag, expAuthTag), 0);
|
|
|
|
/* --- Bad args --- */
|
|
/* wc_ChaCha20Poly1305_Init: NULL aead */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(NULL, key, iv,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* wc_ChaCha20Poly1305_Init: NULL key */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, NULL, iv,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* wc_ChaCha20Poly1305_Init: NULL iv */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, NULL,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* wc_ChaCha20Poly1305_UpdateAad: NULL aead */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(NULL, aad, (word32)sizeof(aad)),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* wc_ChaCha20Poly1305_UpdateData: NULL aead */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(NULL, plaintext, outCipher,
|
|
(word32)sizeof(plaintext)),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* wc_ChaCha20Poly1305_Final: NULL aead */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(NULL, outTag),
|
|
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
|
|
/* wc_ChaCha20Poly1305_Final: wrong state (INIT, not AAD/DATA) */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, outTag),
|
|
WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
} /* END test_wc_ChaCha20Poly1305_Stream */
|
|
|
|
/*
|
|
* ChaCha20-Poly1305 AEAD edge cases:
|
|
* - invalid auth tag rejection (one-shot API)
|
|
* - empty plaintext with non-empty AAD (streaming API)
|
|
*/
|
|
int test_wc_ChaCha20Poly1305_AeadEdgeCases(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
|
/* RFC 8439 Section 2.8.2 key/iv/aad */
|
|
static const byte key[] = {
|
|
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
|
|
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
|
|
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
|
|
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f
|
|
};
|
|
static const byte iv[] = {
|
|
0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43,
|
|
0x44, 0x45, 0x46, 0x47
|
|
};
|
|
static const byte aad[] = {
|
|
0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3,
|
|
0xc4, 0xc5, 0xc6, 0xc7
|
|
};
|
|
static const byte plaintext[] = {
|
|
0x4c, 0x61, 0x64, 0x69, 0x65, 0x73, 0x20, 0x61,
|
|
0x6e, 0x64, 0x20, 0x47, 0x65, 0x6e, 0x74, 0x6c,
|
|
0x65, 0x6d, 0x65, 0x6e, 0x20, 0x6f, 0x66, 0x20,
|
|
0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x61, 0x73,
|
|
0x73, 0x20, 0x6f, 0x66, 0x20, 0x27, 0x39, 0x39,
|
|
0x3a, 0x20, 0x49, 0x66, 0x20, 0x49, 0x20, 0x63,
|
|
0x6f, 0x75, 0x6c, 0x64, 0x20, 0x6f, 0x66, 0x66,
|
|
0x65, 0x72, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x6f,
|
|
0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e, 0x65, 0x20,
|
|
0x74, 0x69, 0x70, 0x20, 0x66, 0x6f, 0x72, 0x20,
|
|
0x74, 0x68, 0x65, 0x20, 0x66, 0x75, 0x74, 0x75,
|
|
0x72, 0x65, 0x2c, 0x20, 0x73, 0x75, 0x6e, 0x73,
|
|
0x63, 0x72, 0x65, 0x65, 0x6e, 0x20, 0x77, 0x6f,
|
|
0x75, 0x6c, 0x64, 0x20, 0x62, 0x65, 0x20, 0x69,
|
|
0x74, 0x2e
|
|
};
|
|
ChaChaPoly_Aead aead;
|
|
byte cipherOut[sizeof(plaintext)];
|
|
byte plainOut[sizeof(plaintext)];
|
|
byte authTag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
byte authTagDecrypt[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
|
|
/* --- Invalid tag rejection (one-shot API) ---
|
|
* Encrypt with correct key/iv/aad/pt, then flip a tag byte and
|
|
* verify that Decrypt returns MAC_CMP_FAILED_E. */
|
|
XMEMSET(cipherOut, 0, sizeof(cipherOut));
|
|
XMEMSET(authTag, 0, sizeof(authTag));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv, aad, sizeof(aad),
|
|
plaintext, sizeof(plaintext), cipherOut, authTag), 0);
|
|
authTag[0] ^= 0xff;
|
|
XMEMSET(plainOut, 0, sizeof(plainOut));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv, aad, sizeof(aad),
|
|
cipherOut, sizeof(cipherOut), authTag, plainOut),
|
|
WC_NO_ERR_TRACE(MAC_CMP_FAILED_E));
|
|
|
|
/* --- Empty plaintext with non-empty AAD (streaming API) ---
|
|
* Init + UpdateAad + Final, no UpdateData call.
|
|
* Correct computed tag must verify; tampered tag must fail. */
|
|
XMEMSET(authTag, 0, sizeof(authTag));
|
|
XMEMSET(authTagDecrypt, 0, sizeof(authTagDecrypt));
|
|
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, authTag), 0);
|
|
|
|
/* Decrypt with same AAD and no data; verify tag matches */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv,
|
|
CHACHA20_POLY1305_AEAD_DECRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, authTagDecrypt), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_CheckTag(authTagDecrypt, authTag), 0);
|
|
|
|
/* Tamper and verify CheckTag rejects it */
|
|
authTagDecrypt[0] ^= 0xff;
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_CheckTag(authTagDecrypt, authTag),
|
|
WC_NO_ERR_TRACE(MAC_CMP_FAILED_E));
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
} /* END test_wc_ChaCha20Poly1305_AeadEdgeCases */
|
|
|
|
/*******************************************************************************
|
|
* ChaCha20-Poly1305 mid-stream state corruption
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* Verify that the ChaCha20-Poly1305 streaming state machine rejects operations
|
|
* called in the wrong order, and handles post-Final reuse gracefully.
|
|
*
|
|
* State transitions: INIT(0) -> READY(1) -> AAD(2) -> DATA(3)
|
|
* UpdateAad: READY or AAD only
|
|
* UpdateData: READY, AAD, or DATA
|
|
* Final: AAD or DATA only
|
|
* After Final, ForceZero resets the struct to all-zeros (state == INIT).
|
|
*/
|
|
int test_wc_ChaCha20Poly1305_MidStreamState(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
|
static const byte key[CHACHA20_POLY1305_AEAD_KEYSIZE] = {
|
|
0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
|
|
0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
|
|
0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
|
|
0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f
|
|
};
|
|
static const byte iv[CHACHA20_POLY1305_AEAD_IV_SIZE] = {
|
|
0x07,0x00,0x00,0x00, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47
|
|
};
|
|
static const byte aad[8] = { 0x50,0x51,0x52,0x53, 0xc0,0xc1,0xc2,0xc3 };
|
|
static const byte plain[8] = { 0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07 };
|
|
ChaChaPoly_Aead aead;
|
|
byte ct[8];
|
|
byte tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
|
|
/* ------------------------------------------------------------------
|
|
* Test 1: UpdateAad after UpdateData (DATA state) -> BAD_STATE_E
|
|
* Once UpdateData has been called the state advances to DATA and any
|
|
* further UpdateAad calls must be rejected.
|
|
* ------------------------------------------------------------------ */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, plain, ct,
|
|
sizeof(plain)), 0);
|
|
/* State is now DATA - UpdateAad must fail. */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)),
|
|
WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
/* Clean up the aead object so the next test starts fresh. */
|
|
XMEMSET(&aead, 0, sizeof(aead));
|
|
|
|
/* ------------------------------------------------------------------
|
|
* Test 2: UpdateData in INIT state (no Init called) -> BAD_STATE_E
|
|
* state == INIT(0): UpdateData requires READY(1), AAD(2), or DATA(3).
|
|
* ------------------------------------------------------------------ */
|
|
/* aead was zeroed above so state == INIT. */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, plain, ct, sizeof(plain)),
|
|
WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
|
|
/* ------------------------------------------------------------------
|
|
* Test 3: Reuse after Final - state reset to INIT by ForceZero
|
|
* wc_ChaCha20Poly1305_Final calls ForceZero on the whole struct, which
|
|
* sets state back to INIT(0). Any subsequent streaming call must fail.
|
|
* ------------------------------------------------------------------ */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)), 0);
|
|
/* First Final succeeds (state == AAD). */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, tag), 0);
|
|
/* State is now INIT (all zeros after ForceZero). */
|
|
/* Second Final must fail. */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, tag),
|
|
WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
/* UpdateAad after Final must also fail. */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)),
|
|
WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
/* UpdateData after Final must also fail. */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, plain, ct, sizeof(plain)),
|
|
WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
|
|
/* ------------------------------------------------------------------
|
|
* Test 4: Direct state field corruption to an invalid value
|
|
* Forcing state to a value outside the defined enum range makes all
|
|
* state-checking calls return BAD_STATE_E.
|
|
* ------------------------------------------------------------------ */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
|
/* Corrupt state: 99 is not a valid CHACHA20_POLY1305_STATE_* value. */
|
|
aead.state = 99;
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)),
|
|
WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, plain, ct, sizeof(plain)),
|
|
WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, tag),
|
|
WC_NO_ERR_TRACE(BAD_STATE_E));
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
} /* END test_wc_ChaCha20Poly1305_MidStreamState */
|
|
|
|
/*******************************************************************************
|
|
* ChaCha20-Poly1305 re-initialization after Final
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* Verify that a ChaCha20-Poly1305 AEAD context can be re-initialized and
|
|
* reused after wc_ChaCha20Poly1305_Final has been called.
|
|
*
|
|
* wc_ChaCha20Poly1305_Final calls ForceZero on the whole ChaChaPoly_Aead
|
|
* struct, so a fresh wc_ChaCha20Poly1305_Init is needed before the next
|
|
* session. These tests confirm:
|
|
*
|
|
* 1. Re-init with the same key and IV produces identical ciphertext and tag.
|
|
* 2. Re-init with a different IV produces different ciphertext and tag.
|
|
* 3. Re-init after an *abandoned* session (Init but no Final) also works.
|
|
*/
|
|
int test_wc_ChaCha20Poly1305_ReinitAfterFinal(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
|
static const byte key[CHACHA20_POLY1305_AEAD_KEYSIZE] = {
|
|
0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
|
|
0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
|
|
0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
|
|
0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f
|
|
};
|
|
static const byte iv1[CHACHA20_POLY1305_AEAD_IV_SIZE] = {
|
|
0x07,0x00,0x00,0x00, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47
|
|
};
|
|
/* Distinct IV - same length, one byte different. */
|
|
static const byte iv2[CHACHA20_POLY1305_AEAD_IV_SIZE] = {
|
|
0x07,0x00,0x00,0x00, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x48
|
|
};
|
|
static const byte aad[] = {
|
|
0x50,0x51,0x52,0x53, 0xc0,0xc1,0xc2,0xc3,
|
|
0xc4,0xc5,0xc6,0xc7
|
|
};
|
|
static const byte plain[] = {
|
|
0x4c,0x61,0x64,0x69, 0x65,0x73,0x20,0x61,
|
|
0x6e,0x64,0x20,0x47, 0x65,0x6e,0x74,0x6c
|
|
};
|
|
ChaChaPoly_Aead aead;
|
|
byte ct1[sizeof(plain)];
|
|
byte ct2[sizeof(plain)];
|
|
byte ct3[sizeof(plain)];
|
|
byte tag1[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
byte tag2[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
byte tag3[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
|
|
/* ---- Session 1: establish baseline ciphertext and tag ---- */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv1,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, plain, ct1,
|
|
sizeof(plain)), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, tag1), 0);
|
|
|
|
/* ---- Session 2: re-init with the same key and IV ---- */
|
|
/* aead was ForceZero'd by Final; Init must succeed. */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv1,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, plain, ct2,
|
|
sizeof(plain)), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, tag2), 0);
|
|
/* Same key + IV must produce identical output. */
|
|
ExpectBufEQ(ct2, ct1, sizeof(ct1));
|
|
ExpectBufEQ(tag2, tag1, sizeof(tag1));
|
|
|
|
/* ---- Session 3: re-init with a different IV ---- */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv2,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, plain, ct3,
|
|
sizeof(plain)), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, tag3), 0);
|
|
/* Different IV must produce different ciphertext and tag. */
|
|
ExpectIntNE(XMEMCMP(ct3, ct1, sizeof(ct1)), 0);
|
|
ExpectIntNE(XMEMCMP(tag3, tag1, sizeof(tag1)), 0);
|
|
|
|
/* ---- Session 4: re-init after an abandoned session ----
|
|
* Start a session (Init + UpdateAad) but never call Final.
|
|
* Then re-init and complete normally - must match session 1. */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv2,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)), 0);
|
|
/* Abandon this session - manually reset before re-init. */
|
|
XMEMSET(&aead, 0, sizeof(aead));
|
|
/* Now re-init with iv1 and verify we get session-1 output again. */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Init(&aead, key, iv1,
|
|
CHACHA20_POLY1305_AEAD_ENCRYPT), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateAad(&aead, aad, sizeof(aad)), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_UpdateData(&aead, plain, ct2,
|
|
sizeof(plain)), 0);
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Final(&aead, tag2), 0);
|
|
ExpectBufEQ(ct2, ct1, sizeof(ct1));
|
|
ExpectBufEQ(tag2, tag1, sizeof(tag1));
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
} /* END test_wc_ChaCha20Poly1305_ReinitAfterFinal */
|
|
|
|
/*
|
|
* Verify that wc_ChaCha20Poly1305_Encrypt and wc_ChaCha20Poly1305_Decrypt work
|
|
* correctly when the plaintext/ciphertext pointer is the same buffer (in-place
|
|
* operation). The cipher uses a ChaCha20 keystream XOR, so in == out is safe.
|
|
* The Poly1305 tag is always a separate output buffer.
|
|
*
|
|
* RFC 8439 2.8.2 key, IV, and AAD are used with a 64-byte counter-pattern
|
|
* plaintext (self-consistency: reference ciphertext computed at test time).
|
|
*/
|
|
int test_wc_ChaCha20Poly1305_InPlace(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
|
static const byte key[CHACHA20_POLY1305_AEAD_KEYSIZE] = {
|
|
0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
|
|
0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
|
|
0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
|
|
0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f
|
|
};
|
|
static const byte iv[CHACHA20_POLY1305_AEAD_IV_SIZE] = {
|
|
0x07,0x00,0x00,0x00, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47
|
|
};
|
|
static const byte aad[12] = {
|
|
0x50,0x51,0x52,0x53, 0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7
|
|
};
|
|
/* 67-byte counter pattern: spans one full ChaCha20 block (64 B) plus
|
|
* a 3-byte partial tail, exercising both full-block and leftover paths. */
|
|
static const byte plain[67] = {
|
|
0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
|
|
0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
|
|
0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
|
|
0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
|
|
0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
|
|
0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
|
|
0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
|
|
0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
|
|
0x40,0x41,0x42
|
|
};
|
|
byte ref_ct[sizeof(plain)], ref_tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
byte buf[sizeof(plain)], tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
|
|
/* Reference ciphertext with separate in/out buffers */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv,
|
|
aad, sizeof(aad), plain, sizeof(plain), ref_ct, ref_tag), 0);
|
|
|
|
/* Encrypt in-place (outCiphertext == inPlaintext) */
|
|
XMEMCPY(buf, plain, sizeof(buf));
|
|
XMEMSET(tag, 0, sizeof(tag));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv,
|
|
aad, sizeof(aad), buf, sizeof(buf), buf, tag), 0);
|
|
ExpectBufEQ(buf, ref_ct, sizeof(buf));
|
|
ExpectBufEQ(tag, ref_tag, sizeof(tag));
|
|
|
|
/* Decrypt in-place (outPlaintext == inCiphertext) */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv,
|
|
aad, sizeof(aad), buf, sizeof(buf), tag, buf), 0);
|
|
ExpectBufEQ(buf, plain, sizeof(buf));
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
} /* END test_wc_ChaCha20Poly1305_InPlace */
|
|
|
|
/*
|
|
* Verify that wc_ChaCha20Poly1305_Encrypt and wc_ChaCha20Poly1305_Decrypt
|
|
* produce correct results when plaintext, ciphertext, and AAD buffers are
|
|
* byte-offset (unaligned). Tests offsets 1, 2, and 3.
|
|
*/
|
|
int test_wc_ChaCha20Poly1305_UnalignedBuffers(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
|
/* Same key / IV / AAD as InPlace test */
|
|
static const byte key[CHACHA20_POLY1305_AEAD_KEYSIZE] = {
|
|
0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
|
|
0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
|
|
0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
|
|
0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f
|
|
};
|
|
static const byte iv[CHACHA20_POLY1305_AEAD_IV_SIZE] = {
|
|
0x07,0x00,0x00,0x00, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47
|
|
};
|
|
static const byte aad[12] = {
|
|
0x50,0x51,0x52,0x53, 0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7
|
|
};
|
|
/* 67-byte counter pattern - same as InPlace test */
|
|
static const byte plain[67] = {
|
|
0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
|
|
0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
|
|
0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
|
|
0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
|
|
0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
|
|
0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
|
|
0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
|
|
0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
|
|
0x40,0x41,0x42
|
|
};
|
|
byte ref_ct[sizeof(plain)], ref_tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
byte in_buf[sizeof(plain) + 3], out_buf[sizeof(plain) + 3];
|
|
byte aad_buf[sizeof(aad) + 3];
|
|
byte tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
int off;
|
|
|
|
/* Reference ciphertext/tag with naturally-aligned buffers */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv,
|
|
aad, sizeof(aad), plain, sizeof(plain), ref_ct, ref_tag), 0);
|
|
|
|
/* Encrypt with byte offsets 1, 2, 3 on plaintext, ciphertext, and AAD */
|
|
for (off = 1; off <= 3 && EXPECT_SUCCESS(); off++) {
|
|
XMEMCPY(in_buf + off, plain, sizeof(plain));
|
|
XMEMCPY(aad_buf + off, aad, sizeof(aad));
|
|
XMEMSET(out_buf, 0, sizeof(out_buf));
|
|
XMEMSET(tag, 0, sizeof(tag));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv,
|
|
aad_buf + off, sizeof(aad), in_buf + off, sizeof(plain),
|
|
out_buf + off, tag), 0);
|
|
ExpectBufEQ(out_buf + off, ref_ct, sizeof(plain));
|
|
ExpectBufEQ(tag, ref_tag, sizeof(tag));
|
|
}
|
|
|
|
/* Decrypt with byte offsets 1, 2, 3 */
|
|
for (off = 1; off <= 3 && EXPECT_SUCCESS(); off++) {
|
|
XMEMCPY(in_buf + off, ref_ct, sizeof(plain));
|
|
XMEMCPY(aad_buf + off, aad, sizeof(aad));
|
|
XMEMSET(out_buf, 0, sizeof(out_buf));
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Decrypt(key, iv,
|
|
aad_buf + off, sizeof(aad), in_buf + off, sizeof(plain),
|
|
ref_tag, out_buf + off), 0);
|
|
ExpectBufEQ(out_buf + off, plain, sizeof(plain));
|
|
}
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
} /* END test_wc_ChaCha20Poly1305_UnalignedBuffers */
|
|
|
|
/*
|
|
* Cross-cipher test: ChaCha20-Poly1305 encrypts plaintext using ChaCha20 with
|
|
* the block counter starting at 1. Counter 0 is reserved for generating the
|
|
* 32-byte Poly1305 one-time key; plaintext encryption begins at counter 1.
|
|
*
|
|
* This test verifies that the ciphertext produced by
|
|
* wc_ChaCha20Poly1305_Encrypt equals the output of wc_Chacha_Process when
|
|
* the counter is initialised to 1 via wc_Chacha_SetIV(ctx, iv, 1).
|
|
*/
|
|
int test_wc_ChaCha20Poly1305_CrossCipher(void)
|
|
{
|
|
EXPECT_DECLS;
|
|
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
|
|
ChaCha ctx;
|
|
/* Same key / IV / plain as the InPlace and UnalignedBuffers tests */
|
|
static const byte key[CHACHA20_POLY1305_AEAD_KEYSIZE] = {
|
|
0x80,0x81,0x82,0x83, 0x84,0x85,0x86,0x87,
|
|
0x88,0x89,0x8a,0x8b, 0x8c,0x8d,0x8e,0x8f,
|
|
0x90,0x91,0x92,0x93, 0x94,0x95,0x96,0x97,
|
|
0x98,0x99,0x9a,0x9b, 0x9c,0x9d,0x9e,0x9f
|
|
};
|
|
static const byte iv[CHACHA20_POLY1305_AEAD_IV_SIZE] = {
|
|
0x07,0x00,0x00,0x00, 0x40,0x41,0x42,0x43, 0x44,0x45,0x46,0x47
|
|
};
|
|
static const byte aad[12] = {
|
|
0x50,0x51,0x52,0x53, 0xc0,0xc1,0xc2,0xc3, 0xc4,0xc5,0xc6,0xc7
|
|
};
|
|
static const byte plain[67] = {
|
|
0x00,0x01,0x02,0x03, 0x04,0x05,0x06,0x07,
|
|
0x08,0x09,0x0a,0x0b, 0x0c,0x0d,0x0e,0x0f,
|
|
0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17,
|
|
0x18,0x19,0x1a,0x1b, 0x1c,0x1d,0x1e,0x1f,
|
|
0x20,0x21,0x22,0x23, 0x24,0x25,0x26,0x27,
|
|
0x28,0x29,0x2a,0x2b, 0x2c,0x2d,0x2e,0x2f,
|
|
0x30,0x31,0x32,0x33, 0x34,0x35,0x36,0x37,
|
|
0x38,0x39,0x3a,0x3b, 0x3c,0x3d,0x3e,0x3f,
|
|
0x40,0x41,0x42
|
|
};
|
|
byte aead_ct[sizeof(plain)], aead_tag[CHACHA20_POLY1305_AEAD_AUTHTAG_SIZE];
|
|
byte chacha_ct[sizeof(plain)];
|
|
|
|
/* ChaCha20-Poly1305 ciphertext */
|
|
ExpectIntEQ(wc_ChaCha20Poly1305_Encrypt(key, iv,
|
|
aad, sizeof(aad), plain, sizeof(plain), aead_ct, aead_tag), 0);
|
|
|
|
/* ChaCha20 ciphertext with counter=1 (counter 0 is the Poly1305 key block) */
|
|
ExpectIntEQ(wc_Chacha_SetKey(&ctx, key, sizeof(key)), 0);
|
|
ExpectIntEQ(wc_Chacha_SetIV(&ctx, iv, 1), 0);
|
|
ExpectIntEQ(wc_Chacha_Process(&ctx, chacha_ct, plain, sizeof(plain)), 0);
|
|
|
|
/* ChaCha20-Poly1305 ciphertext must equal ChaCha20(counter=1) ciphertext */
|
|
ExpectBufEQ(aead_ct, chacha_ct, sizeof(plain));
|
|
#endif
|
|
return EXPECT_RESULT();
|
|
} /* END test_wc_ChaCha20Poly1305_CrossCipher */
|