/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Shared KUnit test cases for AEAD algorithms, including a benchmark
*
* Copyright 2026 Google LLC
*/
/*
* This file implements KUnit test cases shared by the different KUnit test
* suites for Authenticated Encryption with Associated Data (AEAD) algorithms.
*
* Test suites including this file must #define the following:
*
* Data structs:
* - AEAD_KEY: name of key struct
* - AEAD_CTX: name of context for incremental computation
*
* Constants:
* - AEAD_VALID_KEY_LENS: array of all valid key lengths in bytes
* - AEAD_VALID_NONCE_LENS: array of all valid nonce lengths in bytes
* - AEAD_VALID_TAG_LENS: array of all valid authtag lengths in bytes
* - AEAD_MAX_KEY_LEN: max key length in bytes (assumed to fit on stack)
* - AEAD_MAX_NONCE_LEN: max nonce length in bytes (assumed to fit on stack)
* - AEAD_MAX_TAG_LEN: max authtag length in bytes (assumed to fit on stack)
* - AEAD_MONTE_CARLO_CHECKSUM: checksum of a deterministically generated series
* of (ciphertext, authtag) pairs (see test_aead_monte_carlo())
*
* Functions:
* - AEAD_PREPAREKEY: key preparation
* - AEAD_ENCRYPT and AEAD_DECRYPT: one-shot encryption and decryption
* - AEAD_INIT, AEAD_AUTH_UPDATE, AEAD_ENCRYPT_UPDATE, AEAD_ENCRYPT_FINAL,
* AEAD_DECRYPT_UPDATE, AEAD_DECRYPT_FINAL: functions for incremental
* encryption and decryption
*
* Function prototypes and their behavior must match the AES-CCM API.
*/
#include <crypto/blake2s.h>
#include <kunit/run-in-irq-context.h>
#include <kunit/test.h>
#include <linux/ktime.h>
#include <linux/preempt.h>
#include "test-utils.h"
/*
* Allocate a KUnit-managed struct AEAD_KEY and prepare it with a random key,
* using a random key length and random authentication tag length.
*/
static struct AEAD_KEY *aead_alloc_random_key(struct kunit *test,
size_t *tag_len_ret)
{
size_t key_len =
AEAD_VALID_KEY_LENS[rand32() % ARRAY_SIZE(AEAD_VALID_KEY_LENS)];
size_t tag_len =
AEAD_VALID_TAG_LENS[rand32() % ARRAY_SIZE(AEAD_VALID_TAG_LENS)];
u8 raw_key[AEAD_MAX_KEY_LEN];
struct AEAD_KEY *key = alloc_buf(test, sizeof(*key));
int err;
rand_bytes(raw_key, key_len);
err = AEAD_PREPAREKEY(key, raw_key, key_len, tag_len);
KUNIT_ASSERT_EQ(test, 0, err);
*tag_len_ret = tag_len;
return key;
}
/*
* Allocate a KUnit-managed slab buffer of length @len bytes and initialize it
* with random data.
*/
static u8 *aead_alloc_random_data(struct kunit *test, size_t len)
{
u8 *buf = alloc_buf(test, len);
rand_bytes(buf, len);
return buf;
}
/*
* Allocate a KUnit-managed guarded buffer of length @len bytes and initialize
* it with random data.
*/
static u8 *aead_alloc_random_data_guarded(struct kunit *test, size_t len)
{
u8 *buf = alloc_guarded_buf(test, len);
rand_bytes(buf, len);
return buf;
}
/* Process the given associated data using a random incremental strategy. */
static size_t aead_auth_incrementally(struct AEAD_CTX *ctx, const u8 *ad,
size_t ad_len)
{
size_t num_parts = 0;
size_t pos = 0;
while (rand_bool()) {
size_t part_len = rand_length(ad_len - pos);
AEAD_AUTH_UPDATE(ctx, &ad[pos], part_len);
pos += part_len;
num_parts++;
}
if (pos < ad_len || rand_bool()) {
AEAD_AUTH_UPDATE(ctx, &ad[pos], ad_len - pos);
num_parts++;
}
return num_parts;
}
/* Process the given en/decrypted data using a random incremental strategy. */
static size_t aead_crypt_incrementally(struct AEAD_CTX *ctx, u8 *dst,
const u8 *src, size_t data_len, bool enc)
{
size_t num_parts = 0;
size_t pos = 0;
while (rand_bool()) {
size_t part_len = rand_length(data_len - pos);
if (enc)
AEAD_ENCRYPT_UPDATE(ctx, &dst[pos], &src[pos],
part_len);
else
AEAD_DECRYPT_UPDATE(ctx, &dst[pos], &src[pos],
part_len);
pos += part_len;
num_parts++;
}
if (pos < data_len || rand_bool()) {
if (enc)
AEAD_ENCRYPT_UPDATE(ctx, &dst[pos], &src[pos],
data_len - pos);
else
AEAD_DECRYPT_UPDATE(ctx, &dst[pos], &src[pos],
data_len - pos);
num_parts++;
}
return num_parts;
}
struct aead_incremental_info {
size_t num_data_parts;
size_t num_ad_parts;
};
static const char *aead_incr_info_str(struct kunit *test,
const struct aead_incremental_info *info)