Lably
Skip to content

Salsa20

Stream ciphers

  • 18 min read
  • 4 chapters

Related on Lably

Through Bitwise operation, Modular arithmetic and Google

Both ciphers are built on a pseudorandom function based on add–rotate–XOR (ARX) operations — 32-bit addition, bitwise addition (XOR) and rotation operations. The core function maps a 256-bit key, a 64-bit nonce, and a 64-bit counter to a 512-bit block of the key stream (a Salsa version with a 128-bit key also exists). This gives Salsa20 and ChaCha the unusual advantage that the user can efficiently seek to any position in the key stream in constant time. Salsa20 offers speeds of around 4–14 cycles per byte in software on modern x86 processors, and reasonable hardware performance. It is not patented, and Bernstein has written several public domain implementations optimized for common architectures.

Structure

Internally, the cipher uses bitwise addition ⊕ (exclusive OR), 32-bit addition mod 232 ⊞, and constant-distance rotation operations <<< on an internal state of sixteen 32-bit words. Using only add-rotate-xor operations avoids the possibility of timing attacks in software implementations. The internal state is made of sixteen 32-bit words arranged as a 4×4 matrix.

The initial state is made up of eight words of key ( ), two words of stream position ( ), two words of nonce (essentially additional stream position bits) ( ), and four fixed words ( ):

The constant words spell "expand 32-byte k" in ASCII (i.e. the 4 words are "expa", "nd 3", "2-by", and "te k") as a nothing-up-my-sleeve number. The core operation in Salsa20 is the quarter-round QR(a, b, c, d) that takes a four-word input and produces a four-word output:

b ^= (a + d) <<< 7; c ^= (b + a) <<< 9; d ^= (c + b) <<< 13; a ^= (d + c) <<< 18;

Odd-numbered rounds apply QR(a, b, c, d) to each of the four columns in the 4×4 matrix, and even-numbered rounds apply it to each of the four rows. Two consecutive rounds (column-round and row-round) together are called a double-round:

// Odd round QR( 0, 4, 8, 12) // column 1 QR( 5, 9, 13, 1) // column 2 QR(10, 14, 2, 6) // column 3 QR(15, 3, 7, 11) // column 4 // Even round QR( 0, 1, 2, 3) // row 1 QR( 5, 6, 7, 4) // row 2 QR(10, 11, 8, 9) // row 3 QR(15, 12, 13, 14) // row 4

An implementation in C/C++ appears below.

<span id="mwfQ"></span><span class="cp" id="mwfg">#include</span><span class="w" id="mwfw"> </span><span class="cpf" id="mwgA"><stdint.h></span> <span class="cp" id="mwgQ">#define ROTL(a,b) (((a) << (b)) | ((a) >> (32 - (b))))</span> <span class="cp" id="mwgg">#define QR(a, b, c, d)( \</span> <span class="cp" id="mwgw"> b ^= ROTL(a + d, 7), \</span> <span class="cp" id="mwhA"> c ^= ROTL(b + a, 9), \</span> <span class="cp" id="mwhQ"> d ^= ROTL(c + b,13), \</span> <span class="cp" id="mwhg"> a ^= ROTL(d + c,18))</span> <span class="cp" id="mwhw">#define ROUNDS 20</span> <span class="kt" id="mwiA">void</span><span class="w" id="mwiQ"> </span><span class="nf" id="mwig">salsa20_block</span><span class="p" id="mwiw">(</span><span class="kt" id="mwjA">uint32_t</span><span class="w" id="mwjQ"> </span><span class="n" id="mwjg">out</span><span class="p" id="mwjw">[</span><span class="mi" id="mwkA">16</span><span class="p" id="mwkQ">],</span><span class="w" id="mwkg"> </span><span class="kt" id="mwkw">uint32_t</span><span class="w" id="mwlA"> </span><span class="k" id="mwlQ">const</span><span class="w" id="mwlg"> </span><span class="n" id="mwlw">in</span><span class="p" id="mwmA">[</span><span class="mi" id="mwmQ">16</span><span class="p" id="mwmg">])</span> <span class="p" id="mwmw">{</span> <span class="w" id="mwnA"> </span><span class="kt" id="mwnQ">int</span><span class="w" id="mwng"> </span><span class="n" id="mwnw">i</span><span class="p" id="mwoA">;</span> <span class="w" id="mwoQ"> </span><span class="kt" id="mwog">uint32_t</span><span class="w" id="mwow"> </span><span class="n" id="mwpA">x</span><span class="p" id="mwpQ">[</span><span class="mi" id="mwpg">16</span><span class="p" id="mwpw">];</span> <span class="w" id="mwqA"> </span><span class="k" id="mwqQ">for</span><span class="w" id="mwqg"> </span><span class="p" id="mwqw">(</span><span class="n" id="mwrA">i</span><span class="w" id="mwrQ"> </span><span class="o" id="mwrg">=</span><span class="w" id="mwrw"> </span><span class="mi" id="mwsA">0</span><span class="p" id="mwsQ">;</span><span class="w" id="mwsg"> </span><span class="n" id="mwsw">i</span><span class="w" id="mwtA"> </span><span class="o" id="mwtQ"><</span><span class="w" id="mwtg"> </span><span class="mi" id="mwtw">16</span><span class="p" id="mwuA">;</span><span class="w" id="mwuQ"> </span><span class="o" id="mwug">++</span><span class="n" id="mwuw">i</span><span class="p" id="mwvA">)</span> <span class="w" id="mwvQ"> </span><span class="n" id="mwvg">x</span><span class="p" id="mwvw">[</span><span class="n" id="mwwA">i</span><span class="p" id="mwwQ">]</span><span class="w" id="mwwg"> </span><span class="o" id="mwww">=</span><span class="w" id="mwxA"> </span><span class="n" id="mwxQ">in</span><span class="p" id="mwxg">[</span><span class="n" id="mwxw">i</span><span class="p" id="mwyA">];</span> <span class="w" id="mwyQ"> </span><span class="c1" id="mwyg">// 10 loops × 2 rounds/loop = 20 rounds</span> <span class="w" id="mwyw"> </span><span class="k" id="mwzA">for</span><span class="w" id="mwzQ"> </span><span class="p" id="mwzg">(</span><span class="n" id="mwzw">i</span><span class="w" id="mw0A"> </span><span class="o" id="mw0Q">=</span><span class="w" id="mw0g"> </span><span class="mi" id="mw0w">0</span><span class="p" id="mw1A">;</span><span class="w" id="mw1Q"> </span><span class="n" id="mw1g">i</span><span class="w" id="mw1w"> </span><span class="o" id="mw2A"><</span><span class="w" id="mw2Q"> </span><span class="n" id="mw2g">ROUNDS</span><span class="p" id="mw2w">;</span><span class="w" id="mw3A"> </span><span class="n" id="mw3Q">i</span><span class="w" id="mw3g"> </span><span class="o" id="mw3w">+=</span><span class="w" id="mw4A"> </span><span class="mi" id="mw4Q">2</span><span class="p" id="mw4g">)</span><span class="w" id="mw4w"> </span><span class="p" id="mw5A">{</span> <span class="w" id="mw5Q"> </span><span class="c1" id="mw5g">// Odd round</span> <span class="w" id="mw5w"> </span><span class="n" id="mw6A">QR</span><span class="p" id="mw6Q">(</span><span class="n" id="mw6g">x</span><span class="p" id="mw6w">[</span><span class="w" id="mw7A"> </span><span class="mi" id="mw7Q">0</span><span class="p" id="mw7g">],</span><span class="w" id="mw7w"> </span><span class="n" id="mw8A">x</span><span class="p" id="mw8Q">[</span><span class="w" id="mw8g"> </span><span class="mi" id="mw8w">4</span><span class="p" id="mw9A">],</span><span class="w" id="mw9Q"> </span><span class="n" id="mw9g">x</span><span class="p" id="mw9w">[</span><span class="w" id="mw-A"> </span><span class="mi" id="mw-Q">8</span><span class="p" id="mw-g">],</span><span class="w" id="mw-w"> </span><span class="n" id="mw_A">x</span><span class="p" id="mw_Q">[</span><span class="mi" id="mw_g">12</span><span class="p" id="mw_w">]);</span><span class="w" id="mwAQA"> </span><span class="c1" id="mwAQE">// column 1</span> <span class="w" id="mwAQI"> </span><span class="n" id="mwAQM">QR</span><span class="p" id="mwAQQ">(</span><span class="n" id="mwAQU">x</span><span class="p" id="mwAQY">[</span><span class="w" id="mwAQc"> </span><span class="mi" id="mwAQg">5</span><span class="p" id="mwAQk">],</span><span class="w" id="mwAQo"> </span><span class="n" id="mwAQs">x</span><span class="p" id="mwAQw">[</span><span class="w" id="mwAQ0"> </span><span class="mi" id="mwAQ4">9</span><span class="p" id="mwAQ8">],</span><span class="w" id="mwARA"> </span><span class="n" id="mwARE">x</span><span class="p" id="mwARI">[</span><span class="mi" id="mwARM">13</span><span class="p" id="mwARQ">],</span><span class="w" id="mwARU"> </span><span class="n" id="mwARY">x</span><span class="p" id="mwARc">[</span><span class="w" id="mwARg"> </span><span class="mi" id="mwARk">1</span><span class="p" id="mwARo">]);</span><span class="w" id="mwARs"> </span><span class="c1" id="mwARw">// column 2</span> <span class="w" id="mwAR0"> </span><span class="n" id="mwAR4">QR</span><span class="p" id="mwAR8">(</span><span class="n" id="mwASA">x</span><span class="p" id="mwASE">[</span><span class="mi" id="mwASI">10</span><span class="p" id="mwASM">],</span><span class="w" id="mwASQ"> </span><span class="n" id="mwASU">x</span><span class="p" id="mwASY">[</span><span class="mi" id="mwASc">14</span><span class="p" id="mwASg">],</span><span class="w" id="mwASk"> </span><span class="n" id="mwASo">x</span><span class="p" id="mwASs">[</span><span class="w" id="mwASw"> </span><span class="mi" id="mwAS0">2</span><span class="p" id="mwAS4">],</span><span class="w" id="mwAS8"> </span><span class="n" id="mwATA">x</span><span class="p" id="mwATE">[</span><span class="w" id="mwATI"> </span><span class="mi" id="mwATM">6</span><span class="p" id="mwATQ">]);</span><span class="w" id="mwATU"> </span><span class="c1" id="mwATY">// column 3</span> <span class="w" id="mwATc"> </span><span class="n" id="mwATg">QR</span><span class="p" id="mwATk">(</span><span class="n" id="mwATo">x</span><span class="p" id="mwATs">[</span><span class="mi" id="mwATw">15</span><span class="p" id="mwAT0">],</span><span class="w" id="mwAT4"> </span><span class="n" id="mwAT8">x</span><span class="p" id="mwAUA">[</span><span class="w" id="mwAUE"> </span><span class="mi" id="mwAUI">3</span><span class="p" id="mwAUM">],</span><span class="w" id="mwAUQ"> </span><span class="n" id="mwAUU">x</span><span class="p" id="mwAUY">[</span><span class="w" id="mwAUc"> </span><span class="mi" id="mwAUg">7</span><span class="p" id="mwAUk">],</span><span class="w" id="mwAUo"> </span><span class="n" id="mwAUs">x</span><span class="p" id="mwAUw">[</span><span class="mi" id="mwAU0">11</span><span class="p" id="mwAU4">]);</span><span class="w" id="mwAU8"> </span><span class="c1" id="mwAVA">// column 4</span> <span class="w" id="mwAVE"> </span><span class="c1" id="mwAVI">// Even round</span> <span class="w" id="mwAVM"> </span><span class="n" id="mwAVQ">QR</span><span class="p" id="mwAVU">(</span><span class="n" id="mwAVY">x</span><span class="p" id="mwAVc">[</span><span class="w" id="mwAVg"> </span><span class="mi" id="mwAVk">0</span><span class="p" id="mwAVo">],</span><span class="w" id="mwAVs"> </span><span class="n" id="mwAVw">x</span><span class="p" id="mwAV0">[</span><span class="w" id="mwAV4"> </span><span class="mi" id="mwAV8">1</span><span class="p" id="mwAWA">],</span><span class="w" id="mwAWE"> </span><span class="n" id="mwAWI">x</span><span class="p" id="mwAWM">[</span><span class="w" id="mwAWQ"> </span><span class="mi" id="mwAWU">2</span><span class="p" id="mwAWY">],</span><span class="w" id="mwAWc"> </span><span class="n" id="mwAWg">x</span><span class="p" id="mwAWk">[</span><span class="w" id="mwAWo"> </span><span class="mi" id="mwAWs">3</span><span class="p" id="mwAWw">]);</span><span class="w" id="mwAW0"> </span><span class="c1" id="mwAW4">// row 1</span> <span class="w" id="mwAW8"> </span><span class="n" id="mwAXA">QR</span><span class="p" id="mwAXE">(</span><span class="n" id="mwAXI">x</span><span class="p" id="mwAXM">[</span><span class="w" id="mwAXQ"> </span><span class="mi" id="mwAXU">5</span><span class="p" id="mwAXY">],</span><span class="w" id="mwAXc"> </span><span class="n" id="mwAXg">x</span><span class="p" id="mwAXk">[</span><span class="w" id="mwAXo"> </span><span class="mi" id="mwAXs">6</span><span class="p" id="mwAXw">],</span><span class="w" id="mwAX0"> </span><span class="n" id="mwAX4">x</span><span class="p" id="mwAX8">[</span><span class="w" id="mwAYA"> </span><span class="mi" id="mwAYE">7</span><span class="p" id="mwAYI">],</span><span class="w" id="mwAYM"> </span><span class="n" id="mwAYQ">x</span><span class="p" id="mwAYU">[</span><span class="w" id="mwAYY"> </span><span class="mi" id="mwAYc">4</span><span class="p" id="mwAYg">]);</span><span class="w" id="mwAYk"> </span><span class="c1" id="mwAYo">// row 2</span> <span class="w" id="mwAYs"> </span><span class="n" id="mwAYw">QR</span><span class="p" id="mwAY0">(</span><span class="n" id="mwAY4">x</span><span class="p" id="mwAY8">[</span><span class="mi" id="mwAZA">10</span><span class="p" id="mwAZE">],</span><span class="w" id="mwAZI"> </span><span class="n" id="mwAZM">x</span><span class="p" id="mwAZQ">[</span><span class="mi" id="mwAZU">11</span><span class="p" id="mwAZY">],</span><span class="w" id="mwAZc"> </span><span class="n" id="mwAZg">x</span><span class="p" id="mwAZk">[</span><span class="w" id="mwAZo"> </span><span class="mi" id="mwAZs">8</span><span class="p" id="mwAZw">],</span><span class="w" id="mwAZ0"> </span><span class="n" id="mwAZ4">x</span><span class="p" id="mwAZ8">[</span><span class="w" id="mwAaA"> </span><span class="mi" id="mwAaE">9</span><span class="p" id="mwAaI">]);</span><span class="w" id="mwAaM"> </span><span class="c1" id="mwAaQ">// row 3</span> <span class="w" id="mwAaU"> </span><span class="n" id="mwAaY">QR</span><span class="p" id="mwAac">(</span><span class="n" id="mwAag">x</span><span class="p" id="mwAak">[</span><span class="mi" id="mwAao">15</span><span class="p" id="mwAas">],</span><span class="w" id="mwAaw"> </span><span class="n" id="mwAa0">x</span><span class="p" id="mwAa4">[</span><span class="mi" id="mwAa8">12</span><span class="p" id="mwAbA">],</span><span class="w" id="mwAbE"> </span><span class="n" id="mwAbI">x</span><span class="p" id="mwAbM">[</span><span class="mi" id="mwAbQ">13</span><span class="p" id="mwAbU">],</span><span class="w" id="mwAbY"> </span><span class="n" id="mwAbc">x</span><span class="p" id="mwAbg">[</span><span class="mi" id="mwAbk">14</span><span class="p" id="mwAbo">]);</span><span class="w" id="mwAbs"> </span><span class="c1" id="mwAbw">// row 4</span> <span class="w" id="mwAb0"> </span><span class="p" id="mwAb4">}</span> <span class="w" id="mwAb8"> </span><span class="k" id="mwAcA">for</span><span class="w" id="mwAcE"> </span><span class="p" id="mwAcI">(</span><span class="n" id="mwAcM">i</span><span class="w" id="mwAcQ"> </span><span class="o" id="mwAcU">=</span><span class="w" id="mwAcY"> </span><span class="mi" id="mwAcc">0</span><span class="p" id="mwAcg">;</span><span class="w" id="mwAck"> </span><span class="n" id="mwAco">i</span><span class="w" id="mwAcs"> </span><span class="o" id="mwAcw"><</span><span class="w" id="mwAc0"> </span><span class="mi" id="mwAc4">16</span><span class="p" id="mwAc8">;</span><span class="w" id="mwAdA"> </span><span class="o" id="mwAdE">++</span><span class="n" id="mwAdI">i</span><span class="p" id="mwAdM">)</span> <span class="w" id="mwAdQ"> </span><span class="n" id="mwAdU">out</span><span class="p" id="mwAdY">[</span><span class="n" id="mwAdc">i</span><span class="p" id="mwAdg">]</span><span class="w" id="mwAdk"> </span><span class="o" id="mwAdo">=</span><span class="w" id="mwAds"> </span><span class="n" id="mwAdw">x</span><span class="p" id="mwAd0">[</span><span class="n" id="mwAd4">i</span><span class="p" id="mwAd8">]</span><span class="w" id="mwAeA"> </span><span class="o" id="mwAeE">+</span><span class="w" id="mwAeI"> </span><span class="n" id="mwAeM">in</span><span class="p" id="mwAeQ">[</span><span class="n" id="mwAeU">i</span><span class="p" id="mwAeY">];</span> <span class="p" id="mwAec">}</span>

In the last line, the mixed array is added, word by word, to the original array to obtain its 64-byte key stream block. This is important because the mixing rounds on their own are invertible. In other words, applying the reverse operations would produce the original 4×4 matrix, including the key. Adding the mixed array to the original makes it impossible to recover the input. (This same technique is widely used in hash functions from MD4 through SHA-2.)

Salsa20 performs 20 rounds of mixing on its input. However, reduced-round variants Salsa20/8 and Salsa20/12 using 8 and 12 rounds respectively have also been introduced. These variants were introduced to complement the original Salsa20, not to replace it, and perform better in the eSTREAM benchmarks than Salsa20, though with a correspondingly lower security margin.

XSalsa20 with 192-bit nonce

In 2008, Bernstein proposed a variant of Salsa20 with 192-bit nonces called XSalsa20. XSalsa20 is provably secure if Salsa20 is secure, but is more suitable for applications where longer nonces are desired. XSalsa20 feeds the key and the first 128 bits of the nonce into one block of Salsa20 (without the final addition, which may either be omitted, or subtracted after a standard Salsa20 block), and uses 256 bits of the output as the key for standard Salsa20 using the last 64 bits of the nonce and the stream position. Specifically, the 256 bits of output used are those corresponding to the non-secret portions of the input: indexes 0, 5, 10, 15, 6, 7, 8 and 9.

Sign in to keep reading

3 more chapters are free with a Lably account.

More in Computing and communication