HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Salsa20 stream cipher implementation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
salsa20implementationstreamcipher

Problem

I have implemented the Salsa20 stream cipher as an ICryptoTransform. It runs fairly fast and has successfully encrypted and decrypted all of my tests. I would mostly like the Salsa20 algorithm to be reviewed (since there are very few reference implementations in C# out there), to make sure I haven't missed anything. I would also like input on my implementation of the ICryptoTransform interface.

```
namespace Salsa20Cipher {
public sealed class Salsa20CryptoTransform : ICryptoTransform {
// The ChaCha20 state
private uint[] state;
private readonly int numRounds;

// Construct a new Salsa20 state.
public Salsa20CryptoTransform(byte[] key, byte[] iv) {
if (key == null) {
throw new ArgumentNullException("key");
}
if (iv == null) {
throw new ArgumentNullException("iv");
}
if (key.Length != 32) {
throw new ArgumentException(
"Key length must be 32 bytes. Actual is " + key.Length.ToString()
);
}
if (iv.Length = inputBuffer.Length) {
throw new ArgumentOutOfRangeException("inputOffset");
}
if (inputCount inputBuffer.Length) {
throw new ArgumentOutOfRangeException("inputCount");
}
if (outputBuffer == null) {
throw new ArgumentNullException("outputBuffer");
}
if (outputOffset outputBuffer.Length) {
throw new ArgumentOutOfRangeException("outputOffset");
}
if (state == null) {
throw new ObjectDisposedException(GetType().Name);
}

byte[] output = new byte[64];
int bytesTransformed = 0;

while (inputCount > 0) {
Salsa20Core(output, state);

state[8] = AddOne(state[8]);
if (state[8] == 0) {
/* Stopping at 2^70 bytes per nonce is the
* user's responsibility
*/
state[9] = AddOne(sta

Solution

This block of code is insane:

tmp[4] ^= Rotate(Add(tmp[0], tmp[12]), 7);
        tmp[8] ^= Rotate(Add(tmp[4], tmp[0]), 9);
        tmp[12] ^= Rotate(Add(tmp[8], tmp[4]), 13);
        tmp[0] ^= Rotate(Add(tmp[12], tmp[8]), 18);
        tmp[9] ^= Rotate(Add(tmp[5], tmp[1]), 7);
        tmp[13] ^= Rotate(Add(tmp[9], tmp[5]), 9);
        tmp[1] ^= Rotate(Add(tmp[13], tmp[9]), 13);
        tmp[5] ^= Rotate(Add(tmp[1], tmp[13]), 18);
        tmp[14] ^= Rotate(Add(tmp[10], tmp[6]), 7);
        tmp[2] ^= Rotate(Add(tmp[14], tmp[10]), 9);
        tmp[6] ^= Rotate(Add(tmp[2], tmp[14]), 13);
        tmp[10] ^= Rotate(Add(tmp[6], tmp[2]), 18);
        tmp[3] ^= Rotate(Add(tmp[15], tmp[11]), 7);
        tmp[7] ^= Rotate(Add(tmp[3], tmp[15]), 9);
        tmp[11] ^= Rotate(Add(tmp[7], tmp[3]), 13);
        tmp[15] ^= Rotate(Add(tmp[11], tmp[7]), 18);
        tmp[1] ^= Rotate(Add(tmp[0], tmp[3]), 7);
        tmp[2] ^= Rotate(Add(tmp[1], tmp[0]), 9);
        tmp[3] ^= Rotate(Add(tmp[2], tmp[1]), 13);
        tmp[0] ^= Rotate(Add(tmp[3], tmp[2]), 18);
        tmp[6] ^= Rotate(Add(tmp[5], tmp[4]), 7);
        tmp[7] ^= Rotate(Add(tmp[6], tmp[5]), 9);
        tmp[4] ^= Rotate(Add(tmp[7], tmp[6]), 13);
        tmp[5] ^= Rotate(Add(tmp[4], tmp[7]), 18);
        tmp[11] ^= Rotate(Add(tmp[10], tmp[9]), 7);
        tmp[8] ^= Rotate(Add(tmp[11], tmp[10]), 9);
        tmp[9] ^= Rotate(Add(tmp[8], tmp[11]), 13);
        tmp[10] ^= Rotate(Add(tmp[9], tmp[8]), 18);
        tmp[12] ^= Rotate(Add(tmp[15], tmp[14]), 7);
        tmp[13] ^= Rotate(Add(tmp[12], tmp[15]), 9);
        tmp[14] ^= Rotate(Add(tmp[13], tmp[12]), 13);
        tmp[15] ^= Rotate(Add(tmp[14], tmp[13]), 18);


Did you actually type it all out by hand? Work smart, not hard. In pseudocode:

for index_quadruple in ( (8, 4, 0, 9), (12, 8, 4, 13) ...) {
     a, b, c, d = index_quadruple
     tmp[a] ^= Rotate(Add(tmp[b], tmp[c]), d);
}


And the code is so much shorter while doing the same.

Code Snippets

tmp[4] ^= Rotate(Add(tmp[0], tmp[12]), 7);
        tmp[8] ^= Rotate(Add(tmp[4], tmp[0]), 9);
        tmp[12] ^= Rotate(Add(tmp[8], tmp[4]), 13);
        tmp[0] ^= Rotate(Add(tmp[12], tmp[8]), 18);
        tmp[9] ^= Rotate(Add(tmp[5], tmp[1]), 7);
        tmp[13] ^= Rotate(Add(tmp[9], tmp[5]), 9);
        tmp[1] ^= Rotate(Add(tmp[13], tmp[9]), 13);
        tmp[5] ^= Rotate(Add(tmp[1], tmp[13]), 18);
        tmp[14] ^= Rotate(Add(tmp[10], tmp[6]), 7);
        tmp[2] ^= Rotate(Add(tmp[14], tmp[10]), 9);
        tmp[6] ^= Rotate(Add(tmp[2], tmp[14]), 13);
        tmp[10] ^= Rotate(Add(tmp[6], tmp[2]), 18);
        tmp[3] ^= Rotate(Add(tmp[15], tmp[11]), 7);
        tmp[7] ^= Rotate(Add(tmp[3], tmp[15]), 9);
        tmp[11] ^= Rotate(Add(tmp[7], tmp[3]), 13);
        tmp[15] ^= Rotate(Add(tmp[11], tmp[7]), 18);
        tmp[1] ^= Rotate(Add(tmp[0], tmp[3]), 7);
        tmp[2] ^= Rotate(Add(tmp[1], tmp[0]), 9);
        tmp[3] ^= Rotate(Add(tmp[2], tmp[1]), 13);
        tmp[0] ^= Rotate(Add(tmp[3], tmp[2]), 18);
        tmp[6] ^= Rotate(Add(tmp[5], tmp[4]), 7);
        tmp[7] ^= Rotate(Add(tmp[6], tmp[5]), 9);
        tmp[4] ^= Rotate(Add(tmp[7], tmp[6]), 13);
        tmp[5] ^= Rotate(Add(tmp[4], tmp[7]), 18);
        tmp[11] ^= Rotate(Add(tmp[10], tmp[9]), 7);
        tmp[8] ^= Rotate(Add(tmp[11], tmp[10]), 9);
        tmp[9] ^= Rotate(Add(tmp[8], tmp[11]), 13);
        tmp[10] ^= Rotate(Add(tmp[9], tmp[8]), 18);
        tmp[12] ^= Rotate(Add(tmp[15], tmp[14]), 7);
        tmp[13] ^= Rotate(Add(tmp[12], tmp[15]), 9);
        tmp[14] ^= Rotate(Add(tmp[13], tmp[12]), 13);
        tmp[15] ^= Rotate(Add(tmp[14], tmp[13]), 18);
for index_quadruple in ( (8, 4, 0, 9), (12, 8, 4, 13) ...) {
     a, b, c, d = index_quadruple
     tmp[a] ^= Rotate(Add(tmp[b], tmp[c]), d);
}

Context

StackExchange Code Review Q#101435, answer score: 3

Revisions (0)

No revisions yet.