Skip to content
A very small and compact inline implementation of the SHA256 with CBC algorithm.
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.gitignore
CBC.hpp
Constants.hpp
LICENSE
Math.hpp
README.md
RoundKey.hpp
State.hpp
SubstitutionBox.hpp
Word.hpp

README.md

Inline AES 256 CBC Implementation

This is a special implementation of the AES 256 / CBC crypto algorithm. The goal of this implementation is neither speed nor size. It is written be be inlined into existing code.

The compiler will create a new instance of the algorithm on the fly, at the place where it is inserted into the code. Depending in the used optimization flags of the compiler, this inlined code can be very compact.

Goals

  • Completely inline.
  • Generated by the compiler on the fly.
  • Easy to read and understand.
  • Suitable for embedded processors.
  • No static tables (S-Box) in memory or the binary.
  • Minimal dependencies (just stdint and cstddef headers).
  • Compatible with other AES 256 / CBC implementation.

Non-Goals

  • Speed
  • Size
  • Multiple algorithms
  • Error checking of inputs

Requirements

Everything this implementation needs, is a C++ compiler which supports C++17 and a working C++ standard library for the same language version.

Compilance

This algorithm was tested (but not certified) using all applicable NIST test vectors. As you get this implementation in pure code form, feel free to do your own verification.

Usage

The usage is very simple, as illustrated in the following code example:

#include "../InlineSHA256/CBC.hpp"

#include <iostream>
#include <iomanip>
#include <string>


using namespace lrcrypto;


void hexBlock(const uint8_t *data, size_t size) {
    for (size_t i = 0; i < size; ++i) {
        std::cout << std::internal << std::setw(2) << std::setfill('0')
            << std::hex << static_cast<int>(data[i]);
        std::cout << " ";
    }
    std::cout << std::endl;
}


int main(int argc, const char * argv[]) {
	// Do _not_ store your key in a real application!
    const uint8_t key[aes256::cKeySizeBytes] = {
        0x4e, 0x89, 0x5a, 0xb7, 0xaf, 0x3d, 0xf6, 0xcf,
        0x6d, 0x22, 0x86, 0x01, 0x32, 0x0a, 0x8a, 0xa7,
        0x7b, 0x61, 0x2d, 0xdc, 0x92, 0xe0, 0xc9, 0x8a,
        0xd2, 0x43, 0x43, 0xb9, 0x35, 0x57, 0xad, 0x80
    };
    // Do _not_ use a static IV in a real application!
    const uint8_t iv[aes256::cBlockSizeBytes] = {
        0x44, 0xa9, 0x48, 0x66, 0x3d, 0xb9, 0x92, 0xb8,
        0x2b, 0xe7, 0xd5, 0xed, 0xf0, 0x6d, 0x71, 0x4d
    };
    // The buffer size has to be a multiple of the block size
    // which is 16 bytes.
    const int bufferSize = (aes256::cBlockSizeBytes * 4);
    uint8_t buffer[bufferSize];
    std::memset(buffer, 0, bufferSize);
    std::strcpy(reinterpret_cast<char*>(buffer), "Hello World!");
    std::cout << "Original: " << buffer << std::endl;
    hexBlock(buffer, bufferSize);
    aes256::SubstitutionBox box;
    box.initialize();
    aes256::encryptDataCBC(box, buffer, bufferSize, key, iv);
    std::cout << "Encrypted..." << std::endl;
    hexBlock(buffer, bufferSize);
    aes256::decryptDataCBC(box, buffer, bufferSize, key, iv);
    std::cout << "Decrypted: " << buffer << std::endl;
    hexBlock(buffer, bufferSize);
    return 0;
}

For encrypting and decrypting, you need a working S-Box:

aes256::SubstitutionBox box;
box.initialize();

Creating the data for this S-Box takes some time. Therefore you are free to create this S-Box once at the start of your code and reuse it for all encrypt and descrypt calls.

#include <mutex>
// ...
static aes256::SubstitutionBox box;
static std::once_flag boxOnce;
std::call_once(boxOnce, []{
    box.initialize();
});

To encrypt data, you need a 256bit (32byte) key and a 16 byte initialization vector.

const uint8_t key[aes256::cKeySizeBytes] = {
    0x4e, 0x89, 0x5a, 0xb7, 0xaf, 0x3d, 0xf6, 0xcf,
    0x6d, 0x22, 0x86, 0x01, 0x32, 0x0a, 0x8a, 0xa7,
    0x7b, 0x61, 0x2d, 0xdc, 0x92, 0xe0, 0xc9, 0x8a,
    0xd2, 0x43, 0x43, 0xb9, 0x35, 0x57, 0xad, 0x80
};
const uint8_t iv[aes256::cBlockSizeBytes] = {
    0x44, 0xa9, 0x48, 0x66, 0x3d, 0xb9, 0x92, 0xb8,
    0x2b, 0xe7, 0xd5, 0xed, 0xf0, 0x6d, 0x71, 0x4d
};

The data is encrypted calling encryptDataCBC:

aes256::encryptDataCBC(box, buffer, bufferSize, key, iv);

To descrypt the data, call decryptDataCBC:

aes256::decryptDataCBC(box, buffer, bufferSize, key, iv);

Just in case you have no experience with encryption: While the key for the encryption shall stay secret, best is to use a random initialization vector for each encrypted buffer and store it with the buffer. It is like the salt portion used for hashed passwords. Reusing the same initialization vector, or even use a static one - as shown in the example - very bad practice.

FAQ

Why another implementation of the AES/CBC algorithm?

Most implementations focus on speed or size, but I required one which can be inlined and does not require static tables.

Why inline the whole algorithm?

  1. Better Optimization: Compilers these days are beasts. If you feed them well defined code, they will produce incredible smart machine code for the desired architecture.
  2. Code Hardening: If you embed any kind of protection into your software, you may want to increase the required effort for circumventing it.
  3. Curiosity: It is very interesting to see how compiler optimize this code for various settings.

Why do I have to pass the SubstitutionBox as argument?

Most AES algorithms focusing on speed have a built-in precalculated S-Box and reverse S-Box which requires 512 bytes of memory. There may be situations where you either do not like to store a fixed byte sequence in your binary to keep it as small as possible, or you do not even have the reqired space to store it.

This implementation creates the S-Box on the fly in volatile memory, where you can either keep it or delete it after use. Using an own object for the substition box makes this implementation extremely flexible. You can replace it with an own implementation, using a static table from any source or memory you like.

This flexibility is especially helpful for embedded systems, where you either save flash memory or RAM depending on your implementation.

License

This implementation is using a MIT license. A short and simple permissive license with conditions only requiring preservation of copyright and license notices. Licensed works, modifications, and larger works may be distributed under different terms and without source code.

You can’t perform that action at this time.