Every time I want to write an encryption program, say RSA algorithm. I have to re - invent the wheel and design the key generation and all those small things my self.

Now latly I heard abou AES algorithm and that it is so secure and a lot more stuff. But I cant seem to find a good tutorial on how it works and how to implement it (even a basic implementation) in C++.

So what are cross platform cryptography libraries (if they even exist) that I can use?
- I am using visual C++ 2010 and see there is a Security.h header, is this is a crypt librarie, and where can I find tutorials on it?

Also do you know any AES tutorials, explanation etc. ?

Recommended Answers

All 2 Replies

There are good tutorials out there if you search for them. I started writing a cryptography library a couple of years ago and abandoned it, so all of my supplementary bookmarks and references are gone, unfortunately. However, I can at least offer you a basic implementation. :)

I wrote it using the standard definition, and the test driver uses the test cases from that document. It's as simple as I could make it while maintaining core functionality, but please do keep in mind that this isn't a finished project, just a work in progress. As such the comments are lacking and the design is a first draft.

Here's the test driver:

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include "aes_provider.h"

using namespace encryption;
using namespace std;

#define length(a) (sizeof (a) / sizeof *(a))

void display_bytes(const std::string& message, vector<unsigned char> output)
{
    cout << message;

    for (vector<unsigned char>::size_type i = 0; i < output.size(); i++)
        cout<< setw(3) << hex << left << (unsigned)output[i];

    cout <<'\n';
}

int main()
{
    unsigned char key_init[] = {
        // 128-bit key
        /*
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
        */
        // 192-bit key
        /*
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 
        0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17
        */
        // 256-bit key
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
    };

    unsigned char in_init[] = {
        0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
        0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
    };

    aes_provider aes(vector<unsigned char>(key_init, key_init + length(key_init)));
    vector<unsigned char> input(in_init, in_init + length(in_init));
    vector<unsigned char> output = aes.encrypt(input);

    display_bytes("input:   ", input);
    display_bytes("cipher:  ", output);
    display_bytes("icipher: ", aes.decrypt(output));
}

Here's the header:

#ifndef AES_PROVIDER_H
#define AES_PROVIDER_H

namespace encryption {
    class aes_provider {
    public:
        aes_provider(std::vector<unsigned char> key) { reset(key); }

        std::vector<unsigned char> encrypt(const std::vector<unsigned char>& bytes);
        std::vector<unsigned char> decrypt(const std::vector<unsigned char>& bytes);
    private:
        static unsigned char Sbox[][16];
        static unsigned char Sbox_inverse[][16];
        static unsigned char Rcon[][4];
    private:
        int Nb; // Input block length in 32-bit increments (always 4)
        int Nk; // Key length in 32-bit increments (4, 6, or 8)
        int Nr; // Number of rounds corresponding to key size (4:10, 6:12, 8:14)

        std::vector<unsigned char> key;                // Seed key
        std::vector<std::vector<unsigned char>> w;     // Key schedule
        std::vector<std::vector<unsigned char>> state; // State matrix

        void reset(std::vector<unsigned char> key);
        void key_expansion();
        void add_round_key(int round);
        void sub_bytes(unsigned char Sbox[][16]);
        void shift_rows();
        void inverse_shift_rows();
        void mix_columns();
        void inverse_mix_columns();
        std::vector<unsigned char> sub_word(std::vector<unsigned char> word);
        std::vector<unsigned char> rot_word(std::vector<unsigned char> word);
        unsigned char gfield_mul(unsigned char a, unsigned char b);
    };
}

#endif

And finally the implementation file:

#include <stdexcept>
#include <vector>
#include "aes_provider.h"

namespace encryption {
    std::vector<unsigned char> aes_provider::encrypt(const std::vector<unsigned char>& bytes)
    {
        state = std::vector<std::vector<unsigned char>>(4, std::vector<unsigned char>(Nb));

        // Input to state
        for (int r = 0; r < 4; r++) {
            for (int c = 0; c < Nb; c++)
                state[r][c] = bytes[r + 4 * c];
        }

        add_round_key(0);

        for (int round = 1; round < Nr; round++) {
            sub_bytes(Sbox);
            shift_rows();
            mix_columns();
            add_round_key(round);
        }

        sub_bytes(Sbox);
        shift_rows();
        add_round_key(Nr);

        std::vector<unsigned char> output(4 * Nb);

        // State to output
        for (int r = 0; r < 4; r++) {
            for (int c = 0; c < Nb; c++)
                output[r + 4 * c] = state[r][c];
        }

        return output;
    }

    std::vector<unsigned char> aes_provider::decrypt(const std::vector<unsigned char>& bytes)
    {
        state = std::vector<std::vector<unsigned char>>(4, std::vector<unsigned char>(Nb));

        // Input to state
        for (int r = 0; r < 4; r++) {
            for (int c = 0; c < Nb; c++)
                state[r][c] = bytes[r + 4 * c];
        }

        add_round_key(Nr);

        for (int round = Nr - 1; round > 0; round--) {
            inverse_shift_rows();
            sub_bytes(Sbox_inverse);
            add_round_key(round);
            inverse_mix_columns();
        }

        inverse_shift_rows();
        sub_bytes(Sbox_inverse);
        add_round_key(0);

        std::vector<unsigned char> output(4 * Nb);

        // State to output
        for (int r = 0; r < 4; r++) {
            for (int c = 0; c < Nb; c++)
                output[r + 4 * c] = state[r][c];
        }

        return output;
    }

    unsigned char aes_provider::Sbox[][16] = {
        {0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76},
        {0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0},
        {0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15},
        {0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75},
        {0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84},
        {0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf},
        {0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8},
        {0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2},
        {0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73},
        {0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb},
        {0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79},
        {0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08},
        {0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a},
        {0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e},
        {0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf},
        {0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16} 
    };

    unsigned char aes_provider::Sbox_inverse[][16] = {
        {0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb},
        {0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb},
        {0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e},
        {0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25},
        {0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92},
        {0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84},
        {0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06},
        {0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b},
        {0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73},
        {0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e},
        {0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b},
        {0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4},
        {0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f},
        {0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef},
        {0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61},
        {0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d}
    };

    unsigned char aes_provider::Rcon[][4] = {
        {0x00, 0x00, 0x00, 0x00},  
        {0x01, 0x00, 0x00, 0x00},
        {0x02, 0x00, 0x00, 0x00},
        {0x04, 0x00, 0x00, 0x00},
        {0x08, 0x00, 0x00, 0x00},
        {0x10, 0x00, 0x00, 0x00},
        {0x20, 0x00, 0x00, 0x00},
        {0x40, 0x00, 0x00, 0x00},
        {0x80, 0x00, 0x00, 0x00},
        {0x1b, 0x00, 0x00, 0x00},
        {0x36, 0x00, 0x00, 0x00}
    };

    void aes_provider::reset(std::vector<unsigned char> key)
    {
        Nb = 4;

        switch (key.size()) {
        case 32:
            Nk = 8;
            Nr = 14;
            break;
        case 24:
            Nk = 6;
            Nr = 12;
            break;
        case 16:
            Nk = 4;
            Nr = 10;
            break;
        default:
            throw std::invalid_argument("Not a valid key size");
        }

        this->key = key;
        key_expansion();
    }

    void aes_provider::key_expansion()
    {
        std::vector<unsigned char> temp(4);

        w = std::vector<std::vector<unsigned char>>(Nb * (Nr + 1), std::vector<unsigned char>(4, 0));

        for (int i = 0; i < Nk; i++) {
            for (int byte = 0; byte < 4; byte++)
                w[i][byte] = key[4 * i + byte];
        }

        for (int i = Nk; i < Nb * (Nr + 1); i++) {
            for (int byte = 0; byte < 4; byte++)
                temp[byte] = w[i - 1][byte];

            if (i % Nk == 0) {
                temp = sub_word(rot_word(temp));

                for (int byte = 0; byte < 4; byte++)
                    temp[byte] ^= Rcon[i / Nk][byte];
            }
            else if (Nk > 6 && i % Nk == 4) {
                temp = sub_word(temp);
            }

            for (int byte = 0; byte < 4; byte++)
                w[i][byte] = w[i - Nk][byte] ^ temp[byte];
        }
    }

    void aes_provider::add_round_key(int round)
    {
        for (int r = 0; r < 4; r++) {
            for (int c = 0; c < Nb; c++)
                state[r][c] ^= w[round * 4 + c][r];
        }
    }

    void aes_provider::sub_bytes(std::vector<unsigned char>::value_type Sbox[][16])
    {
        for (int r = 0; r < 4; r++) {
            for (int c = 0; c < Nb; c++)
                state[r][c] = Sbox[state[r][c] >> 4][state[r][c] & 0x0f];
        }
    }

    void aes_provider::shift_rows()
    {
        std::vector<std::vector<unsigned char>> temp = state;

        for (int r = 1; r < 4; r++) {
            for (int c = 0; c < Nb; c++)
                state[r][c] = temp[r][(c + r) % Nb];
        }
    }

    void aes_provider::inverse_shift_rows()
    {
        std::vector<std::vector<unsigned char>> temp = state;

        for (int r = 1; r < 4; r++) {
            for (int c = 0; c < Nb; c++)
                state[r][(c + r) % Nb] = temp[r][c];
        }
    }

    void aes_provider::mix_columns()
    {
        std::vector<std::vector<unsigned char>> temp = state;

        for (int c = 0; c < 4; ++c) {
            state[0][c] = gfield_mul(temp[0][c], 2) ^ gfield_mul(temp[1][c], 3) ^ temp[2][c] ^ temp[3][c];
            state[1][c] = temp[0][c] ^ gfield_mul(temp[1][c], 2) ^ gfield_mul(temp[2][c], 3) ^ temp[3][c];
            state[2][c] = temp[0][c] ^ temp[1][c] ^ gfield_mul(temp[2][c], 2) ^ gfield_mul(temp[3][c], 3);
            state[3][c] = gfield_mul(temp[0][c], 3) ^ temp[1][c] ^ temp[2][c] ^ gfield_mul(temp[3][c], 2);
        }
    }

    void aes_provider::inverse_mix_columns()
    {
        std::vector<std::vector<unsigned char>> temp = state;

        for (int c = 0; c < 4; ++c) {
            state[0][c] = 
                gfield_mul(temp[0][c], 0x0e) ^ gfield_mul(temp[1][c], 0x0b) ^ 
                gfield_mul(temp[2][c], 0x0d) ^ gfield_mul(temp[3][c], 0x09);
            state[1][c] = 
                gfield_mul(temp[0][c], 0x09) ^ gfield_mul(temp[1][c], 0x0e) ^ 
                gfield_mul(temp[2][c], 0x0b) ^ gfield_mul(temp[3][c], 0x0d);
            state[2][c] = 
                gfield_mul(temp[0][c], 0x0d) ^ gfield_mul(temp[1][c], 0x09) ^ 
                gfield_mul(temp[2][c], 0x0e) ^ gfield_mul(temp[3][c], 0x0b);
            state[3][c] =
                gfield_mul(temp[0][c], 0x0b) ^ gfield_mul(temp[1][c], 0x0d) ^ 
                gfield_mul(temp[2][c], 0x09) ^ gfield_mul(temp[3][c], 0x0e);
        }
    }

    std::vector<unsigned char> aes_provider::sub_word(std::vector<unsigned char> word)
    {
        std::vector<unsigned char> result(4);

        result[0] = Sbox[word[0] >> 4][word[0] & 0x0f];
        result[1] = Sbox[word[1] >> 4][word[1] & 0x0f];
        result[2] = Sbox[word[2] >> 4][word[2] & 0x0f];
        result[3] = Sbox[word[3] >> 4][word[3] & 0x0f];

        return result;
    }

    std::vector<unsigned char> aes_provider::rot_word(std::vector<unsigned char> word)
    {
        std::vector<unsigned char> result(4);

        result[0] = word[1];
        result[1] = word[2];
        result[2] = word[3];
        result[3] = word[0];

        return result;
    }

    unsigned char aes_provider::gfield_mul(unsigned char a, unsigned char b)
    {
        unsigned char prod = 0;

        for (int i = 0; i < 8; i++) {
            if ((b & 1) == 1)
                prod ^= a;

            bool is_set = (a & 0x80) == 0x80;

            a <<= 1;

            if (is_set)
                a ^= 0x1b;

            b >>= 1;
        }

        return prod;
    }
}

Thank you so much for that. It is hard to learn these things when you are self tought.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.