Line data Source code
1 : /**
2 : * @file test_ige_aes_functional.c
3 : * @brief Functional (real crypto) tests for AES-256-IGE.
4 : *
5 : * Tests link against real crypto.c (OpenSSL) rather than the mock, so
6 : * these catch bugs that mock passthrough would hide.
7 : *
8 : * Test strategy:
9 : * 1. Round-trip: encrypt → decrypt → verify recovered plaintext.
10 : * 2. OpenSSL cross-check: manually implement IGE using raw OpenSSL AES ECB
11 : * and compare against aes_ige_encrypt. If they differ, our
12 : * crypto_aes_encrypt_block wrapper is broken.
13 : * 3. Error-propagation: flip a byte in ciphertext, verify decrypted output
14 : * differs (IGE forward error propagation).
15 : * 4. Different key / IV / length combinations.
16 : */
17 :
18 : #include "test_helpers.h"
19 : #include "ige_aes.h"
20 :
21 : /* Reference implementation uses OpenSSL's low-level AES API (deprecated in
22 : * OpenSSL 3.0 but still functional). Suppress deprecation warnings only for
23 : * this reference block — production code uses crypto.h wrappers instead. */
24 : #pragma GCC diagnostic push
25 : #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
26 : #include <openssl/aes.h>
27 : #pragma GCC diagnostic pop
28 :
29 : #include <string.h>
30 : #include <stdint.h>
31 : #include <stddef.h>
32 :
33 : /* ---- Reference IGE using raw OpenSSL (cross-check) ---- */
34 :
35 : #pragma GCC diagnostic push
36 : #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
37 :
38 4 : static void ref_ige_encrypt(const uint8_t *plain, size_t len,
39 : const uint8_t *key, const uint8_t *iv,
40 : uint8_t *cipher) {
41 : AES_KEY sched;
42 4 : AES_set_encrypt_key(key, 256, &sched);
43 :
44 : uint8_t iv_c[16], iv_p[16];
45 4 : memcpy(iv_c, iv, 16);
46 4 : memcpy(iv_p, iv + 16, 16);
47 :
48 16 : for (size_t off = 0; off < len; off += 16) {
49 : uint8_t buf[16];
50 204 : for (int j = 0; j < 16; j++)
51 192 : buf[j] = plain[off + j] ^ iv_c[j];
52 12 : AES_encrypt(buf, cipher + off, &sched);
53 204 : for (int j = 0; j < 16; j++)
54 192 : cipher[off + j] ^= iv_p[j];
55 12 : memcpy(iv_c, cipher + off, 16);
56 12 : memcpy(iv_p, plain + off, 16);
57 : }
58 4 : }
59 :
60 2 : static void ref_ige_decrypt(const uint8_t *cipher, size_t len,
61 : const uint8_t *key, const uint8_t *iv,
62 : uint8_t *plain) {
63 : AES_KEY sched;
64 2 : AES_set_decrypt_key(key, 256, &sched);
65 :
66 : uint8_t iv_c[16], iv_p[16];
67 2 : memcpy(iv_c, iv, 16);
68 2 : memcpy(iv_p, iv + 16, 16);
69 :
70 10 : for (size_t off = 0; off < len; off += 16) {
71 : uint8_t buf[16];
72 136 : for (int j = 0; j < 16; j++)
73 128 : buf[j] = cipher[off + j] ^ iv_p[j];
74 8 : AES_decrypt(buf, plain + off, &sched);
75 136 : for (int j = 0; j < 16; j++)
76 128 : plain[off + j] ^= iv_c[j];
77 8 : memcpy(iv_p, plain + off, 16);
78 8 : memcpy(iv_c, cipher + off, 16);
79 : }
80 2 : }
81 :
82 : #pragma GCC diagnostic pop
83 :
84 : /* ---- Helper: all-zero buffers of given size ---- */
85 42 : static void fill_pattern(uint8_t *buf, size_t len, uint8_t pattern) {
86 1642 : for (size_t i = 0; i < len; i++) buf[i] = (uint8_t)(pattern + i);
87 42 : }
88 :
89 : /* ---- Test: single-block round-trip ---- */
90 2 : static void test_ige_single_block_roundtrip(void) {
91 : uint8_t key[32], iv[32], plain[16], cipher[16], recovered[16];
92 2 : fill_pattern(key, 32, 0x01);
93 2 : fill_pattern(iv, 32, 0xAA);
94 2 : fill_pattern(plain, 16, 0x55);
95 :
96 2 : aes_ige_encrypt(plain, 16, key, iv, cipher);
97 2 : aes_ige_decrypt(cipher, 16, key, iv, recovered);
98 :
99 2 : ASSERT(memcmp(plain, recovered, 16) == 0,
100 : "single-block: decrypt(encrypt(p)) != p");
101 2 : ASSERT(memcmp(plain, cipher, 16) != 0,
102 : "single-block: ciphertext must differ from plaintext");
103 : }
104 :
105 : /* ---- Test: multi-block round-trip ---- */
106 2 : static void test_ige_multi_block_roundtrip(void) {
107 : uint8_t key[32], iv[32], plain[128], cipher[128], recovered[128];
108 2 : fill_pattern(key, 32, 0x13);
109 2 : fill_pattern(iv, 32, 0x7F);
110 2 : fill_pattern(plain, 128, 0x00);
111 :
112 2 : aes_ige_encrypt(plain, 128, key, iv, cipher);
113 2 : aes_ige_decrypt(cipher, 128, key, iv, recovered);
114 :
115 2 : ASSERT(memcmp(plain, recovered, 128) == 0,
116 : "multi-block: decrypt(encrypt(p)) != p");
117 : }
118 :
119 : /* ---- Test: cross-check against raw OpenSSL ---- */
120 2 : static void test_ige_cross_check_openssl(void) {
121 : uint8_t key[32], iv[32], plain[64];
122 2 : fill_pattern(key, 32, 0xDE);
123 2 : fill_pattern(iv, 32, 0xAD);
124 2 : fill_pattern(plain, 64, 0xBE);
125 :
126 : uint8_t cipher_ours[64], cipher_ref[64];
127 2 : aes_ige_encrypt(plain, 64, key, iv, cipher_ours);
128 2 : ref_ige_encrypt(plain, 64, key, iv, cipher_ref);
129 :
130 2 : ASSERT(memcmp(cipher_ours, cipher_ref, 64) == 0,
131 : "encrypt: our IGE differs from reference OpenSSL IGE");
132 :
133 : uint8_t plain_ours[64], plain_ref[64];
134 2 : aes_ige_decrypt(cipher_ours, 64, key, iv, plain_ours);
135 2 : ref_ige_decrypt(cipher_ref, 64, key, iv, plain_ref);
136 :
137 2 : ASSERT(memcmp(plain_ours, plain_ref, 64) == 0,
138 : "decrypt: our IGE differs from reference OpenSSL IGE");
139 2 : ASSERT(memcmp(plain_ours, plain, 64) == 0,
140 : "decrypt: recovered plaintext differs from original");
141 : }
142 :
143 : /* ---- Test: all-zero key and IV ---- */
144 2 : static void test_ige_zero_key_iv(void) {
145 2 : uint8_t key[32] = {0}, iv[32] = {0};
146 : uint8_t plain[32], cipher[32], cipher_ref[32], recovered[32];
147 2 : fill_pattern(plain, 32, 0xAB);
148 :
149 2 : aes_ige_encrypt(plain, 32, key, iv, cipher);
150 2 : ref_ige_encrypt(plain, 32, key, iv, cipher_ref);
151 :
152 2 : ASSERT(memcmp(cipher, cipher_ref, 32) == 0,
153 : "zero key/iv: our IGE differs from reference");
154 :
155 2 : aes_ige_decrypt(cipher, 32, key, iv, recovered);
156 2 : ASSERT(memcmp(plain, recovered, 32) == 0,
157 : "zero key/iv: round-trip failed");
158 : }
159 :
160 : /* ---- Test: error propagation (corrupt ciphertext affects 2 blocks) ---- */
161 2 : static void test_ige_error_propagation(void) {
162 : uint8_t key[32], iv[32], plain[48], cipher[48], recovered[48];
163 2 : fill_pattern(key, 32, 0x11);
164 2 : fill_pattern(iv, 32, 0x22);
165 2 : fill_pattern(plain, 48, 0x33);
166 :
167 2 : aes_ige_encrypt(plain, 48, key, iv, cipher);
168 :
169 : /* Corrupt byte 0 in block 1 (offset 16) */
170 : uint8_t corrupted[48];
171 2 : memcpy(corrupted, cipher, 48);
172 2 : corrupted[16] ^= 0xFF;
173 :
174 2 : aes_ige_decrypt(corrupted, 48, key, iv, recovered);
175 :
176 : /* Block 1 and block 2 should differ from original (IGE propagates) */
177 2 : ASSERT(memcmp(plain + 16, recovered + 16, 16) != 0,
178 : "error propagation: block 1 should differ after corruption");
179 2 : ASSERT(memcmp(plain + 32, recovered + 32, 16) != 0,
180 : "error propagation: block 2 should differ after corruption");
181 : }
182 :
183 : /* ---- Test: different key changes ciphertext ---- */
184 2 : static void test_ige_key_sensitivity(void) {
185 : uint8_t key1[32], key2[32], iv[32], plain[32];
186 : uint8_t cipher1[32], cipher2[32];
187 2 : fill_pattern(key1, 32, 0x01);
188 2 : fill_pattern(key2, 32, 0x01);
189 2 : key2[0] ^= 0x01; /* one bit difference */
190 2 : fill_pattern(iv, 32, 0xFF);
191 2 : fill_pattern(plain, 32, 0x00);
192 :
193 2 : aes_ige_encrypt(plain, 32, key1, iv, cipher1);
194 2 : aes_ige_encrypt(plain, 32, key2, iv, cipher2);
195 :
196 2 : ASSERT(memcmp(cipher1, cipher2, 32) != 0,
197 : "key sensitivity: single key bit change should change ciphertext");
198 : }
199 :
200 : /* ---- Test: IV sensitivity ---- */
201 2 : static void test_ige_iv_sensitivity(void) {
202 : uint8_t key[32], iv1[32], iv2[32], plain[32];
203 : uint8_t cipher1[32], cipher2[32];
204 2 : fill_pattern(key, 32, 0xAA);
205 2 : fill_pattern(iv1, 32, 0x11);
206 2 : fill_pattern(iv2, 32, 0x11);
207 2 : iv2[0] ^= 0x01;
208 2 : fill_pattern(plain, 32, 0x55);
209 :
210 2 : aes_ige_encrypt(plain, 32, key, iv1, cipher1);
211 2 : aes_ige_encrypt(plain, 32, key, iv2, cipher2);
212 :
213 2 : ASSERT(memcmp(cipher1, cipher2, 32) != 0,
214 : "IV sensitivity: single IV bit change should change ciphertext");
215 : }
216 :
217 2 : void run_ige_aes_functional_tests(void) {
218 2 : RUN_TEST(test_ige_single_block_roundtrip);
219 2 : RUN_TEST(test_ige_multi_block_roundtrip);
220 2 : RUN_TEST(test_ige_cross_check_openssl);
221 2 : RUN_TEST(test_ige_zero_key_iv);
222 2 : RUN_TEST(test_ige_error_propagation);
223 2 : RUN_TEST(test_ige_key_sensitivity);
224 2 : RUN_TEST(test_ige_iv_sensitivity);
225 2 : }
|