LCOV - code coverage report
Current view: top level - tests/functional - test_ige_aes_functional.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 100 100
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 11 11

            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 : }
        

Generated by: LCOV version 2.0-1