More Ciphers

1

TForge 0.71 is released.

What’s new:

  • TCipher.Salsa20.SetNonce bug fixed
  • added 3DES block cipher algorithm
  • added ChaCha20 stream cipher algorithm

Examples of creating new cipher instances:

var
  Cipher: TCipher;

begin
  Cipher:= TCipher.TripleDES;   // 3DES
  Cipher:= TCipher.ChaCha20;    // 20-rounds ChaCha
  Cipher:= TCipher.ChaCha20(12) // 12-rounds ChaCha

ChaCha20 (aka ChaCha) is Salsa20 variant which is becoming popular after Google has selected ChaCha20 as a replacement for RC4 in communication protocols.

Salsa20 support explained

10

Salsa20 is a stream cipher; like any stream cipher Salsa20 encrypts/decrypts data by xor‘ing a plaintext/ciphertext with a pseudorandom keystream.

Salsa20 generates keystream by hashing 64-byte (512-bit) blocks; the block consists of 4 parts:

  • fixed “magic words” – 16 bytes
  • key – 32 bytes
  • nonce (message number) – 8 bytes
  • block number – 8 bytes

This simple structure allows to generate 64-byte blocks of keystream independently; to generate an arbitrary block of the keystream one need to set the block number in the cipher’s state and hash the 64-byte block.

TCipher class supports Salsa20 design by following methods:

function TCipher.SetIV(const AIV: ByteArray): TCipher;
function TCipher.SetNonce(Value: UInt64): TCipher;
function TCipher.Skip(Value: UInt64): TCipher;

TCipher class considers concatenated nonce and block number as initialization vector (IV). The TCipher.SetIV sets both the nonce and the block number in the cipher instance state. [Corrected – changed in ver 0.71]

The TCipher.SetIV  and TCipher.SetNonce methods do the same – set the nonce field in the the cipher instance state and clear the block number field; the difference is that TCipher.SetIV accepts the parameter as a byte array while TCipher.SetNonce as an integer.

The TCipher.SetIV method was introduced mainly for testing purposes.

The purpose of nonce is to serve as a unique message number; a nonce need not be kept secret, but it should never repeat during the lifetime of a secret key.

As an example suppose we want to encrypt several files by a single secret key; the right way to do it is to encrypt each file with a unique nonce value, like this:

procedure EncryptFiles(const Key: ByteArray; FileNames: array of string);
var
  Cipher: TCipher;
  Nonce: UInt64;
  I: Integer;

begin
  Nonce:= 0;
  for I:= 0 to High(FileNames) do begin
    Cipher:= TCipher.Salsa20;
    Inc(Nonce);
    try
      Cipher.ExpandKey(Key)
            .SetNonce(Nonce)
            .EncryptFile(FileNames[I], FileNames[I] + '.salsa20');
    finally
      Cipher.Burn;
    end;
  end;
end;

The try .. finally block ensures that the sensitive key data in the cipher instances’ states is destroyed.

The TCipher.Skip method increments the block number field in the cipher instance state; its purpose is to implement parallel encryption/decryption as was demonstrated in the previous post.