TCipher

Implements secret-key encryption and decryption; declared in tfCiphers unit.


Class Methods

Method Description
GetInstance Creates TCipher instance implementing a given algorithm
GetRC5 Creates TCipher instance implementing customized RC5 algorithm
GetSalsa Creates TCipher instance implementing customized Salsa algorithm
GetChaCha Creates TCipher instance implementing customized ChaCha algorithm

Instance Methods

Method Description
AddAuthData Adds additional authentication data before encryption or decryption
Burn Clears sensitive data stored in TCipher instance
Clone Clones TCipher instance
Decrypt Decrypts memory data
DecryptBlock Decrypts block of data
DecryptByteArray Decrypts data stored in ByteArray
DecryptStream Decrypts a stream of data
DecryptFile Decrypts a file
Free Uninstantiates TCipher variable
Encrypt Encrypts arbitrary memory data
EncryptBlock Encrypts a block of data
EncryptByteArray Encrypts a ByteArray
EncryptStream Encrypts a stream of data
EncryptFile Encrypts a file
ExpandKey Expands a secret key into a form used by an algorithm
IsAssigned Checks if a TCipher variable is instantiated
Apply Encrypts/decrypts arbitrary memory data by a keystream
KeyStream Generates a given number of keystream bytes
SetIV Sets an IV value in a TCipher instance state
SetNonce Sets a nonce value in a TCipher instance state
Skip Discards a given number of keystream blocks

Backend engines

TCipher is frontend class supported by two backend engines:

Using external backend has its limitations: some TCipher methods requiring access to the internal cipher instance structures can't be implemented by an external backend, and some algorithms are not supported by OpenSSL; TCipher throws exception if you try to use them, but many methods and algorithms are supported by OpenSSL and in most cases it is enough.

To use OpenSSL backend you should:

For example to use built-in backend, AES algorithm and CTR mode of operation:

    var
      Cipher: TCipher;

    begin
      Cipher:= TCipher.GetInstance(AES_CTR);
      ...

Using OpenSSL backend:

    var
      Cipher: TCipher;

    begin
      Cipher:= TCipher.GetInstance(ENGINE_OSSL or AES_CTR);
      ...

That is all; now Cipher methods are implemented by OpenSSL backend.


Coding paradigms

TCipher supports two coding paradigms:

Lets consider an example: encrypt file using AES algorithm in GCM mode of operation and obtain authentication tag:

    uses
      tfArrays, tfCiphers;

    function EncryptGCM(const FileName: string; const Key, IV: ByteArray): ByteArray;
    begin
      Result:= TCipher.GetInstance(AES_GCM_ENCRYPT)
                      .Init(Key, IV)
                      .AddAuthData(ByteArray.FromText('Encrypted in GCM mode'))
                      .EncryptFile(FileName, FileName + '.aes_gcm')
                      .GetAuthTag(16);
    end;
    uses
      tfArrays, tfCiphers;

    function EncryptGCM(const FileName: string; const Key, IV: ByteArray): ByteArray;
    var
      Cipher: TCipher;

    begin
      Cipher:= TCipher.GetInstance(AES_GCM_ENCRYPT);
      try
        Cipher.Init(Key, IV);
        Cipher.AddAuthData(ByteArray.FromText('Encrypted in GCM mode'));
        Cipher.EncryptFile(FileName, FileName + '.aes_gcm');
        Result:= Cipher.GetAuthTag(16);
      finally
        Cipher.Burn;
//        Cipher.Free;
      end;
    end;

The fluent coding is outright gorgeous. TCipher implements automatic memory management and there is no need to call TCipher.Free method to deallocate memory used by TCipher instance; fluent coding leverages automatic memory management.

TCipher.Free method does not guaranty that the memory is freed: it decrements the instance's reference count and frees memory (having cleared the sensitive information first) if the reference count reaches zero; there is little or no reason to call TCipher.Free manually.

The traditional coding has its pros too: the TCipher.Burn method called in finally section clears sensitive information stored in Cipher instance.

You can take the best from both worlds, and the recommended code is:

    uses
      tfArrays, tfCiphers;

    function EncryptGCM(const FileName: string; const Key, IV: ByteArray): ByteArray;
    var
      Cipher: TCipher;

    begin
      Cipher:= TCipher.GetInstance(AES_GCM_ENCRYPT);
      try
        Result:= Cipher.Init(Key, IV)
                       .AddAuthData(ByteArray.FromText('Encrypted in GCM mode'))
                       .EncryptFile(FileName, FileName + '.aes_gcm')
                       .GetAuthTag(16);
      finally
        Cipher.Burn;
      end;
    end;

Modes of operation

Mode of operation is an algorithm that uses block cipher algorithm to encrypt and decrypt data of arbitrary length; a block cipher algorithm encrypts and decrypts blocks of fixed length (called cipher block size) only.

TCipher supports the following modes of operation:

Mode Description
ECB Electronic Code Book
CBC Cipher Block Chaining
CFB Cipher Feedback
OFB Output Feedback
CTR Counter
GCM Galois Counter Mode

ECB mode of operation is deterministic (encryption with the same key always produces the same ciphertext from the same plaintext) and not recommended to use, other modes of operation are indeterministic (provided the initialization vectors are generated correctly).

ECB and CBC modes of operation encrypt plaintext by blocks (of cipher block size length) and require padding to encrypt plaintext of arbitrary length; other modes of operation encrypt plaintext by bytes and don't need padding.

GCM is authenticated mode of operation and supported for 128-bit block ciphers only.

For detailed description of block cipher modes of operation see Wikipedia.


Nonces

Nonce is "number used once". Nonce can be considered as non-secret part of a key. If you encrypt multiple messages, each message must be encrypted with a unique key, but not necessarily unique secret key. In many cases it is allowed to reuse secret key provided each message is encrypted with unique nonce, and nonce need not be kept secret. Nonces can be deterministic (generated by a counter or a LFSR) or random; deterministic nonces never repeat by design, for random nonces the probability of generating two identical nonces during the lifetime of a secret key must be negligible.

TForge defines

    type TNonce = UInt64;

and TCipher supports 64-bit nonces for 128-bit block cipher algorithms and Salsa and ChaCha stream cipher algorithms in Init, EncryptInit and DecryptInit methods' overloads. Salsa and ChaCha support 64-bit nonces by design, for 128-bit block ciphers in CBC, CFB, OFB and CTR modes of operation nonce is implemented as the left half of 128-bit IV, and for 128-bit block ciphers in GCM mode of operation nonce is bits [32..95] of 96-bit IV.

In many cases it is more convenient to use nonces than to use initialization vectors. Consider encryption of multiple messages with AES block cipher algorithm in CTR mode of operation with the same secret key. Let the first message is encrypted with zero initialization vector, and the message size is 10000 bytes; 10000 bytes are contained in 625=0x271 16-byte AES blocks, and it is user responsibility not to reuse initialization vectors in the range from (hexadecimal)

    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

to

    00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 70

otherwise you end up with double-time pad and encryption is completely broken. Using nonces you can simply generate deterministic nonces by a counter or a LFSR, and there is no such problem; the only limitation here is that a message size should not exceed 264 blocks and the number of messages should not exceed 264, but you should not encrypt more than 264 blocks with an ideal 128-bit block cipher using the same secret key by security considerations anyway. For AES it is reasonable to change secret key after encrypting about 240 blocks or less.


Paddings

ECB and CBC modes of operation require that the size of input data to be encrypted is multiple of the cipher block size. Padding extends plaintext so that the total size of the data to be encrypted is multiple of the cipher block size; when the ciphertext is decrypted, the padded block is decoded and the original plaintext size is restored.

TCipher supports padding for ECB and CBC modes of operation only; the other modes of operation supported by TCipher do not need padding and you should not specify padding for them.

The default padding is PKCS; if padding is not specified for ECB or CBC modes of operation, the PKCS padding applies.

PKCS and ISO are the only types of padding defined in acting official standards; they must be implemented identically in all cryptography libraries. The NONE padding is also implemented identically because it can't be implemented otherwise. The other paddings are implementation dependent.

OpenSSL backend supports NONE and PKCS paddings only.

Padding is specified in TCipher.GetInstance method's call, for example

    var
      Cipher: TCipher;

    begin
      Cipher:= TCipher.GetInstance(AES_CBC_ENCRYPT or PADDING_ISO);
      ...

The following types of padding are supported:

    ... | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 00 |
    ... | DD DD DD DD DD DD DD DD | DD DD DD DD 00 00 00 04 |
    ... | DD DD DD DD DD DD DD DD | DD DD DD DD 04 04 04 04 |
    ... | DD DD DD DD DD DD DD DD | DD DD DD DD 80 00 00 00 |