class ref name: Rijndael_crypt
category-group: security
layer: 2
header file: z_crypt.h
libraries: libz00.lib libz01.lib libz02.lib

synopsis.
Rijndael_crypt_o is considered the strongest encryptor currently offered by the Z Directory, on par with the pair_crypt_o class (eg RSA). This encryptor is more complex than the others. It offers 3 modes of operation:

ECB: Electronic Code Book
CBC: Cipher Block Chaining
CFB: Cipher Feedback Block - used as a "self-synchronising stream cipher"

The various modes allow optimized capability for stream-type encoding. ECB is the most compatible with the Z Directory's approach to encrypting a string, that is, to process a single monolithic block of data, independently of any assumptions of chaining blocks together. Hence, ECB is the object's default mode. Discussion of these modes is beyond the scope of this page; more information is readily accessible in the internet.

You can set a number of parameters, such as password (key) length, block size, and mode (as per above). However, in order to keep compatibility with the behaviour of all encryptors, you can ignore these parameters - defaults are provided when the object is created that will allow you to encrypt-decrypt a block of bytes loaded into a string object in the same way as for the other encryptors.

NOTICE: with this cipher implementation, the number of bytes to encrypt-decypt absolutely, positively must be a multiple of 16 or 32 bytes . Vettrasoft has tried [unsuccessfully] work-arounds, but it turns out that the nature of the algorithm requires a block size set of data of specific length. So it is up to the application to arrange the size of the block supplied to this object (that means you!).

description.
This algorithm was created by Belgian cryptographers Joan Daemen and Vincent Rijmen around 1999. It was selected, in contest style as a winner out of 15 designs, by the National Institute of Science and Technology (NIST) to fill the slot known as the Advanced Encryption Standard (AES). Hence, the terms AES and Rijndael are used interchangeably here (which may lead to a problem in the future if NIST adopts a different design for AES). In 2002, it was formally adopted for general use by the American federal government and is considered to supercede the Data Encryption Standard (DES), which was adopted in 1977.

AES is a symmetric key algorithm - the same key is used for encryption and decryption. Thus, the default usage (simolest case) in the Z Directory is conformant to usage of most of the other encryptors, and is actually easier than RSA ("pair crypt").

For this encryption algorithm, the key size (the number of characters in the password string, passed to set_key()) must be exactly one of these: 16, 24, or 32 bytes. The latter 2 sizes are widely considered to be immune to brute force (eg 'try every password') attacks, whieh are estimated to require billions of years of computer time (actually, more time than the universe is believed to have existed) by today's current technology. Whether other techniques (such as "side-channel" cache-timing", or "related-key" attacks) can crack AES is still being debated.

member functions (primary)

Rijndael_crypt_o()
SIGNATURE: Rijndael_crypt_o ()
SYNOPSIS:
creates a a new encryption object (Rijndael_crypt). mode is set to zcrypto_mode_ECB; key size and block size are undefined.
 

destructor
SIGNATURE: ~Rijndael_crypt_o ()
SYNOPSIS: virtual destructor. The instance settings are reset.
 

key_size()
SIGNATURE: int key_size() const
SYNOPSIS: returns the key size (password length).
RETURNS:
[val > 0] the key size, in bytes. This can be only either 16, 24, or 32.
-1: indicates user has not set a key (password).
 

block_size()
SIGNATURE: int block_size() const
SYNOPSIS:
returns the block size. This parameter is internal and specific to the Rijndael algorithm. It is the number of characters to process (encryption or decryption) per one Rijndael encryption-decryption iteration.
 

num_rounds()
SIGNATURE: int num_rounds() const
SYNOPSIS:
returns the value of "m_iROUNDS" (internal variable). this variable is set inside gen_key() (eg when the password is set). Its possible values are given by this table; rows indicate key size and columns are block size (in bytes):

  16 24 32
16 10 12 14
24 10 14 12
32 14 14 14

TRAITS: an inline function. this function is for specialized, advanced appliecations and is not intended for general use.
 

isvalid_keysize()
SIGNATURE: boolean isvalid_keysize (int n) const
SYNOPSIS:
a simple convenience function. tells if 'n' is a valid key size value (one of 16, 24, or 32). If the password has been set, 'n' must be the length of the current key.
RETURNS:
TRUE: if "n" is a valid key size for this encryption algorithm. If the key has been set earlier, the parameter is evaluated in terms of the object's current settings (eg, whether "n" matches the current key size).
FALSE: otherwise.
 

isvalid_blocksize()
SIGNATURE: boolean isvalid_blocksize (int n) const
SYNOPSIS:
tells if the "n" is a valid block size (Rijndael-specific "internal" value). this can be only one of 16, 24, or 32. If the block size has been set previously, the value of "n" is evaluated in terms of the object's current settings.
Exactly analogous to the member function isvalid_keysize().
RETURNS:
TRUE: if "n" is a valid block size for this encryption algorithm.
FALSE: otherwise.
 

input_chunksize()
SIGNATURE: size_t input_chunksize (int *pi = NULL) const
SYNOPSIS:
returns the value of the input string's "chunk size" that is required by this algorithm (see discuession on security group main page). The size of the input block must be a multiple of this value.
 

output_chunksize()
SIGNATURE: size_t output_chunksize (int *pi = NULL) const
SYNOPSIS:
returns the value of the output string's "chunk size". (see discuession on security group main page). This value indicates whether the encrypted data is the same length as the clear-text input, or expanded. For instance, if "input chunk size" = 16 and "output chunk size" = 32, the resultant encrypted data is twice as long as the input data.
For Rijndael, output chunk size is always the same as the input chunk size.
 

set_mode()
SIGNATURE: int set_mode (enum zCrypto_Mode new_mode = zcrypto_mode_ECB)
SYNOPSIS: sets the internal mode parameter ("my_mode") to the mode given by "new_mode".
RETURNS: 0 (always)
 

set_keysize()
SIGNATURE: int set_keysize (int val, int *pi = NULL)
SYNOPSIS:
sets the object's internal variable "m_keylength" to the value specified by "val", if valid. If this member function is called prior to set_key(), the key length then must match the value of "val".
PARAMETERS

  • val: key size, in bytes. must be one of 16, 24, or 32.
  • pi: [optional] error indicator [output] variable. Values:
    0: operation successful
    zcrypERR_BADKEY: key size invalid
RETURNS:
0 key size successfully set.
-1: error ocurred in attempting to set the key size.
 

set_blocksize()
SIGNATURE: int set_blocksize (int val, int *pi = NULL)
SYNOPSIS:
sets the object's internal variable "m_blockSize" to the value specified by "val" (if valid). See the "usage" section on this page for more details.
PARAMETERS

  • val: block size, in bytes. must be one of 16, 24, or 32.
  • pi: optional error indicator [output] variable. Values:
    0: operation successful
    zcrypERR_BADPARAM: invalid block size
RETURNS:
0 block size successfully set.
-1: error ocurred in attempting to set the block size.
 

set_chain()
SIGNATURE: int set_chain (const string_o &s, int *pi = NULL)
SYNOPSIS:
This sets the "chain" string, as held by 2 variables internal to the object ("m_chain" and "m_chain0") to the value given by "s". The size of variable "s" must be the same as the block size, and up to 32 bytes maximum. The chain string is used only in CBC mode, where each block of data as it is encrypted or decrypted is XOR'd with the contents of the chain-string. Thus, the chain acts as an auxiliary password. If not set, whenever the chain is accessed, a default string is used. Since this default string is the same in all programs using the Z Directory, by allowing the application to provide its own string, it adds a degree of protection. In ECB mode, the chain is not used, and hence, calling this function will not affect ECB encryption.
'set_blocksize()' must be invoked prior to calling this function. Also
 

set_keybg()
SIGNATURE: static int set_keybg (const string_o &s, int *pi)
SYNOPSIS:
This function sets a global character buffer that adds padding to a password, if needed. This concatenation is done inside of gen_key().
PARAMETERS

  • s: a string of [at least] 32 characters. this string can be longer than 32, but any excess will be ignored.
  • pi: error indicator [output] variable. Values:
    0: operation successful
    1: "s" under 32 bytes long
TRAITS:
does not do anything in actauality, since set_key() requires a key of full and correct size. It is included for future expansion.
 

reset_chain()
SIGNATURE: int reset_chain()
SYNOPSIS: sets the "chain" string to null (zeroes) (see set_chain() for more info).
 

set_key()
SIGNATURE: int set_key (const string_o &s, int *pi = NULL)
SYNOPSIS:
sets the password. This function must be invoked prior to any encryption or decryption. If key size, block size, or chain has not been set prior, this function will automatically set them. set_key() calls gen_key() internally, which does a multitude of initialization specific to the Rijndael algorithm: "expands a user-supplied key into a session key, and initializes the chain block".
PARAMETERS

  • s: the password. Must be of length 128 / 192 / 256 bits.
  • pi: [optional] error indicator [output] variable. Values:
    0: operation successful
    zcrypERR_BADKEY: invalid key size
    zcrypERR_BADKEY: failure attempting to set key size
    zcrypERR_NOT_INITTED: key or block size not initialized
    zcrypERR_BADCHAINSIZE: problems with the chain
 

gen_key()
SIGNATURE: int gen_key (const string_o &skey, const string_o &newchain, int keylen = zRij_DEFAULT_BLOCKSIZ, int blocksiz = zRij_DEFAULT_BLOCKSIZ, int *pi = NULL)
SYNOPSIS:
this function sets up the password, chain, and key and block sizes. Callig this function is nearly equivalent to calling set_key(), but if the key is too short, it will be padded (overlaid on top of) with the "background" key (see set_keybg()). Key size and block size must have been explicitly set prior to calling this function.
TRAITS:
this function has been superceded by set_key(), and (although public) will probably be be made private in future releases. Do not use it.
 

encrypt()
SIGNATURE: int encrypt (const string_o &si, string_o &so, size_t *pnb)
SYNOPSIS:
encrypts the string "si". Result is put into output variable "so". This function can operate in the following modes: zcrypto_mode_ECB, zcrypto_mode_CBC or zcrypto_mode_CFB. zcrypto_mode_ECB does not use chaining. If the same block is encrypted twice with the same key, the resulting ciphered blocks are the same. In zcrypto_mode_CBC [CBC mode], a ciphered block is obtained by XORing the plaintext block with the previous ciphertext block, and encrypting the resulting value. In zcrypto_mode_CFB mode, a ciphertext block is obtained by encrypting the previous ciphertext block and XORing the resulting value with the plaintext.
 

decrypt()
SIGNATURE: int decrypt (const string_o &si, string_o &so, size_t *pnb)
SYNOPSIS: decrypts ciphered data in "si". resultant decrypted data is put into "so". The converse of encrypt().
 

encrypt_block()
SIGNATURE: int encrypt_block (const char *bufin, char *bufout)
SYNOPSIS:
Encrypts data in "bufin". Result is put into "bufout". the number of bytes encoded is determined by Rijndael block size. There is no boundary checking of the parameters to make sure they are of sufficient size. This function is called by "encrypt()", and is not really intended to be made available to applications. This can be called directly in the event char * buffers are preferred.
TRAITS: not recommended for public use.
 

decrypt_block()
SIGNATURE: int decrypt_block (const char *bufin, char *bufout)
SYNOPSIS: Decrypts data in "bufin". Result is put into "bufout". the converse of encrypt_block() (which see).
TRAITS: not recommended for public use.
 

clone()
SIGNATURE: crypto_o *clone () const
SYNOPSIS: creates a copy of the current object and returns a pointer to the newly created object.
 

usage.
Generally, there is a specific order to configuring this object for use:

  1. set_keybg() - the purpose of this function is to allow an application to provide a 'background mask' to provide additional characters in the event that the password used in set_key() is too short.

  2. set_keysize() - if you know the size of the key you want to use, you can call this function to set it. Note that this function is not really necessary, as by calling set_key() with a correct number of characters in the password will achieve the same result. However, by using this function you can add an extra degreee of validation checking to make sure that the key length is what is desired.

  3. set_blocksize() - use this when you want to specific control over the Rijndael [internal] block size. The ordering of this function is independent of set_keysize() (you can call this before or after it). Explicit calling of this function is not necessary; set_key() will set the block size to that of the key size, if it is not set.

  4. set_chain() - this sets the "chain" - a type of auxiliary password used by the Rijndael algorithm, used in CFB and CBC modes [only]. This must be done only after set_keysize() and set_blocksize() are both called.

  5. set_key() - this sets the password to be used for encryption and decryption.

  6. encrypt() or decrypt() - encrypt [decrypt] a block of data

note.
Prior to 2017, the default Rijndael encryption mode in the Z Directory was ECB (Electronic Code Book). This was mainly due to the fact that in CBC mode, the decrypted output did not match the input, and so, the object was broken. EBC is inferior to CBC, so the Z Directory Rijndael_crypt_o class was not a strong cipher. This was a serious problem. Six years after this class was introduced to the Z Directory, on 01/13/2017, this was fixed. Now, CBC is the default mode. Use of ECB is discouraged.

examples.
This is a quick example that demonstrates usage of a few Rijndael-specific features.

#include "stdafx.h"
#include "z_crypt.h"

static char *strings32[] =
{
    "Pope Pius IX patents barbed wire",
    "Nikola Tesla did not do the math",
    "where is the Socialist Party now",
    "Salvador Dali museum in Florida.",
    "the first native-born politician",
    NULL
};

static char *some_text =
"Does increased security provide comfort to paranoid people? Or does \
it provide some basic protections that we may think that we don't need?";

void Rijndael_test ();

//----------------------------------------------------------------------
// SYNOPSIS
//      this example encrypts, then decrypts, then compares the original
//      text to the decoded text. it has 2 sections: the first loops
//      thru a set of 5 lines each 32 characters long. the second uses
//      a string that is 161 bytes long - not a multiple of 32. it shows
//      how an application must pad the input, then unpad the output.
//----------------------------------------------------------------------
int main (int argc, char *argv[])
{
    int i, ie0;
    size_t n_blocks, nx, nb = 32;
    string_o salt, sin, si2, sot, so2, see, skey;
    Rijndael_crypt_o x;

    salt = "a human* & a fish: can co-exist!";

    x.set_mode (zcrypto_mode_CBC);      // not needed; CBC is default
    x.set_keysize   (32);               // this is not really necessary
    x.set_blocksize (32);               // neither is this,
    x.set_chain (salt);                 // require: after 'set_blocksize()'

    // the password must be 32 chars long
    x.set_key (">Mary, Mary; she is so contrary!");

    for (i=0; i < 5; i++)
    {
        sin = strings32[i];

        ie0 = x.encrypt (sin, see, &nb);
        if (ie0) { std::cout << "ERROR IN ENCRYPTION!\n"; continue; }

        ie0 = x.decrypt (see, sot, &nb);
        if (ie0) { std::cout << "ERROR IN DECRYPTION!\n"; continue; }

        if (sin == sot)
            std::cout << "TEST PASSED.\n";
        else
            std::cout << "TEST FAILED on loop #" << i << "\n";
    }

    // --- PART 2 ---
    salt = "Where goest thou? -Are you lost?";
    sin = see = sot = "";

    x.reset();
    x.set_blocksize(32);
    x.set_chain(salt);                  // require: after 'set_blocksize()'
    x.set_key ("Joe: so contrary");     // 16 bytes
    sin = si2 = some_text;
    nb = sin.size();                    // 161 bytes (!)
    //..........................................................
    // if we try now to directly encrypt/decrypt 'sin', the
    // decryption will fail. need to pad 'sin' to a multiple
    // of 32 bytes
    //..........................................................
    n_blocks = nb / 32;
    if (nb % 32)
    {
        nx = ((n_blocks+1) * 32) - nb;
        nb += nx;
        for (i=0; i < nx; i++)
            si2 += "P";                 // "P" for pad
    }

    ie0 = x.encrypt (si2, see, &nb);    // note that 'si2' includes pad chars
    ie0 = x.decrypt (see, so2, &nb);    // also, 'so2' has the same pad chars
    sot = so2.substring(0, so2.size() - ++nx);  // need to remove the padding
    if (sin == sot)
        std::cout << "TEST #2: PASSED\n";
    else
        std::cout << "TEST #2: FAILED\n";

    exit(0);
    return 0;
}