// This file is part of the Advanced Encryption Standard (AES) Tutorial.
// Copyright (C) 2005 - 2008 CodePlanet http://www.codeplanet.eu/                     
//
// This 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.
//
// It 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, see <http://www.gnu.org/licenses/>.

#include <stdio.h>
#include <stdint.h>     // for uint8_t


/**
 * Galois field GF(2^8): Print log and exponentiation table. 
 */
void write_tables(uint8_t* logtable, uint8_t* exptable, FILE *fp)
{
    unsigned int i, c;

    fprintf(fp, "Generator: %2.2x\n\n", exptable[1]);

    fprintf(fp, "Log table:\n");
    fprintf(fp, "   | 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\n");
    fprintf(fp, "---|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|");

    for(i = 0, c = 0; i < 256; i++) {
        if(!(i % 16)) {
            fprintf(fp, "\n%x0 |", c);
            ++c;
        }
        fprintf(fp, "%2.2x ", logtable[i]); 
    }

    fprintf(fp, "\n\nExponentiation table:\n");
    fprintf(fp, "   | 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f\n");
    fprintf(fp, "---|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|--|");

    for(i = 0, c = 0; i < 256; i++) {
        if(!(i % 16)) {
            fprintf(fp, "\n%x0 |", c);
            ++c;
        }
        fprintf(fp, "%2.2x ", exptable[i]); 
    }

    fprintf(fp, "\n\n");
}

/**
 * Multiply two numbers in the GF(2^8) finite field defined 
 * by the polynomial x^8 + x^4 + x^3 + x + 1 
 */
uint8_t galois_multiplication(uint8_t a, uint8_t b) 
{
    uint8_t p = 0;
    uint8_t counter;
    uint8_t hi_bit_set;
    for (counter = 0; counter < 8; counter++) {
        if ((b & 1) == 1) 
            p ^= a;
        hi_bit_set = (a & 0x80);    // xtime()
        a <<= 1;
        if (hi_bit_set == 0x80) 
            a ^= 0x1b;
        b >>= 1;
    }
    return p;
}

int main(int argc, char **argv)
{
    static char filename[] = "rijndael-tables.txt";
    FILE *fp = NULL;
    uint8_t logtable[256];  // Log table
    uint8_t exptable[256];  // Exp table
    unsigned int i, j;

    uint8_t generators[128] = {   
        3,   5,   6,   9,  11,  14,  17,  18,  19,  20,  23,  24, 
       25,  26,  28,  30,  31,  33,  34,  35,  39,  40,  42,  44,  
       48,  49,  60,  62,  63,  65,  69,  70,  71,  72,  73,  75, 
       76,  78,  79,  82,  84,  86,  87,  88,  89,  90,  91,  95, 
      100, 101, 104, 105, 109, 110, 112, 113, 118, 119, 121, 122,
      123, 126, 129, 132, 134, 135, 136, 138, 142, 143, 144, 147, 
      152, 153, 155, 157, 160, 164, 165, 166, 149, 150, 167, 169,  
      170, 172, 173, 178, 180, 183, 184, 185, 186, 190, 191, 192, 
      193, 196, 200, 201, 206, 207, 208, 214, 215, 218, 220, 221, 
      222, 226, 227, 229, 230, 231, 233, 234, 235, 238, 240, 241, 
      244, 245, 246, 248, 251, 253, 254, 255 };

    // Open file
    if((fp = fopen(filename, "w")) == NULL) {
        printf("Error opening %s", filename);
        return 1;
    }

    for (i = 0; i < 128; i++) {
        exptable[0] = 0x01;
        logtable[0] = 0x00;
        
        // Calculate tables
        for (j = 1; j < 256; j++) {
            exptable[j] = galois_multiplication(exptable[j-1], generators[i]);            
            logtable[exptable[j]] = j;  // The value in exptable[j] is the actual offset        
        }

        write_tables(logtable, exptable, fp);   // Write the 2 tables
    }

    fclose(fp); // Close file

    return 0;
}