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.