Skip to content

Commit e715964

Browse files
authored
AES-XTS Enc Dec test on rand incremental length inputs (#2795)
Add incremental length random input message tests for AES-XTS encryption/decryption ## Issues: N/A ## Description of changes: AWS-LC's current AES-XTS tests use fixed-size test vectors but do not systematically verify that encryption and decryption work correctly across different message lengths. This PR adds a new test that generates random inputs of incremental lengths and verify that `encrypt(decrypt(input)) == input` for each length. This ensures that AES-XTS correctly handles messages of varying sizes, particularly: - Messages at and above the minimum length (16 bytes) - Messages that require ciphertext stealing (non-block-aligned sizes) - Messages of various sizes up to the practical maximum The tests also add explicit length equality checks between plaintext and ciphertext to catch potential length-related bugs early. ## Call-outs: The test provides better confidence in correctness across different input sizes. ## Testing: `./crypto/crypto_test --gtest_filter=XTSTest.EncryptDecryptRand.` By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.
1 parent 1820fee commit e715964

File tree

1 file changed

+104
-1
lines changed

1 file changed

+104
-1
lines changed

crypto/fipsmodule/modes/xts_test.cc

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include <openssl/cipher.h>
1616
#include <openssl/aes.h>
17+
#include <openssl/rand.h>
1718

1819
#include <vector>
1920

@@ -23,6 +24,8 @@
2324
#include "internal.h"
2425
#include "../../test/test_util.h"
2526

27+
static constexpr size_t AESXTS_RAND_MSG_MAX_LEN = 8192;
28+
2629
#if defined(OPENSSL_LINUX)
2730
#include <sys/mman.h>
2831
#endif
@@ -1202,7 +1205,7 @@ TEST(XTSTest, TestVectors) {
12021205
in_p = in.get();
12031206
out_p = out.get();
12041207
#endif
1205-
for (bool in_place : {false, true}) {
1208+
for (bool in_place : {false, true}) {
12061209
SCOPED_TRACE(in_place);
12071210

12081211
// Test encryption.
@@ -1240,6 +1243,106 @@ TEST(XTSTest, TestVectors) {
12401243
#endif
12411244
}
12421245

1246+
TEST(XTSTest, EncryptDecryptRand) {
1247+
#if defined(OPENSSL_LINUX)
1248+
int pagesize = sysconf(_SC_PAGE_SIZE);
1249+
ASSERT_GE(pagesize, 0);
1250+
uint8_t *in_buffer_beg = get_buffer_beg(pagesize);
1251+
uint8_t *out_buffer_beg = get_buffer_beg(pagesize);
1252+
uint8_t *in_buffer_end = in_buffer_beg + pagesize;
1253+
uint8_t *out_buffer_end = out_buffer_beg + pagesize;
1254+
#endif
1255+
1256+
const EVP_CIPHER *cipher = EVP_aes_256_xts();
1257+
bssl::ScopedEVP_CIPHER_CTX ctx;
1258+
std::vector<uint8_t> key(EVP_CIPHER_key_length(cipher)), iv(EVP_CIPHER_iv_length(cipher));
1259+
1260+
// Test AESXTS Encrypt and Decrypt with random messages of incremental lenghts
1261+
for (size_t msg_len = 16; msg_len < AESXTS_RAND_MSG_MAX_LEN; msg_len += 1) {
1262+
1263+
std::vector<uint8_t> plaintext(msg_len);
1264+
RAND_bytes(plaintext.data(), msg_len);
1265+
1266+
SCOPED_TRACE(plaintext.size());
1267+
1268+
int len = 0;
1269+
uint8_t *in_p = nullptr, *out_p = nullptr;
1270+
#if defined(OPENSSL_LINUX)
1271+
std::unique_ptr<uint8_t[]> in, out;
1272+
1273+
for (bool beg: {false, true}) {
1274+
if (pagesize < (int)plaintext.size() && !beg) {
1275+
// For small page sizes skip page bound edge cases
1276+
in.reset(new uint8_t[plaintext.size()]);
1277+
out.reset(new uint8_t[plaintext.size()]);
1278+
in_p = in.get();
1279+
out_p = out.get();
1280+
} else if (pagesize < (int)plaintext.size() && beg) {
1281+
// Skip second iteration for small page sizes since it would use
1282+
// identical allocation for |in_p| and |out_p| buffers
1283+
continue;
1284+
} else {
1285+
if (!beg) {
1286+
in_p = in_buffer_end - plaintext.size();
1287+
out_p = out_buffer_end - plaintext.size();
1288+
} else {
1289+
in_p = in_buffer_end - pagesize;
1290+
out_p = out_buffer_end - pagesize;
1291+
}
1292+
OPENSSL_memset(in_p, 0x00, plaintext.size());
1293+
OPENSSL_memset(out_p, 0x00, plaintext.size());
1294+
}
1295+
#else
1296+
std::unique_ptr<uint8_t[]> in(new uint8_t[plaintext.size()]);
1297+
std::unique_ptr<uint8_t[]> out(new uint8_t[plaintext.size()]);
1298+
in_p = in.get();
1299+
out_p = out.get();
1300+
#endif
1301+
for (bool in_place : {false, true}) {
1302+
SCOPED_TRACE(in_place);
1303+
1304+
// Generate random key and iv for each encryption test
1305+
RAND_bytes(key.data(), EVP_CIPHER_key_length(cipher));
1306+
RAND_bytes(iv.data(), EVP_CIPHER_iv_length(cipher));
1307+
1308+
// Test encryption.
1309+
1310+
OPENSSL_memcpy(in_p, plaintext.data(), plaintext.size());
1311+
if (in_place) {
1312+
out_p = in_p;
1313+
}
1314+
1315+
ctx.Reset();
1316+
len = 0;
1317+
ASSERT_TRUE(EVP_EncryptInit_ex(ctx.get(), cipher, nullptr, key.data(),
1318+
iv.data()));
1319+
ASSERT_TRUE(
1320+
EVP_EncryptUpdate(ctx.get(), out_p, &len, in_p, plaintext.size()));
1321+
ASSERT_EQ(static_cast<size_t>(len), plaintext.size());
1322+
1323+
// Test decryption.
1324+
1325+
if (!in_place) {
1326+
OPENSSL_memset(in_p, 0, len);
1327+
}
1328+
1329+
ctx.Reset();
1330+
len = 0;
1331+
ASSERT_TRUE(EVP_DecryptInit_ex(ctx.get(), cipher, nullptr, key.data(),
1332+
iv.data()));
1333+
ASSERT_TRUE(
1334+
EVP_DecryptUpdate(ctx.get(), in_p, &len, out_p, plaintext.size()));
1335+
ASSERT_EQ(static_cast<size_t>(len), plaintext.size());
1336+
EXPECT_EQ(Bytes(plaintext), Bytes(in_p, static_cast<size_t>(len)));
1337+
}
1338+
}
1339+
#if defined(OPENSSL_LINUX)
1340+
}
1341+
free_memory(in_buffer_end, pagesize);
1342+
free_memory(out_buffer_end, pagesize);
1343+
#endif
1344+
}
1345+
12431346
// Negative test for key1 = key2
12441347
TEST(XTSTest, DuplicateKey) {
12451348

0 commit comments

Comments
 (0)