Skip to content

Instantly share code, notes, and snippets.

@avalko
Created October 1, 2017 10:08
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save avalko/180bdcc3824537eb0932ad3b3c8d970e to your computer and use it in GitHub Desktop.
Save avalko/180bdcc3824537eb0932ad3b3c8d970e to your computer and use it in GitHub Desktop.
Salsa20
/*
* This implementation of Salsa20 is ported from the reference implementation
* by D. J. Bernstein, which can be found at:
* http://cr.yp.to/snuffle/salsa20/ref/salsa20.c
*
* This work is hereby released into the Public Domain. To view a copy of the public domain dedication,
* visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
* Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
*/
using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
namespace Logos.Utility.Security.Cryptography
{
/// <summary>
/// Implements the Salsa20 stream encryption cipher, as defined at http://cr.yp.to/snuffle.html.
/// </summary>
/// <remarks>See <a href="http://code.logos.com/blog/2008/06/salsa20_implementation_in_c_1.html">Salsa20 Implementation in C#</a>.</remarks>
public sealed class Salsa20 : SymmetricAlgorithm
{
/// <summary>
/// Initializes a new instance of the <see cref="Salsa20"/> class.
/// </summary>
/// <exception cref="CryptographicException">The implementation of the class derived from the symmetric algorithm is not valid.</exception>
public Salsa20()
{
// set legal values
LegalBlockSizesValue = new[] { new KeySizes(512, 512, 0) };
LegalKeySizesValue = new[] { new KeySizes(128, 256, 128) };
// set default values
BlockSizeValue = 512;
KeySizeValue = 256;
m_rounds = 20;
}
/// <summary>
/// Creates a symmetric decryptor object with the specified <see cref="SymmetricAlgorithm.Key"/> property
/// and initialization vector (<see cref="SymmetricAlgorithm.IV"/>).
/// </summary>
/// <param name="rgbKey">The secret key to use for the symmetric algorithm.</param>
/// <param name="rgbIV">The initialization vector to use for the symmetric algorithm.</param>
/// <returns>A symmetric decryptor object.</returns>
public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV)
{
// decryption and encryption are symmetrical
return CreateEncryptor(rgbKey, rgbIV);
}
/// <summary>
/// Creates a symmetric encryptor object with the specified <see cref="SymmetricAlgorithm.Key"/> property
/// and initialization vector (<see cref="SymmetricAlgorithm.IV"/>).
/// </summary>
/// <param name="rgbKey">The secret key to use for the symmetric algorithm.</param>
/// <param name="rgbIV">The initialization vector to use for the symmetric algorithm.</param>
/// <returns>A symmetric encryptor object.</returns>
public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV)
{
if (rgbKey == null)
throw new ArgumentNullException("rgbKey");
if (!ValidKeySize(rgbKey.Length * 8))
throw new CryptographicException("Invalid key size; it must be 128 or 256 bits.");
CheckValidIV(rgbIV, "rgbIV");
return new Salsa20CryptoTransform(rgbKey, rgbIV, m_rounds);
}
/// <summary>
/// Generates a random initialization vector (<see cref="SymmetricAlgorithm.IV"/>) to use for the algorithm.
/// </summary>
public override void GenerateIV()
{
// generate a random 8-byte IV
IVValue = GetRandomBytes(8);
}
/// <summary>
/// Generates a random key (<see cref="SymmetricAlgorithm.Key"/>) to use for the algorithm.
/// </summary>
public override void GenerateKey()
{
// generate a random key
KeyValue = GetRandomBytes(KeySize / 8);
}
/// <summary>
/// Gets or sets the initialization vector (<see cref="SymmetricAlgorithm.IV"/>) for the symmetric algorithm.
/// </summary>
/// <value>The initialization vector.</value>
/// <exception cref="ArgumentNullException">An attempt was made to set the initialization vector to null. </exception>
/// <exception cref="CryptographicException">An attempt was made to set the initialization vector to an invalid size. </exception>
public override byte[] IV
{
get
{
return base.IV;
}
set
{
CheckValidIV(value, "value");
IVValue = (byte[]) value.Clone();
}
}
/// <summary>
/// Gets or sets the number of rounds used by the Salsa20 algorithm.
/// </summary>
/// <value>The number of rounds.</value>
public int Rounds
{
get
{
return m_rounds;
}
set
{
if (value != 8 && value != 12 && value != 20)
throw new ArgumentOutOfRangeException("value", "The number of rounds must be 8, 12, or 20.");
m_rounds = value;
}
}
// Verifies that iv is a legal value for a Salsa20 IV.
private static void CheckValidIV(byte[] iv, string paramName)
{
if (iv == null)
throw new ArgumentNullException(paramName);
if (iv.Length != 8)
throw new CryptographicException("Invalid IV size; it must be 8 bytes.");
}
// Returns a new byte array containing the specified number of random bytes.
private static byte[] GetRandomBytes(int byteCount)
{
byte[] bytes = new byte[byteCount];
using (RandomNumberGenerator rng = new RNGCryptoServiceProvider())
rng.GetBytes(bytes);
return bytes;
}
int m_rounds;
/// <summary>
/// Salsa20Impl is an implementation of <see cref="ICryptoTransform"/> that uses the Salsa20 algorithm.
/// </summary>
private sealed class Salsa20CryptoTransform : ICryptoTransform
{
public Salsa20CryptoTransform(byte[] key, byte[] iv, int rounds)
{
Debug.Assert(key.Length == 16 || key.Length == 32, "abyKey.Length == 16 || abyKey.Length == 32", "Invalid key size.");
Debug.Assert(iv.Length == 8, "abyIV.Length == 8", "Invalid IV size.");
Debug.Assert(rounds == 8 || rounds == 12 || rounds == 20, "rounds == 8 || rounds == 12 || rounds == 20", "Invalid number of rounds.");
Initialize(key, iv);
m_rounds = rounds;
}
public bool CanReuseTransform
{
get { return false; }
}
public bool CanTransformMultipleBlocks
{
get { return true; }
}
public int InputBlockSize
{
get { return 64; }
}
public int OutputBlockSize
{
get { return 64; }
}
public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
{
// check arguments
if (inputBuffer == null)
throw new ArgumentNullException("inputBuffer");
if (inputOffset < 0 || inputOffset >= inputBuffer.Length)
throw new ArgumentOutOfRangeException("inputOffset");
if (inputCount < 0 || inputOffset + inputCount > inputBuffer.Length)
throw new ArgumentOutOfRangeException("inputCount");
if (outputBuffer == null)
throw new ArgumentNullException("outputBuffer");
if (outputOffset < 0 || outputOffset + inputCount > outputBuffer.Length)
throw new ArgumentOutOfRangeException("outputOffset");
if (m_state == null)
throw new ObjectDisposedException(GetType().Name);
byte[] output = new byte[64];
int bytesTransformed = 0;
while (inputCount > 0)
{
Hash(output, m_state);
m_state[8] = AddOne(m_state[8]);
if (m_state[8] == 0)
{
// NOTE: stopping at 2^70 bytes per nonce is user's responsibility
m_state[9] = AddOne(m_state[9]);
}
int blockSize = Math.Min(64, inputCount);
for (int i = 0; i < blockSize; i++)
outputBuffer[outputOffset + i] = (byte) (inputBuffer[inputOffset + i] ^ output[i]);
bytesTransformed += blockSize;
inputCount -= 64;
outputOffset += 64;
inputOffset += 64;
}
return bytesTransformed;
}
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount)
{
if (inputCount < 0)
throw new ArgumentOutOfRangeException("inputCount");
byte[] output = new byte[inputCount];
TransformBlock(inputBuffer, inputOffset, inputCount, output, 0);
return output;
}
public void Dispose()
{
if (m_state != null)
Array.Clear(m_state, 0, m_state.Length);
m_state = null;
}
private static uint Rotate(uint v, int c)
{
return (v << c) | (v >> (32 - c));
}
private static uint Add(uint v, uint w)
{
return unchecked(v + w);
}
private static uint AddOne(uint v)
{
return unchecked(v + 1);
}
private void Hash(byte[] output, uint[] input)
{
uint[] state = (uint[]) input.Clone();
for (int round = m_rounds; round > 0; round -= 2)
{
state[4] ^= Rotate(Add(state[0], state[12]), 7);
state[8] ^= Rotate(Add(state[4], state[0]), 9);
state[12] ^= Rotate(Add(state[8], state[4]), 13);
state[0] ^= Rotate(Add(state[12], state[8]), 18);
state[9] ^= Rotate(Add(state[5], state[1]), 7);
state[13] ^= Rotate(Add(state[9], state[5]), 9);
state[1] ^= Rotate(Add(state[13], state[9]), 13);
state[5] ^= Rotate(Add(state[1], state[13]), 18);
state[14] ^= Rotate(Add(state[10], state[6]), 7);
state[2] ^= Rotate(Add(state[14], state[10]), 9);
state[6] ^= Rotate(Add(state[2], state[14]), 13);
state[10] ^= Rotate(Add(state[6], state[2]), 18);
state[3] ^= Rotate(Add(state[15], state[11]), 7);
state[7] ^= Rotate(Add(state[3], state[15]), 9);
state[11] ^= Rotate(Add(state[7], state[3]), 13);
state[15] ^= Rotate(Add(state[11], state[7]), 18);
state[1] ^= Rotate(Add(state[0], state[3]), 7);
state[2] ^= Rotate(Add(state[1], state[0]), 9);
state[3] ^= Rotate(Add(state[2], state[1]), 13);
state[0] ^= Rotate(Add(state[3], state[2]), 18);
state[6] ^= Rotate(Add(state[5], state[4]), 7);
state[7] ^= Rotate(Add(state[6], state[5]), 9);
state[4] ^= Rotate(Add(state[7], state[6]), 13);
state[5] ^= Rotate(Add(state[4], state[7]), 18);
state[11] ^= Rotate(Add(state[10], state[9]), 7);
state[8] ^= Rotate(Add(state[11], state[10]), 9);
state[9] ^= Rotate(Add(state[8], state[11]), 13);
state[10] ^= Rotate(Add(state[9], state[8]), 18);
state[12] ^= Rotate(Add(state[15], state[14]), 7);
state[13] ^= Rotate(Add(state[12], state[15]), 9);
state[14] ^= Rotate(Add(state[13], state[12]), 13);
state[15] ^= Rotate(Add(state[14], state[13]), 18);
}
for (int index = 0; index < 16; index++)
ToBytes(Add(state[index], input[index]), output, 4 * index);
}
private void Initialize(byte[] key, byte[] iv)
{
m_state = new uint[16];
m_state[1] = ToUInt32(key, 0);
m_state[2] = ToUInt32(key, 4);
m_state[3] = ToUInt32(key, 8);
m_state[4] = ToUInt32(key, 12);
byte[] constants = key.Length == 32 ? c_sigma : c_tau;
int keyIndex = key.Length - 16;
m_state[11] = ToUInt32(key, keyIndex + 0);
m_state[12] = ToUInt32(key, keyIndex + 4);
m_state[13] = ToUInt32(key, keyIndex + 8);
m_state[14] = ToUInt32(key, keyIndex + 12);
m_state[0] = ToUInt32(constants, 0);
m_state[5] = ToUInt32(constants, 4);
m_state[10] = ToUInt32(constants, 8);
m_state[15] = ToUInt32(constants, 12);
m_state[6] = ToUInt32(iv, 0);
m_state[7] = ToUInt32(iv, 4);
m_state[8] = 0;
m_state[9] = 0;
}
private static uint ToUInt32(byte[] input, int inputOffset)
{
return unchecked((uint) (((input[inputOffset] | (input[inputOffset + 1] << 8)) | (input[inputOffset + 2] << 16)) | (input[inputOffset + 3] << 24)));
}
private static void ToBytes(uint input, byte[] output, int outputOffset)
{
unchecked
{
output[outputOffset] = (byte) input;
output[outputOffset + 1] = (byte) (input >> 8);
output[outputOffset + 2] = (byte) (input >> 16);
output[outputOffset + 3] = (byte) (input >> 24);
}
}
static readonly byte[] c_sigma = Encoding.ASCII.GetBytes("expand 32-byte k");
static readonly byte[] c_tau = Encoding.ASCII.GetBytes("expand 16-byte k");
uint[] m_state;
readonly int m_rounds;
}
}
}
/*
* These tests for the Salsa20 implementation are designed to be
* used with the NUnit testing framework.
*
* This work is hereby released into the Public Domain. To view a copy of the public domain dedication,
* visit http://creativecommons.org/licenses/publicdomain/ or send a letter to
* Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
*/
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using Logos.Utility.Security.Cryptography;
using NUnit.Framework;
namespace Logos.Utility.Tests
{
[TestFixture]
public class Salsa20Tests
{
[Test]
public void BlockSize()
{
using (SymmetricAlgorithm salsa20 = new Salsa20())
{
Assert.AreEqual(512, salsa20.BlockSize);
KeySizes[] sizes = salsa20.LegalBlockSizes;
Assert.AreEqual(1, sizes.Length);
Assert.AreEqual(512, sizes[0].MinSize);
Assert.AreEqual(512, sizes[0].MaxSize);
Assert.AreEqual(0, sizes[0].SkipSize);
Assert.Throws<CryptographicException>(() => salsa20.BlockSize = 128);
}
}
[Test]
public void IV()
{
using (SymmetricAlgorithm salsa20 = new Salsa20())
{
Assert.Throws<ArgumentNullException>(() => salsa20.IV = null);
Assert.Throws<CryptographicException>(() => salsa20.IV = new byte[9]);
byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
salsa20.IV = iv;
CollectionAssert.AreEqual(iv, salsa20.IV);
}
}
[Test]
public void Key()
{
using (SymmetricAlgorithm salsa20 = new Salsa20())
{
KeySizes[] sizes = salsa20.LegalKeySizes;
Assert.AreEqual(1, sizes.Length);
Assert.AreEqual(128, sizes[0].MinSize);
Assert.AreEqual(256, sizes[0].MaxSize);
Assert.AreEqual(128, sizes[0].SkipSize);
Assert.Throws<ArgumentNullException>(() => salsa20.Key = null);
Assert.Throws<CryptographicException>(() => salsa20.Key = new byte[8]);
byte[] key16 = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
salsa20.Key = key16;
CollectionAssert.AreEqual(key16, salsa20.Key);
byte[] key32 = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f };
salsa20.Key = key32;
CollectionAssert.AreEqual(key32, salsa20.Key);
}
}
[Test]
public void Rounds()
{
using (Salsa20 salsa20 = new Salsa20())
{
Assert.AreEqual(20, salsa20.Rounds);
salsa20.Rounds = 12;
Assert.AreEqual(12, salsa20.Rounds);
salsa20.Rounds = 8;
Assert.AreEqual(8, salsa20.Rounds);
Assert.Throws<ArgumentOutOfRangeException>(() => salsa20.Rounds = 16);
}
}
[Test]
public void CreateDecryptorInvalid()
{
using (SymmetricAlgorithm salsa20 = new Salsa20())
{
Assert.Throws<ArgumentNullException>(() => salsa20.CreateDecryptor(null, new byte[8]));
Assert.Throws<ArgumentNullException>(() => salsa20.CreateDecryptor(new byte[16], null));
Assert.Throws<CryptographicException>(() => salsa20.CreateDecryptor(new byte[15], new byte[8]));
Assert.Throws<CryptographicException>(() => salsa20.CreateDecryptor(new byte[16], new byte[7]));
}
}
[Test]
public void CreateEncryptorInvalid()
{
using (SymmetricAlgorithm salsa20 = new Salsa20())
{
Assert.Throws<ArgumentNullException>(() => salsa20.CreateEncryptor(null, new byte[8]));
Assert.Throws<ArgumentNullException>(() => salsa20.CreateEncryptor(new byte[16], null));
Assert.Throws<CryptographicException>(() => salsa20.CreateEncryptor(new byte[15], new byte[8]));
Assert.Throws<CryptographicException>(() => salsa20.CreateEncryptor(new byte[16], new byte[7]));
}
}
[Test]
public void CryptoTransform()
{
using (SymmetricAlgorithm salsa20 = new Salsa20())
using (ICryptoTransform transform = salsa20.CreateEncryptor(new byte[16], new byte[8]))
{
Assert.AreEqual(salsa20.BlockSize / 8, transform.InputBlockSize);
Assert.AreEqual(salsa20.BlockSize / 8, transform.OutputBlockSize);
Assert.IsFalse(transform.CanReuseTransform);
Assert.IsTrue(transform.CanTransformMultipleBlocks);
}
}
[Test]
public void TransformBlock()
{
using (SymmetricAlgorithm salsa20 = new Salsa20())
using (ICryptoTransform transform = salsa20.CreateEncryptor(new byte[16], new byte[8]))
{
byte[] aby0 = new byte[0];
byte[] aby1 = new byte[1];
byte[] aby2 = new byte[2];
Assert.Throws<ArgumentNullException>(() => transform.TransformBlock(null, 0, 0, aby0, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformBlock(aby0, -1, 0, aby0, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformBlock(aby0, 1, 0, aby0, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformBlock(aby1, 0, -1, aby1, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformBlock(aby1, 0, 2, aby2, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformBlock(aby1, 1, 1, aby1, 0));
Assert.Throws<ArgumentNullException>(() => transform.TransformBlock(aby1, 0, 1, null, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformBlock(aby1, 0, 1, aby1, 1));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformBlock(aby2, 0, 2, aby1, 0));
}
}
[Test]
public void TransformFinalBlock()
{
using (SymmetricAlgorithm salsa20 = new Salsa20())
using (ICryptoTransform transform = salsa20.CreateEncryptor(new byte[16], new byte[8]))
{
byte[] aby0 = new byte[0];
byte[] aby1 = new byte[1];
Assert.Throws<ArgumentNullException>(() => transform.TransformFinalBlock(null, 0, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformFinalBlock(aby0, -1, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformFinalBlock(aby0, 1, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformFinalBlock(aby1, 0, -1));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformFinalBlock(aby1, 0, 2));
Assert.Throws<ArgumentOutOfRangeException>(() => transform.TransformFinalBlock(aby1, 1, 1));
}
}
[Test]
public void Dispose()
{
using (SymmetricAlgorithm salsa20 = new Salsa20())
using (ICryptoTransform transform = salsa20.CreateEncryptor(new byte[16], new byte[8]))
{
transform.Dispose();
transform.Dispose();
Assert.Throws<ObjectDisposedException>(() => transform.TransformBlock(new byte[1], 0, 1, new byte[1], 0));
Assert.Throws<ObjectDisposedException>(() => transform.TransformFinalBlock(new byte[1], 0, 1));
((IDisposable) salsa20).Dispose();
((IDisposable) salsa20).Dispose();
}
}
[Test]
public void EncryptDecrypt()
{
// generate data to encrypt
byte[] input;
using (MemoryStream stream = new MemoryStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
for (short value = -1024; value <= 1023; value++)
writer.Write(value);
writer.Flush();
input = stream.ToArray();
}
using (SymmetricAlgorithm salsa20 = new Salsa20())
{
byte[] encrypted = new byte[input.Length];
using (ICryptoTransform encrypt = salsa20.CreateEncryptor())
encrypt.TransformBlock(input, 0, input.Length, encrypted, 0);
byte[] decrypted = new byte[input.Length];
using (ICryptoTransform decrypt = salsa20.CreateDecryptor())
decrypt.TransformBlock(encrypted, 0, encrypted.Length, decrypted, 0);
CollectionAssert.AreEqual(input, decrypted);
}
}
/*
* The following tests use the vectors submitted to ECRYPT for the eSTREAM project. They were downloaded from:
* http://www.ecrypt.eu.org/stream/svn/viewcvs.cgi/ecrypt/trunk/submissions/salsa20/full/verified.test-vectors?rev=210
* http://www.ecrypt.eu.org/stream/svn/viewcvs.cgi/ecrypt/trunk/submissions/salsa20/reduced/12-rounds/verified.test-vectors?rev=210
* http://www.ecrypt.eu.org/stream/svn/viewcvs.cgi/ecrypt/trunk/submissions/salsa20/reduced/8-rounds/verified.test-vectors?rev=182
*/
[TestCase("128 bit key, Set 1, vector# 0", 20, "80000000000000000000000000000000", "0000000000000000",
"4DFA5E481DA23EA09A31022050859936DA52FCEE218005164F267CB65F5CFD7F2B4F97E0FF16924A52DF269515110A07F9E460BC65EF95DA58F740B7D1DBB0AA",
"DA9C1581F429E0A00F7D67E23B730676783B262E8EB43A25F55FB90B3E753AEF8C6713EC66C51881111593CCB3E8CB8F8DE124080501EEEB389C4BCB6977CF95",
"7D5789631EB4554400E1E025935DFA7B3E9039D61BDC58A8697D36815BF1985CEFDF7AE112E5BB81E37ECF0616CE7147FC08A93A367E08631F23C03B00A8DA2F",
"B375703739DACED4DD4059FD71C3C47FC2F9939670FAD4A46066ADCC6A5645783308B90FFB72BE04A6B147CBE38CC0C3B9267C296A92A7C69873F9F263BE9703")]
[TestCase("128 bit key, Set 2, vector# 18", 20, "12121212121212121212121212121212", "0000000000000000",
"05835754A1333770BBA8262F8A84D0FD70ABF58CDB83A54172B0C07B6CCA5641060E3097D2B19F82E918CB697D0F347DC7DAE05C14355D09B61B47298FE89AEB",
"5525C22F425949A5E51A4EAFA18F62C6E01A27EF78D79B073AEBEC436EC8183BC683CD3205CF80B795181DAFF3DC98486644C6310F09D865A7A75EE6D5105F92",
"2EE7A4F9C576EADE7EE325334212196CB7A61D6FA693238E6E2C8B53B900FF1A133A6E53F58AC89D6A695594CE03F7758DF9ABE981F23373B3680C7A4AD82680",
"CB7A0595F3A1B755E9070E8D3BACCF9574F881E4B9D91558E19317C4C254988F42184584E5538C63D964F8EF61D86B09D983998979BA3F44BAF527128D3E5393")]
[TestCase("128 bit key, Set 5, vector# 0", 20, "00000000000000000000000000000000", "8000000000000000",
"B66C1E4446DD9557E578E223B0B768017B23B267BB0234AE4626BF443F219776436FB19FD0E8866FCD0DE9A9538F4A09CA9AC0732E30BCF98E4F13E4B9E201D9",
"462920041C5543954D6230C531042B999A289542FEB3C129C5286E1A4B4CF1187447959785434BEF0D05C6EC8950E469BBA6647571DDD049C72D81AC8B75D027",
"DD84E3F631ADDC4450B9813729BD8E7CC8909A1E023EE539F12646CFEC03239A68F3008F171CDAE514D20BCD584DFD44CBF25C05D028E51870729E4087AA025B",
"5AC8474899B9E28211CC7137BD0DF290D3E926EB32D8F9C92D0FB1DE4DBE452DE3800E554B348E8A3D1B9C59B9C77B090B8E3A0BDAC520E97650195846198E9D")]
[TestCase("256 bit key, Set 1, vector# 0", 20, "8000000000000000000000000000000000000000000000000000000000000000", "0000000000000000",
"E3BE8FDD8BECA2E3EA8EF9475B29A6E7003951E1097A5C38D23B7A5FAD9F6844B22C97559E2723C7CBBD3FE4FC8D9A0744652A83E72A9C461876AF4D7EF1A117",
"57BE81F47B17D9AE7C4FF15429A73E10ACF250ED3A90A93C711308A74C6216A9ED84CD126DA7F28E8ABF8BB63517E1CA98E712F4FB2E1A6AED9FDC73291FAA17",
"958211C4BA2EBD5838C635EDB81F513A91A294E194F1C039AEEC657DCE40AA7E7C0AF57CACEFA40C9F14B71A4B3456A63E162EC7D8D10B8FFB1810D71001B618",
"696AFCFD0CDDCC83C7E77F11A649D79ACDC3354E9635FF137E929933A0BD6F5377EFA105A3A4266B7C0D089D08F1E855CC32B15B93784A36E56A76CC64BC8477")]
[TestCase("256 bit key, Set 2, vector# 63", 20, "3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F3F", "0000000000000000",
"18B631E89190A2C763AD5F1DBC57B565EAD588F7DC85C3DD75E7D7E74C1D4429E2FB3C6CB687A620EB7050CCD49B54D0F147302BFB7ADC6D1EB235A60338D190",
"FE2017B0E26C72416B6789071D0EABE48DA7531CAD058597AB3742792C79167844C84243B910FCA131C4EB3D39BD6341842F96F4059261438A81423586EEE459",
"5FA44FAD6149C7E80BA6A98A8C861993F7D39F1CAEAD07CEB96CBB9BD9153C978B8957C82F88EC2EDD1BCC207627CDB7029AFC907BBEAFAA14444F66CB9A20EA",
"CF4DD50E4D99B8A26A9ED0F8CEE5FC10E8410C7071CCFD6939C09AE576C3A5EDD2F03412E40C8BAD8DC72FAFD2ED76A1AF3BDD674EC5428BD400E2D4AE9026EF")]
[TestCase("256 bit key, Set 5, vector# 18", 20, "0000000000000000000000000000000000000000000000000000000000000000", "0000200000000000",
"621F3014E0ADC8022868C3D9070BC49E48BC6B504AFF11CB17957F0EBFB7612F7FCB67C60A2FBD7A4BD7C312E8F50AF3CA7520821D73DB47189DAD557C436DDC",
"42C8DFE869C90018825E2037BB5E2EBBC4A4A42660AFEA8A2E385AFBBC63EF3098D052FF4A52ED12107EE71C1AEC271E6870538FCEAA1191B4224A6FFDCE5327",
"4214DA4FAF0DF7FC2955D81403C9D49EE87116B1975C5823E28D9A08C5B1189DC52BCBEF065B637F1870980CB778B75ADDA41613F5F4728AD8D8D189FBF0E76D",
"4CA854257ECE95E67383FC8665C3A8238B87255F815CA4DEC2D57DB72924C60CB20A7EE40C559406AAAB25BE5F47184DD187ED7EA191133F3000CB88DCBAC433")]
[TestCase("128 bit key, Set 1, vector# 0", 12, "80000000000000000000000000000000", "0000000000000000",
"FC207DBFC76C5E1774961E7A5AAD09069B2225AC1CE0FE7A0CE77003E7E5BDF8B31AF821000813E6C56B8C1771D6EE7039B2FBD0A68E8AD70A3944B677937897",
"4B62A4881FA1AF9560586510D5527ED48A51ECAFA4DECEEBBDDC10E9918D44AB26B10C0A31ED242F146C72940C6E9C3753F641DA84E9F68B4F9E76B6C48CA5AC",
"F52383D9DEFB20810325F7AEC9EADE34D9D883FEE37E05F74BF40875B2D0BE79ED8886E5BFF556CEA8D1D9E86B1F68A964598C34F177F8163E271B8D2FEB5996",
"A52ED8C37014B10EC0AA8E05B5CEEE123A1017557FB3B15C53E6C5EA8300BF74264A73B5315DC821AD2CAB0F3BB2F152BDAEA3AEE97BA04B8E72A7B40DCC6BA4")]
[TestCase("128 bit key, Set 1, vector# 0", 8, "80000000000000000000000000000000", "0000000000000000",
"A9C9F888AB552A2D1BBFF9F36BEBEB337A8B4B107C75B63BAE26CB9A235BBA9D784F38BEFC3ADF4CD3E266687EA7B9F09BA650AE81EAC6063AE31FF12218DDC5",
"BB5B6BB2CC8B8A0222DCCC1753ED4AEB23377ACCBD5D4C0B69A8A03BB115EF71871BC10559080ACA7C68F0DEF32A80DDBAF497259BB76A3853A7183B51CC4B9F",
"4436CDC0BE39559F5E5A6B79FBDB2CAE4782910F27FFC2391E05CFC78D601AD8CD7D87B074169361D997D1BED9729C0DEB23418E0646B7997C06AA84E7640CE3",
"BEE85903BEA506B05FC04795836FAAAC7F93F785D473EB762576D96B4A65FFE463B34AAE696777FC6351B67C3753B89BA6B197BD655D1D9CA86E067F4D770220")]
public void EcryptTestVector512(string strComment, int rounds, string key, string iv, string strOutput0_63, string strOutput192_255, string strOutput256_319, string strOutput448_511)
{
const int c_nSize = 512;
byte[] output = new byte[c_nSize];
// encrypt by using CryptoStream
using (SymmetricAlgorithm salsa20 = new Salsa20() { Rounds = rounds })
using (ICryptoTransform encrypt = salsa20.CreateEncryptor(ToBytes(key), ToBytes(iv)))
using (MemoryStream streamInput = new MemoryStream(new byte[c_nSize], false))
using (CryptoStream streamEncrypted = new CryptoStream(streamInput, encrypt, CryptoStreamMode.Read))
{
streamEncrypted.Read(output, 0, output.Length);
}
CollectionAssert.AreEqual(ToBytes(strOutput0_63), output.Skip(0).Take(64).ToList());
CollectionAssert.AreEqual(ToBytes(strOutput192_255), output.Skip(192).Take(64).ToList());
CollectionAssert.AreEqual(ToBytes(strOutput256_319), output.Skip(256).Take(64).ToList());
CollectionAssert.AreEqual(ToBytes(strOutput448_511), output.Skip(448).Take(64).ToList());
}
[TestCase("128 bit key, Set 6, vector# 0", 20, "0053A6F94C9FF24598EB3E91E4378ADD", "0D74DB42A91077DE",
"05E1E7BEB697D999656BF37C1B978806735D0B903A6007BD329927EFBE1B0E2A8137C1AE291493AA83A821755BEE0B06CD14855A67E46703EBF8F3114B584CBA",
"1A70A37B1C9CA11CD3BF988D3EE4612D15F1A08D683FCCC6558ECF2089388B8E555E7619BF82EE71348F4F8D0D2AE464339D66BFC3A003BF229C0FC0AB6AE1C6",
"4ED220425F7DDB0C843232FB03A7B1C7616A50076FB056D3580DB13D2C295973D289CC335C8BC75DD87F121E85BB998166C2EF415F3F7A297E9E1BEE767F84E2",
"E121F8377E5146BFAE5AEC9F422F474FD3E9C685D32744A76D8B307A682FCA1B6BF790B5B51073E114732D3786B985FD4F45162488FEEB04C8F26E27E0F6B5CD")]
[TestCase("128 bit key, Set 6, vector# 3", 20, "0F62B5085BAE0154A7FA4DA0F34699EC", "288FF65DC42B92F9",
"71DAEE5142D0728B41B6597933EBF467E43279E30978677078941602629CBF68B73D6BD2C95F118D2B3E6EC955DABB6DC61C4143BC9A9B32B99DBE6866166DC0",
"906258725DDD0323D8E3098CBDAD6B7F941682A4745E4A42B3DC6EDEE565E6D9C65630610CDB14B5F110425F5A6DBF1870856183FA5B91FC177DFA721C5D6BF0",
"09033D9EBB07648F92858913E220FC528A10125919C891CCF8051153229B958BA9236CADF56A0F328707F7E9D5F76CCBCAF5E46A7BB9675655A426ED377D660E",
"F9876CA5B5136805445520CDA425508AE0E36DE975DE381F80E77D951D885801CEB354E4F45A2ED5F51DD61CE09942277F493452E0768B2624FACA4D9E0F7BE4")]
[TestCase("256 bit key, Set 6, vector# 0", 20, "0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D", "0D74DB42A91077DE",
"F5FAD53F79F9DF58C4AEA0D0ED9A9601F278112CA7180D565B420A48019670EAF24CE493A86263F677B46ACE1924773D2BB25571E1AA8593758FC382B1280B71",
"B70C50139C63332EF6E77AC54338A4079B82BEC9F9A403DFEA821B83F7860791650EF1B2489D0590B1DE772EEDA4E3BCD60FA7CE9CD623D9D2FD5758B8653E70",
"81582C65D7562B80AEC2F1A673A9D01C9F892A23D4919F6AB47B9154E08E699B4117D7C666477B60F8391481682F5D95D96623DBC489D88DAA6956B9F0646B6E",
"A13FFA1208F8BF50900886FAAB40FD10E8CAA306E63DF39536A1564FB760B242A9D6A4628CDC878762834E27A541DA2A5E3B3445989C76F611E0FEC6D91ACACC")]
[TestCase("256 bit key, Set 6, vector# 3", 20, "0F62B5085BAE0154A7FA4DA0F34699EC3F92E5388BDE3184D72A7DD02376C91C", "288FF65DC42B92F9",
"5E5E71F90199340304ABB22A37B6625BF883FB89CE3B21F54A10B81066EF87DA30B77699AA7379DA595C77DD59542DA208E5954F89E40EB7AA80A84A6176663F",
"2DA2174BD150A1DFEC1796E921E9D6E24ECF0209BCBEA4F98370FCE629056F64917283436E2D3F45556225307D5CC5A565325D8993B37F1654195C240BF75B16",
"ABF39A210EEE89598B7133377056C2FEF42DA731327563FB67C7BEDB27F38C7C5A3FC2183A4C6B277F901152472C6B2ABCF5E34CBE315E81FD3D180B5D66CB6C",
"1BA89DBD3F98839728F56791D5B7CE235036DE843CCCAB0390B8B5862F1E4596AE8A16FB23DA997F371F4E0AACC26DB8EB314ED470B1AF6B9F8D69DD79A9D750")]
[TestCase("128 bit key, Set 6, vector# 0:", 12, "0053A6F94C9FF24598EB3E91E4378ADD", "0D74DB42A91077DE",
"AD9E60E6D2A264B89DFF9FB129C43BE7AF76941B496AA3D2CD43489DB59AB424491A7E48421DA3AAAFBD841E86AEADD762A08B2198FFC403D1023C90C1D5C45C",
"FCFCEDDB8BB103AE3D0F838F16D387903345EC7EF5BFF71767116F8B12AB648B8CA707BFE466D340C9047C4777FDAB3D87BDAE93ACF7CE284FBA25B3426B479E",
"7B134CC1E9DDE0CE5B3177106DE6BDB97793A531FE5A8A1B01B5FD10649E2D7109795C572456A2C3E18B0E1BF938766F9944B31A178BECBD9F2191C6DD608A2A",
"727730F9D6F5B2C9F14849B7E7E03291E83BFB478A50F8E67D0FC5C4722011BBB75B76D60604734BB89F7FB2146C29B42F0949F29FA37B8E1B8E2F99E8429F9A")]
[TestCase("256 bit key, Set 6, vector# 0:", 12, "0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D", "0D74DB42A91077DE",
"52E20CF8775AE882F200C2999FE4BA31A7A18F1D5C9716191D123175E147BD4E8CA6ED166CE0FC8E65A5CA608420FC6544C9700A0F2138E8C1A286FB8C1FBFA0",
"8FBC9FE8691BD4F082B47F5405EDFBC16F4D5A12DDCB2D754E8A9998D0B219557DFE2984F4A1D2DDA76B9596928CCE0556F50066CD599E44EF5C14B226683AEF",
"BCBD01DD28961CC7AD3047386CBCC67C108D6AF11167E40D7AE1B2FC4518A867EFE402651D1D8851C4FD2330C597B36A46D5689E00FC96FECF9CE3E2211D44BE",
"9166F31CD85B5BB18FC614E54E4AD67FB8658E3BF9FB19B7A82F0FE7DC902DF563C6AC4F446748C4BC3E1405E124820DC40941998F44A810E722787FCD47784C")]
[TestCase("128 bit key, Set 6, vector# 0:", 8, "0053A6F94C9FF24598EB3E91E4378ADD", "0D74DB42A91077DE",
"75FCAE3A3961BDC7D2513662C24ADECE995545599FF129006E7A6EE57B7F33A26D1B27C51EA15E8F956693472DC23132FCD90FB0E352D26AF4DCE5427193CA26",
"EA75A566C431A10CED804CCD45172AD1EC4930E9869372B8EDDF303098A8910CEE123BF849C51A33554BA1445E6B62684921F36B77EADC9681A2BB9DDFEC2FC8",
"227CBD5D7AAC2DACA9D3A1D86E8F7628ACF6787019B4FBD77EF63478C19A51B49F30EDE4FFD8623DD321A35D615FECF48D97AE7B33FBF5C0DE5E6B4CA286002F",
"11D4302F3C7A3E406AF5AF012787B6882FA8339F65CB1D2C5FA794A0FEECB9A23F3702D754F3C3D66DF6F528F5E7BB71EF182B4231B142A1845191D38F0FC578")]
[TestCase("256 bit key, Set 6, vector# 0:", 8, "0053A6F94C9FF24598EB3E91E4378ADD3083D6297CCF2275C81B6EC11467BA0D", "0D74DB42A91077DE",
"420609C7CDDA902D6FA7CB264AB0C89A030DB2E4BF18D179BF7A3114F2266E2D5519F123A079233B5FF82481E508828DAB2620E5521056FAF60BE1489A716971",
"FB60D119C4443C7978FD8D68900D5478D5ED042DC632FB5AE025896B8E61F20CD169FB57A72DB4BCAEBC9671B146B99BADB2EAFF56E83CECC94A913A852B6711",
"854FE0BE60EA49F4D389D9A63E6BCD74263211722D6D4B3865466D806F61E719F4C76909436897E64C3C34B91E8D0220776DEA862B0C692DAD4EFF58E0CC08E4",
"BC4B88C2D58367F7A523D6DD118CFA6C7EFA657B115E9B4D9E54C10BFC2DA2BDD71F2001EA6A22FC2DC5BBE682C2A072DAAA8520E7B74CD59B75305DFE816F9B")]
public void EcryptTestVector131072(string strComment, int rounds, string key, string iv, string strOutput0_63, string strOutput65472_65535, string strOutput65536_65599, string strOutput131008_131071)
{
byte[] output;
// use the Salsa20 transform directly
using (SymmetricAlgorithm salsa20 = new Salsa20() { Rounds = rounds })
using (ICryptoTransform encrypt = salsa20.CreateEncryptor(ToBytes(key), ToBytes(iv)))
{
// input is all zeroes
byte[] input = new byte[131072];
output = encrypt.TransformFinalBlock(input, 0, input.Length);
}
CollectionAssert.AreEqual(ToBytes(strOutput0_63), output.Skip(0).Take(64).ToList());
CollectionAssert.AreEqual(ToBytes(strOutput65472_65535), output.Skip(65472).Take(64).ToList());
CollectionAssert.AreEqual(ToBytes(strOutput65536_65599), output.Skip(65536).Take(64).ToList());
CollectionAssert.AreEqual(ToBytes(strOutput131008_131071), output.Skip(131008).Take(64).ToList());
}
private static byte[] ToBytes(string hex)
{
byte[] output = new byte[hex.Length / 2];
for (int nChar = 0; nChar < hex.Length; nChar += 2)
output[nChar / 2] = ToByte(hex, nChar);
return output;
}
private static byte ToByte(string hex, int offset)
{
return (byte) (ToNibble(hex[offset]) * 16 + ToNibble(hex[offset + 1]));
}
private static int ToNibble(char hex)
{
return hex > '9' ? hex - ('A' - 10) : hex - '0';
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment