Salsa20

  • Key length = 32 bytes

Salsa20 is a modern and efficient stream symmetric cipher. It was designed in 2005 by Daniel Bernstein, research professor of Computer Science at the University of Illinois at Chicago.

Usage

Salsa20 is a cipher that was submitted to eSTREAM project, running from 2004 to 2008, which was supposed to promote development of stream ciphers. It is considered to be a well-designed and efficient algorithm. There aren't any known and effective attacks on the family of Salsa20 ciphers.

Algorithm

Salsa20 is a stream cipher that works on data blocks of size of 64 bytes.

Encryption

For each 64-byte data block, the algorithm uses the Salsa20 expansion function. The input to the function is the secret key (which can have either 32 or 16 bytes) and an 8-byte long nonce concatenated with an additional block number, which values change from 0 to 264-1 (it is also stored on 8 bytes). Every call to the expansion function increases the block number by one.

The core of Salsa20 encryption algorithm is a hash function which receives the 64-byte long input data from the Salsa20 expansion function, mixes it, and eventually returns the 64-byte long output. The Salsa20 hash function works on the received sequence of bytes, which consists of:

  • the secret key.
  • the nonce with the block number.
  • four constant vectors received from the expansion function, which values depend on the size of the secret key.

The hash function operates on data divided into words. Every word contains 4 bytes and can have values from 0 to 232-1. Therefore, the input data is 16-word long, a key contains 8 or 4 words, and the nonce has 2 words.

The output from the Salsa20 expansion function is added XOR to the 64-byte block of data. The result is a 64-byte block of ciphertext.

Decryption

The same algorithm should be used during decryption. The data should be divided into parts of the same size.

The output from the Salsa20 expansion function should be added XOR to the 64-byte block of ciphertext. The result is a 64-byte block of plaintext.

Other Salsa20 ciphers

There are also some other ciphers, which are based on the Salsa20 algorithm but differ in details.

  • Salsa20/8 and Salsa20/12 - they work exactly as the original Salsa20 algorithm but instead of 10 doublerounds inside the hash function, they perform 4 or respectively 6 doublerounds.
  • ChaCha family - published by Bernstein in 2008. They provide better security than the original Salsa20 cipher, by using slightly better hash functions. The hash function input data was rearranged, to allow to implement the algorithm in a more efficient way.

Block Diagram of Salsa20 Algorithm

Scheme of Salsa20 algorithm
Scheme of Salsa20 algorithm

Maths:

The operations for Salsa20 algorithm are presented in the order from the low-level functions, to the more complex functions, which use the functions described above them.

Sum of two words

The addition of two 4-byte words is denoted in the algorithm description as a + b. The result is divided modulo by 232 (so, by the maximum value that can be stored in one word).

The sum of two words a and b is equal to a+b mod 232. The result is a valid 4-byte long word.

Exclusive-Or of two words

The Exclusive-Or of two 4-byte words is denoted in the algorithm description as a XOR b.

To perform XOR addition, two words are compared bit by bit, and XOR addition is performed for each pair. The result is also a valid proper 4-byte long word.

Binary Left Rotation

The a-bit left rotation of a 4-byte word w is denoted in the algorithm as w <<< a. The leftmost a bits move to the rightmost positions.

A rotation of bits to the left w <<< a of a 4-byte word w may be represented as the multiplication:
2a·w mod(232-1)
The result is a valid 4-byte long word.

Quarterround Function

The Quarterround Function takes 4 words as input and returns another 4-word sequence.

If x is a 4-word input:
    x = (x0, x1, x2, x3)
then the function can be defined as follow:
    quarterround(x) = (y0, y1, y2, y3)
where:
    y1 = x1 XOR ((x0 + x3) <<< 7)
    y2 = x2 XOR ((y1 + x0) <<< 9)
    y3 = x3 XOR ((y2 + y1) <<< 13)
    y0 = x0 XOR ((y3 + y2) <<< 18)

The Quarterround Function can be performed in place, without the need of allocating any additional memory. First, x1 changes to y1, then x2 changes to y2, next x3 changes to y3, then x0 changes to y0. The Quarterround Function is invertible because all the modifications above are invertible.

Rowround Function

The Rowround Function takes 16 words as input, transforms them, and returns 16-word sequence.

This function is very similar to the Columnround Function but it operates on the words in a different order.

If x is a 16-word input:
    x = (x0, x1, x2, ..., x15)
then the function can be defined as follow:
    rowround(x) = (y0, y1, y2, ..., y15)
where:
    (y0, y1, y2, y3) = quarterround(x0, x1, x2, x3)
    (y5, y6, y7, y4) = quarterround(x5, x6, x7, x4)
    (y10, y11, y8, y9) = quarterround(x10, x11, x8, x9)
    (y15, y12, y13, y14) = quarterround(x15, x12, x13, x14)


The 16-word input can be presented as a square matrix:

x0x1x2x3
x4x5x6x7
x8x9x10x11
x12x13x14x15

The rows in the matrix can be changed in parallel. Each of them is modified by the Quarterround Function.

In the first row, the words are changed in the following order:

  1. x1
  2. x2
  3. x3
  4. x0

In the second row, the words are changed in the following order:

  1. x6
  2. x7
  3. x4
  4. x5

In the third row, the words are modified in the order:

  1. x11
  2. x8
  3. x9
  4. x10

Finally, in the last, fourth row, the words are changed in the order:

  1. x12
  2. x13
  3. x14
  4. x15

Columnround Function

The Columnround Function takes 16 words as input and returns 16-word sequence.

This function is very similar to the Rowround Function but operates on the words in different order.

If x is a 16-word input:
    x = (x0, x1, x2, ..., x15)
then the function can be defined as follow:
    columnround(x) = (y0, y1, y2, ..., y15)
where:
    (y0, y4, y8, y12) = quarterround(x0, x4, x8, x12)
    (y5, y9, y13, y1) = quarterround(x5, x9, x13, x1)
    (y10, y14, y2, y6) = quarterround(x10, x14, x2, x6)
    (y15, y3, y7, y11) = quarterround(x15, x3, x7, x11)


The 16-word input can be presented as a square matrix:

x0x1x2x3
x4x5x6x7
x8x9x10x11
x12x13x14x15

The columns in the matrix can be changed in parallel. Each of them is modified by the Quarterround Function.

In the first column, the words are changed in the following order:

  1. x4
  2. x8
  3. x12
  4. x0

In the second column, the words are modified in the following order:

  1. x9
  2. x13
  3. x1
  4. x5

In the third column, the words are changed in the order:

  1. x14
  2. x2
  3. x6
  4. x10

In the last fourth column, the words are modified in the order:

  1. x3
  2. x7
  3. x11
  4. x15

Doubleround Function

The Doubleround Function takes 16 words as input and returns 16-word sequence.

If x is a 16-word input, then the Doubleround Function can be defined as follow:
    doubleround(x) = rowround(columnround(x))

Littleendian Function

The Littleendian Function changes the order of a 4-byte sequence.

If b is a sequence of four bytes:
    b = (b0, b1, b2, b3)
then the function is defined as:
    littleendian(b) = b0 + 28b1 + 216b2 + 224b3

The Littleendian Function is invertible. It just simply changes the order of bytes in a word.

Salsa20 Hash Function

The Salsa20 Hash Function takes 64 bytes as input and returns a 64-byte sequence.

First, the Hash Function creates 16 words from the received 64-byte input. If input is a sequence of 64 bytes:
    input =(b0, b1, b2, ..., b63)
then 16 words are created as below:
    w0 = littleendian(b0, b1, b2, b3)
    w1 = littleendian(b4, b5, b6, b7)
    [...]
    w15 = littleendian(b60, b61, b62, b63)

Then, all 16 words are modified by 10 iterations of the Doubleround Function:
    (x0, x1, ..., x15) = doubleround10(w0, w1, ..., w15)

Finally, the 16 words received as input are added (as described above) to the modified 16 words and changed to 64 new bytes using the Littleendian Function. The bytes are output from the Salsa20 Hash Function:
    output = littleendian-1(x0+w0) + littleendian-1(x1+w1) + ... + littleendian-1(x15+w15)

Salsa20 Expansion Function

The Salsa20 Expansion Function takes two sequences of bytes. The first sequence can have either 16 or 32 bytes and the second sequence (n) is always 16-byte long. The function returns another sequence of 64 bytes.

If the first sequence is 32-bytes long, then it is divided into two shorter sequences of 16 bytes (k0 and k1). The Salsa20 Expansion Function is defined by using the Salsa20 Hash Function, as shown below:
    Salsa20Expansionk0, k1(n) = Salsa20Hash(a0, k0, a1, n, a2, k1, a3)
where:
    a0 = (101, 120, 112, 97)
    a1 = (110, 100, 32, 51)
    a2 = (50, 45, 98, 121)
    a3 = (116, 101, 32, 107)

If the first sequence is 16-bytes long (k), then the Salsa20 Expansion Function is defined by using the Salsa20 Hash Function as below:
    Salsa20Expansionk(n) = Salsa20Hash(b0, k, b1, n, b2, k, b3)
where:
    b0 = (101, 120, 112, 97)
    b1 = (110, 100, 32, 49)
    b2 = (54, 45, 98, 121)
    b3 = (116, 101, 32, 107)

The constant values of the vector (a0, a1, a2, a3) mean 'expand 32-byte k' in ASCII code. Similarly, the constant values of the second vector (b0, b1, b2, b3) mean 'expand 16-byte k' in ASCII.