Skip to content
/ RSAES Public

A learning experience in writing cryptographic functions. RSA-OAEP and AES managers.

Notifications You must be signed in to change notification settings

Shizcow/RSAES

Repository files navigation

-------------------------------------------------------------------------
                                 WARNING

These implimentations of crypto functions are NOT SECURE and SHOULD NOT
be used in any serious implimentations of anything whatsoever. This code
has been written solely as a learning experience for the writer. One day
far in the future it might come closer to being secure, but that day is
not today. As it stands, there are more vulnerabilities than I can count
or even understand.

Seriously, don't use this.

-------------------------------------------------------------------------

                               DESCRIPTION

This is RSAES, a C++ interface for encryption over RSA and AES-N.

In its current state, this project IS NOT SECURE and should not
be used in production. It is a learning experience on the author's
part, and only might be secure in the distant future.

The intent is for applications which need very simple encryption
in a very small footprint. This library is meant to be portable
above all else, with two options of use: using a pre-compiled
version of GMP linked through lgmp, or through a version of
mini-gmp when <gmp.h> is not available, which is contained herein.

AES-N is a fork of the AES algorithm which has been designed to work with
any key size that is a power of two above 128. For values of 128, 256,
and 512, the algorithm is the same as the reference AES algorithm.
Larger key sizes follow the most obvious pattern in the algorithm, and
are implimented accordingly. I've personally tested key sizes up to 
about 1 Gigabyte with success.

-------------------------------------------------------------------------

                          MAKEFILE INSTRUCTIONS

To begin using RSAES, one must first run `make init` in the RSAES
directory. This will construct RSAES.hpp from RSAES.hpp.proto, which can
be included in desired applications. `make init` has a few options,
which are described as follows:

make init
     Assumes the behavior of make init lib=yes

make init lib=yes
     Assumes GMP is installed on the target system. RSAES.hpp will
     include <gmp.h> and should compiled with the -lgmp flag. If
     GMP is not installed on the target system, use the next option

make init lib=no
     Assumes GMP is not installed on the target system. Instead of
     linking to libgmp, RSAES.hpp will include "mini-gmp/mini-gmp.c"
     and will include the functions and struct definitions needed for
     RSAES to work properly. The advantage here is more portability
     at the cost of performance, as mini-gmp uses a pure C
     implimentation of GMP. This is slower as it does not have assemby
     optimizations, but should be fully cross-platform.

In both cases, simply `#include "RSAES.hpp"` in your project, with
proper directory structure, and everything should work smoothly.

Furthermore, the makefile has a few more abilities, all accessable only
after a `make init`. They are as follows:

make test
     Compiles tests_and_examples.cpp using RSAES.hpp and runs a test
     suite. `make test` assumes g++ to comple, however a specific
     compiler can be used through specifying the CXX flag. As follows is
     an example:
     	make test CXX=clang++
     This would compile tests_and_examples.cpp with clang and run a.out.
     Additionally, CXXFLAGS is recognized and can be passed to the
     makefile in order to specify optimizations or special circumstances.
     Example is as follows:
     	make test CXXFLAGS="-O2 -Wall -Werror"

make debug
     Same as make test, but runs the resulting executable under valgrind
     to check for errors.

make time
     Same as make test, but runs the resulting executable with the
     `time` command

make clean
     Remove all generated files, likely editor files, and executables
     generated by other make options. Should leave the entire repository
     the same as it was when first cloned

-------------------------------------------------------------------------

                           TOP LEVEL INTERFACE

The top level interface is held in the `RSAES` namespace in the class
`EncryptionManager`.


----------EncryptionManager CONSTRUCTORS

EncryptionManager(unsigned int RSAbits)
    Creates an empty EncryptionManager and initilizes public and private
    RSA keys (the size of which is passed as RSAbits). This should be the
    first constructor used when starting 2-way communicatoin.

    At this point, the getPublicKey() method is now available.

EncryptionManager(std::string key, size_t AESbits)
    Creates an EncryptionManager using an RSA public key. This public key
    should come from the getPublicKey() method from the first
    constructor. AESbits is the size of bits in AES-N. See description for
    clarification on AES-N.

    At this point, the getKeyResponse() method is now available.

EncryptionManager(std::string key)
    Creates an EncryptionManager with only an RSA public key. Identical
    in purpose to the above constructor, but chooses the largest
    AES-N keysize that can fit within the RSA key. Gives less control,
    but ensures the most secure possible algorithm given the size of
    the RSA key.
    
    At this point, the getKeyResponse() method is now available.

EncryptionManager()
    Creates an empty EncryptionManager for the express reason of
    unpacking a string into it. See the pack() and unpack() methods

~EncryptionManager()
    The deconstructor is available at any time and should be mentioned
    here. When deconstructed, this object overwrites all its members
    with random bits before freeing them, just in case a malacoius
    program would get a hold of the malloc'd memory. Additionally,
    memory management has been taken care of so that even if an
    EncryptionManager goes out of scope before being fully initilized,
    memory management is still safe and secure.

----------EncryptionManager METHODS

std::string getPublicKey()
    Available immediatly after using the first constructor. This method
    returns the RSA public key, as packed in base64. Once transfered,
    the result should be passed to the second constructor.

std::string getKeyResponse()
    Available immediatly after using the second constructor. This method
    returns the internal AES-N key, encrypted with the RSA key, and packed
    in base64. Once transfered, the result should be passed to the
    following method on the first object.

    At this point, the encrypt() and decrypt() methods are available.

void registerPass(std::string KeyResponse)
    Available immediatly after calling getPublicKey() on an object
    instantiated by the first constructor. This method decrypts and stores
    the shared AES-N key.

    At this point, the encrypt() and decrypt() methods are available.


std::string encrypt(std::string input)
    Available immediatly after calling getKeyResponse() or registerPass()
    on an object. This method takes a string of any length and returns the
    string as encrypted by AES-N, using the internal AES-N key, and packed
    as base64.

std::string decrypt(std::string input)
    Available immediatly after calling getKeyResponse() or registerPass()
    on an object. This method takes a string returned from the encrypt()
    method and returns the decrypted string using the internal AES-N key.

std::string pack()
    Return a base64 encoded string containing a small snippet of the AES
    key. This string can be saved to disk for future sessions when RAM
    is cleared

void unpack(std::string input)
    Takes a string previously returned from pack(), used to initilize
    an EncryptionManager constructed from the default EncryptionManager()
    constructor. After running unpack, both encrypt and decrypt are
    available.

void destroy()
    Free all allocated memory associated with the object. Very similar
    to the deconstructor, but doesn't re-seed the random objects.

-------------------------------------------------------------------------

                          LOW LEVEL INTERFACES

The low level interfaces are held in the RSAES namespace, under one of
the following sub-namespaces: RSA, AES, or UTIL. The low level interface
is not meant to be used directly and thus is not as clean as the top
level interface. However, in the usecases of debugging or highly-
optimized implimentations where the top level interface offers more than
needed, use of the low level interfaces are encouraged.

----------UTIL

The UTILity namespace houses the general utility functions and objects
used all over in the library. They are as follows:

-->OBJECTS
Global objects are as such so that they only needs to be initilized once,
saving on memory usage and improving speed.

std::random_device rd;
       An std::random_device used for better randomization than rand().
std::mt19937 mt(rd());
       The Mersenne Twister algorithm, as initilized by our random device.
       Used for extracting random numbers from the distribution objects.
std::uniform_int_distribution<unsigned short> dist_char(0, 255);
       Used for creating padding at the end of messages.
std::uniform_int_distribution<unsigned short> dist_char_1(1, 255);
       Used for creating an AES key. This is different because bugs.
std::uniform_int_distribution<unsigned long long int>
       dist_short(0, std::numeric_limits<unsigned short>::max());
       Used for clearing vectors, as it's much faster to use shorts when
       possible.
std::uniform_int_distribution<unsigned long>
       dist_r(0, std::numeric_limits<unsigned long>::max());
       Used for generic randomness and seeding the GMP random structs.
const char base64_chars[64];
       Used as a lookup table for base64 en/decoding.

-->FUNCTIONS

inline char find_as_base64(char tofind)
       Used to reverse the lookup base64_chars lookup table during decodng.

std::string base64_encode(unsigned char const* bytes_to_encode,
       		   	         size_t in_len)
       Used to base64 encode some bytes. It doesn't take a string because
       that's how I found it on the internet.

std::string base64_decode(std::string const& encoded_string)
       Used to decode a base64 string. The return value is a string whos
       bytes are not all valid ascii.


----------RSA

The RSA namespace houses everything needed to run and manage the RSA side
of RSAES. It contains various functions and a single class (RSAmanager).

-->FUNCTIONS

std::string packKey(std::pair<mpz_t,mpz_t> key)
       Packs the public `key` and returns valid ascii.

void unpackKey(std::pair<mpz_t,mpz_t> **rop, std::string key)
       Unpacks the public `key` into `rop`.

std::string encrypt(std::string input, std::pair<mpz_t,mpz_t> *key)
       The full RSA encryption algorithm. Encrypts `input` using the public
       `key`.

-->RSAmanager

The RSAmanager is a mid-level interface for dealing with RSA encryption.

>Members
std::pair<mpz_t,mpz_t> public_key;
	Public member which stores the public key.

mpz_t private_key;
        Private member which stores the private key.

gmp_randstate_t r;
        The source of randomness for the object.

>Constructor

RSAmanager(unsigned int bits)
        Takes bits as an argument. Typically 1024, 2048, 4096, or higher.
	Creates the public and private key.

>Destructor

~RSAmanager()
        Fills all used ram with random bits before clearing memory, just
	in case.

>Methods

inline std::string decrypt(std::string msg)
        Full RSA decryption algorithm. Decrypts `msg` using the private key.

All private methods are simply used to help decrypt and instantiate.


----------AES

The AES namespace houses everything needed to run and manage the AES side
of RSAES. Although it is a part of RSAES, it can stand alone as just an
AES implimentation. It contains many functions, varibles and a single
class (AESkey). Only the important ones are shown.

-->FUNCTIONS

std::vector<unsigned char> expand_key(std::vector<unsigned char> in)
       Takes an N-bit key, where N is restricted to 128, 256, or any power
       of two thereafter. Returns an expanded key. This is the KeyExpansion
       step in the reference AES algorithm.

unsigned char small_encrypt(unsigned char *in, AESkey &expanded_key)
       Fully encrypts a 4x4 bytetable. This is the entire reference AES
       algorithm.

unsigned char small_decrypt(unsigned char *in, AESkey &expanded_key)
       Fully decrypts a 4x4 bytetable. This is the reference AES decryption
       algorithm.

std::string big_encrypt(std::string input, AESkey &expanded_key)
       Takes a whole string, padds it, encrypts each table, then wraps
       it up in base64 and returns a passable, encrypted string.

std::string big_decrypt(std::string input, AESkey &expanded_key)
       Takes a whole string, decodes it from base64, decrypts it,
       un-padds it and returns the clean string.

-->AESkey

The AES key class is simply a wrapper for an expanded key with methods to
step over the key and produce round keys.

>Members
size_t base;
        The base size of the key in bytes. If you're using AES-256, this
	value would be 32.
std::vector<unsigned char> expanded_key;
        Holds the value of the expanded key.

>Constructors

AESkey(std::vector<unsigned char> in)
        Creates an expanded key from a given unexpanded key (in the case)
	of a hashed password.

AESkey(size_t _base)
	Creates an expanded key at random, given only by the size of key
	desired.

>Destructor

~AESkey()
	Randomizes all ram before releasing just in case.

>Methods

std::array<unsigned char, 16> getRoundKey(bool B = false)
        Gets the next round key and automatically advances forward
	or backwards.

inline void advanceRound()
        Readys the next round key.

inline void setStart()
        Gets the key ready for front-to-back navigation (encryption).

inline void setEnd()
        Gets the key ready for back-to-fromt navigation (decryption).

inline std::string pack()
        Returns a base64 encoded, shortened version of the internal key
	used for saving to disk.