diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2013-11-01 18:46:29 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2013-11-01 18:46:29 +0700 |
commit | c1203484971419bc08b1ae937ee439aef9161b4d (patch) | |
tree | 8120ae47d99573e9ccbe988445a68279218449c3 /crypto/src | |
parent | added obligatory Inc. (diff) | |
parent | Use xmldoc for documentation of Salsa20/XSalsa20/ChaCha (diff) | |
download | BouncyCastle.NET-ed25519-c1203484971419bc08b1ae937ee439aef9161b4d.tar.xz |
Merge branch 'feature/latin-dances' of git://github.com/timw/bc-csharp
Diffstat (limited to 'crypto/src')
-rw-r--r-- | crypto/src/crypto/engines/ChaChaEngine.cs | 189 | ||||
-rw-r--r-- | crypto/src/crypto/engines/Salsa20Engine.cs | 277 | ||||
-rw-r--r-- | crypto/src/crypto/engines/XSalsa20Engine.cs | 71 |
3 files changed, 435 insertions, 102 deletions
diff --git a/crypto/src/crypto/engines/ChaChaEngine.cs b/crypto/src/crypto/engines/ChaChaEngine.cs new file mode 100644 index 000000000..f4a7b8fe1 --- /dev/null +++ b/crypto/src/crypto/engines/ChaChaEngine.cs @@ -0,0 +1,189 @@ +using System; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <summary> + /// Implementation of Daniel J. Bernstein's ChaCha stream cipher. + /// </summary> + public class ChaChaEngine + : Salsa20Engine + { + + /// <summary> + /// Creates a 20 rounds ChaCha engine. + /// </summary> + public ChaChaEngine() + { + } + + /// <summary> + /// Creates a ChaCha engine with a specific number of rounds. + /// </summary> + /// <param name="rounds">the number of rounds (must be an even number).</param> + public ChaChaEngine(int rounds) + : base(rounds) + { + } + + public override string AlgorithmName + { + get { return "ChaCha" + rounds; } + } + + protected override void AdvanceCounter() + { + if (++engineState[12] == 0) + { + ++engineState[13]; + } + } + + protected override void ResetCounter() + { + engineState[12] = engineState[13] = 0; + } + + protected override void SetKey(byte[] keyBytes, byte[] ivBytes) + { + if ((keyBytes.Length != 16) && (keyBytes.Length != 32)) + { + throw new ArgumentException(AlgorithmName + " requires 128 bit or 256 bit key"); + } + + int offset = 0; + byte[] constants; + + // Key + engineState[4] = Pack.LE_To_UInt32(keyBytes, 0); + engineState[5] = Pack.LE_To_UInt32(keyBytes, 4); + engineState[6] = Pack.LE_To_UInt32(keyBytes, 8); + engineState[7] = Pack.LE_To_UInt32(keyBytes, 12); + + if (keyBytes.Length == 32) + { + constants = sigma; + offset = 16; + } else + { + constants = tau; + } + + engineState[8] = Pack.LE_To_UInt32(keyBytes, offset); + engineState[9] = Pack.LE_To_UInt32(keyBytes, offset + 4); + engineState[10] = Pack.LE_To_UInt32(keyBytes, offset + 8); + engineState[11] = Pack.LE_To_UInt32(keyBytes, offset + 12); + + engineState[0] = Pack.LE_To_UInt32(constants, 0); + engineState[1] = Pack.LE_To_UInt32(constants, 4); + engineState[2] = Pack.LE_To_UInt32(constants, 8); + engineState[3] = Pack.LE_To_UInt32(constants, 12); + + // Counter + engineState[12] = engineState[13] = 0; + + // IV + engineState[14] = Pack.LE_To_UInt32(ivBytes, 0); + engineState[15] = Pack.LE_To_UInt32(ivBytes, 4); + } + + protected override void GenerateKeyStream(byte[] output) + { + ChachaCore(rounds, engineState, x); + Pack.UInt32_To_LE(x, output, 0); + } + + /// <summary> + /// ChacCha function. + /// </summary> + /// <param name="rounds">The number of ChaCha rounds to execute</param> + /// <param name="input">The input words.</param> + /// <param name="x">The ChaCha state to modify.</param> + internal static void ChachaCore(int rounds, uint[] input, uint[] x) + { + if (input.Length != 16) { + throw new ArgumentException(); + } + if (x.Length != 16) { + throw new ArgumentException(); + } + if (rounds % 2 != 0) { + throw new ArgumentException("Number of rounds must be even"); + } + + uint x00 = input[ 0]; + uint x01 = input[ 1]; + uint x02 = input[ 2]; + uint x03 = input[ 3]; + uint x04 = input[ 4]; + uint x05 = input[ 5]; + uint x06 = input[ 6]; + uint x07 = input[ 7]; + uint x08 = input[ 8]; + uint x09 = input[ 9]; + uint x10 = input[10]; + uint x11 = input[11]; + uint x12 = input[12]; + uint x13 = input[13]; + uint x14 = input[14]; + uint x15 = input[15]; + + for (int i = rounds; i > 0; i -= 2) + { + x00 += x04; x12 = R(x12 ^ x00, 16); + x08 += x12; x04 = R(x04 ^ x08, 12); + x00 += x04; x12 = R(x12 ^ x00, 8); + x08 += x12; x04 = R(x04 ^ x08, 7); + x01 += x05; x13 = R(x13 ^ x01, 16); + x09 += x13; x05 = R(x05 ^ x09, 12); + x01 += x05; x13 = R(x13 ^ x01, 8); + x09 += x13; x05 = R(x05 ^ x09, 7); + x02 += x06; x14 = R(x14 ^ x02, 16); + x10 += x14; x06 = R(x06 ^ x10, 12); + x02 += x06; x14 = R(x14 ^ x02, 8); + x10 += x14; x06 = R(x06 ^ x10, 7); + x03 += x07; x15 = R(x15 ^ x03, 16); + x11 += x15; x07 = R(x07 ^ x11, 12); + x03 += x07; x15 = R(x15 ^ x03, 8); + x11 += x15; x07 = R(x07 ^ x11, 7); + x00 += x05; x15 = R(x15 ^ x00, 16); + x10 += x15; x05 = R(x05 ^ x10, 12); + x00 += x05; x15 = R(x15 ^ x00, 8); + x10 += x15; x05 = R(x05 ^ x10, 7); + x01 += x06; x12 = R(x12 ^ x01, 16); + x11 += x12; x06 = R(x06 ^ x11, 12); + x01 += x06; x12 = R(x12 ^ x01, 8); + x11 += x12; x06 = R(x06 ^ x11, 7); + x02 += x07; x13 = R(x13 ^ x02, 16); + x08 += x13; x07 = R(x07 ^ x08, 12); + x02 += x07; x13 = R(x13 ^ x02, 8); + x08 += x13; x07 = R(x07 ^ x08, 7); + x03 += x04; x14 = R(x14 ^ x03, 16); + x09 += x14; x04 = R(x04 ^ x09, 12); + x03 += x04; x14 = R(x14 ^ x03, 8); + x09 += x14; x04 = R(x04 ^ x09, 7); + + } + + x[ 0] = x00 + input[ 0]; + x[ 1] = x01 + input[ 1]; + x[ 2] = x02 + input[ 2]; + x[ 3] = x03 + input[ 3]; + x[ 4] = x04 + input[ 4]; + x[ 5] = x05 + input[ 5]; + x[ 6] = x06 + input[ 6]; + x[ 7] = x07 + input[ 7]; + x[ 8] = x08 + input[ 8]; + x[ 9] = x09 + input[ 9]; + x[10] = x10 + input[10]; + x[11] = x11 + input[11]; + x[12] = x12 + input[12]; + x[13] = x13 + input[13]; + x[14] = x14 + input[14]; + x[15] = x15 + input[15]; + } + + } + +} + diff --git a/crypto/src/crypto/engines/Salsa20Engine.cs b/crypto/src/crypto/engines/Salsa20Engine.cs index 7d68deab1..81884d603 100644 --- a/crypto/src/crypto/engines/Salsa20Engine.cs +++ b/crypto/src/crypto/engines/Salsa20Engine.cs @@ -7,44 +7,60 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Engines { - /** - * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005 - */ + /// <summary> + /// Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005 + /// </summary> public class Salsa20Engine : IStreamCipher { + public static readonly int DEFAULT_ROUNDS = 20; + /** Constants */ private const int StateSize = 16; // 16, 32 bit ints = 64 bytes - private readonly static byte[] + protected readonly static byte[] sigma = Strings.ToAsciiByteArray("expand 32-byte k"), tau = Strings.ToAsciiByteArray("expand 16-byte k"); + protected int rounds; + /* * variables to hold the state of the engine * during encryption and decryption */ - private int index = 0; - private uint[] engineState = new uint[StateSize]; // state - private uint[] x = new uint[StateSize]; // internal buffer - private byte[] keyStream = new byte[StateSize * 4], // expanded state, 64 bytes - workingKey = null, - workingIV = null; - private bool initialised = false; + private int index = 0; + internal uint[] engineState = new uint[StateSize]; // state + internal uint[] x = new uint[StateSize]; // internal buffer + private byte[] keyStream = new byte[StateSize * 4]; // expanded state, 64 bytes + private bool initialised = false; /* * internal counter */ private uint cW0, cW1, cW2; - /** - * initialise a Salsa20 cipher. - * - * @param forEncryption whether or not we are for encryption. - * @param params the parameters required to set up the cipher. - * @exception ArgumentException if the params argument is - * inappropriate. - */ + /// <summary> + /// Creates a 20 round Salsa20 engine. + /// </summary> + public Salsa20Engine() + : this(DEFAULT_ROUNDS) + { + } + + /// <summary> + /// Creates a Salsa20 engine with a specific number of rounds. + /// </summary> + /// <param name="rounds">the number of rounds (must be an even number).</param> + public Salsa20Engine(int rounds) + { + if (rounds <= 0 || (rounds & 1) != 0) + { + throw new ArgumentException("'rounds' must be a positive, even number"); + } + + this.rounds = rounds; + } + public void Init( bool forEncryption, ICipherParameters parameters) @@ -58,27 +74,38 @@ namespace Org.BouncyCastle.Crypto.Engines ParametersWithIV ivParams = parameters as ParametersWithIV; if (ivParams == null) - throw new ArgumentException("Salsa20 Init requires an IV", "parameters"); + throw new ArgumentException(AlgorithmName + " Init requires an IV", "parameters"); byte[] iv = ivParams.GetIV(); - if (iv == null || iv.Length != 8) - throw new ArgumentException("Salsa20 requires exactly 8 bytes of IV"); + if (iv == null || iv.Length != NonceSize) + throw new ArgumentException(AlgorithmName + " requires exactly " + NonceSize + " bytes of IV"); KeyParameter key = ivParams.Parameters as KeyParameter; if (key == null) - throw new ArgumentException("Salsa20 Init requires a key", "parameters"); + throw new ArgumentException(AlgorithmName + " Init requires a key", "parameters"); - workingKey = key.GetKey(); - workingIV = iv; + SetKey(key.GetKey(), iv); + Reset(); + initialised = true; + } - SetKey(workingKey, workingIV); + protected virtual int NonceSize + { + get { return 8; } } - public string AlgorithmName + public virtual string AlgorithmName { - get { return "Salsa20"; } + get { + string name = "Salsa20"; + if (rounds != DEFAULT_ROUNDS) + { + name += "/" + rounds; + } + return name; + } } public byte ReturnByte( @@ -92,11 +119,7 @@ namespace Org.BouncyCastle.Crypto.Engines if (index == 0) { GenerateKeyStream(keyStream); - - if (++engineState[8] == 0) - { - ++engineState[9]; - } + AdvanceCounter(); } byte output = (byte)(keyStream[index] ^ input); @@ -105,6 +128,14 @@ namespace Org.BouncyCastle.Crypto.Engines return output; } + protected virtual void AdvanceCounter() + { + if (++engineState[8] == 0) + { + ++engineState[9]; + } + } + public void ProcessBytes( byte[] inBytes, int inOff, @@ -137,11 +168,7 @@ namespace Org.BouncyCastle.Crypto.Engines if (index == 0) { GenerateKeyStream(keyStream); - - if (++engineState[8] == 0) - { - ++engineState[9]; - } + AdvanceCounter(); } outBytes[i+outOff] = (byte)(keyStream[index]^inBytes[i+inOff]); index = (index + 1) & 63; @@ -150,28 +177,32 @@ namespace Org.BouncyCastle.Crypto.Engines public void Reset() { - SetKey(workingKey, workingIV); + index = 0; + ResetLimitCounter(); + ResetCounter(); } - // Private implementation + protected virtual void ResetCounter() + { + engineState[8] = engineState[9] = 0; + } - private void SetKey(byte[] keyBytes, byte[] ivBytes) + protected virtual void SetKey(byte[] keyBytes, byte[] ivBytes) { - workingKey = keyBytes; - workingIV = ivBytes; + if ((keyBytes.Length != 16) && (keyBytes.Length != 32)) { + throw new ArgumentException(AlgorithmName + " requires 128 bit or 256 bit key"); + } - index = 0; - ResetCounter(); int offset = 0; byte[] constants; // Key - engineState[1] = Pack.LE_To_UInt32(workingKey, 0); - engineState[2] = Pack.LE_To_UInt32(workingKey, 4); - engineState[3] = Pack.LE_To_UInt32(workingKey, 8); - engineState[4] = Pack.LE_To_UInt32(workingKey, 12); + engineState[1] = Pack.LE_To_UInt32(keyBytes, 0); + engineState[2] = Pack.LE_To_UInt32(keyBytes, 4); + engineState[3] = Pack.LE_To_UInt32(keyBytes, 8); + engineState[4] = Pack.LE_To_UInt32(keyBytes, 12); - if (workingKey.Length == 32) + if (keyBytes.Length == 32) { constants = sigma; offset = 16; @@ -181,83 +212,125 @@ namespace Org.BouncyCastle.Crypto.Engines constants = tau; } - engineState[11] = Pack.LE_To_UInt32(workingKey, offset); - engineState[12] = Pack.LE_To_UInt32(workingKey, offset + 4); - engineState[13] = Pack.LE_To_UInt32(workingKey, offset + 8); - engineState[14] = Pack.LE_To_UInt32(workingKey, offset + 12); + engineState[11] = Pack.LE_To_UInt32(keyBytes, offset); + engineState[12] = Pack.LE_To_UInt32(keyBytes, offset + 4); + engineState[13] = Pack.LE_To_UInt32(keyBytes, offset + 8); + engineState[14] = Pack.LE_To_UInt32(keyBytes, offset + 12); engineState[0] = Pack.LE_To_UInt32(constants, 0); engineState[5] = Pack.LE_To_UInt32(constants, 4); engineState[10] = Pack.LE_To_UInt32(constants, 8); engineState[15] = Pack.LE_To_UInt32(constants, 12); // IV - engineState[6] = Pack.LE_To_UInt32(workingIV, 0); - engineState[7] = Pack.LE_To_UInt32(workingIV, 4); - engineState[8] = engineState[9] = 0; - - initialised = true; + engineState[6] = Pack.LE_To_UInt32(ivBytes, 0); + engineState[7] = Pack.LE_To_UInt32(ivBytes, 4); + ResetCounter(); } - private void GenerateKeyStream(byte[] output) + protected virtual void GenerateKeyStream(byte[] output) { - SalsaCore(20, engineState, x); + SalsaCore(rounds, engineState, x); Pack.UInt32_To_LE(x, output, 0); } - internal static void SalsaCore(int rounds, uint[] state, uint[] x) + internal static void SalsaCore(int rounds, uint[] input, uint[] x) { - // TODO Exception if rounds odd? + if (input.Length != 16) { + throw new ArgumentException(); + } + if (x.Length != 16) { + throw new ArgumentException(); + } + if (rounds % 2 != 0) { + throw new ArgumentException("Number of rounds must be even"); + } - Array.Copy(state, 0, x, 0, state.Length); + uint x00 = input[ 0]; + uint x01 = input[ 1]; + uint x02 = input[ 2]; + uint x03 = input[ 3]; + uint x04 = input[ 4]; + uint x05 = input[ 5]; + uint x06 = input[ 6]; + uint x07 = input[ 7]; + uint x08 = input[ 8]; + uint x09 = input[ 9]; + uint x10 = input[10]; + uint x11 = input[11]; + uint x12 = input[12]; + uint x13 = input[13]; + uint x14 = input[14]; + uint x15 = input[15]; for (int i = rounds; i > 0; i -= 2) { - x[ 4] ^= R((x[ 0]+x[12]), 7); - x[ 8] ^= R((x[ 4]+x[ 0]), 9); - x[12] ^= R((x[ 8]+x[ 4]),13); - x[ 0] ^= R((x[12]+x[ 8]),18); - x[ 9] ^= R((x[ 5]+x[ 1]), 7); - x[13] ^= R((x[ 9]+x[ 5]), 9); - x[ 1] ^= R((x[13]+x[ 9]),13); - x[ 5] ^= R((x[ 1]+x[13]),18); - x[14] ^= R((x[10]+x[ 6]), 7); - x[ 2] ^= R((x[14]+x[10]), 9); - x[ 6] ^= R((x[ 2]+x[14]),13); - x[10] ^= R((x[ 6]+x[ 2]),18); - x[ 3] ^= R((x[15]+x[11]), 7); - x[ 7] ^= R((x[ 3]+x[15]), 9); - x[11] ^= R((x[ 7]+x[ 3]),13); - x[15] ^= R((x[11]+x[ 7]),18); - x[ 1] ^= R((x[ 0]+x[ 3]), 7); - x[ 2] ^= R((x[ 1]+x[ 0]), 9); - x[ 3] ^= R((x[ 2]+x[ 1]),13); - x[ 0] ^= R((x[ 3]+x[ 2]),18); - x[ 6] ^= R((x[ 5]+x[ 4]), 7); - x[ 7] ^= R((x[ 6]+x[ 5]), 9); - x[ 4] ^= R((x[ 7]+x[ 6]),13); - x[ 5] ^= R((x[ 4]+x[ 7]),18); - x[11] ^= R((x[10]+x[ 9]), 7); - x[ 8] ^= R((x[11]+x[10]), 9); - x[ 9] ^= R((x[ 8]+x[11]),13); - x[10] ^= R((x[ 9]+x[ 8]),18); - x[12] ^= R((x[15]+x[14]), 7); - x[13] ^= R((x[12]+x[15]), 9); - x[14] ^= R((x[13]+x[12]),13); - x[15] ^= R((x[14]+x[13]),18); + x04 ^= R((x00+x12), 7); + x08 ^= R((x04+x00), 9); + x12 ^= R((x08+x04),13); + x00 ^= R((x12+x08),18); + x09 ^= R((x05+x01), 7); + x13 ^= R((x09+x05), 9); + x01 ^= R((x13+x09),13); + x05 ^= R((x01+x13),18); + x14 ^= R((x10+x06), 7); + x02 ^= R((x14+x10), 9); + x06 ^= R((x02+x14),13); + x10 ^= R((x06+x02),18); + x03 ^= R((x15+x11), 7); + x07 ^= R((x03+x15), 9); + x11 ^= R((x07+x03),13); + x15 ^= R((x11+x07),18); + + x01 ^= R((x00+x03), 7); + x02 ^= R((x01+x00), 9); + x03 ^= R((x02+x01),13); + x00 ^= R((x03+x02),18); + x06 ^= R((x05+x04), 7); + x07 ^= R((x06+x05), 9); + x04 ^= R((x07+x06),13); + x05 ^= R((x04+x07),18); + x11 ^= R((x10+x09), 7); + x08 ^= R((x11+x10), 9); + x09 ^= R((x08+x11),13); + x10 ^= R((x09+x08),18); + x12 ^= R((x15+x14), 7); + x13 ^= R((x12+x15), 9); + x14 ^= R((x13+x12),13); + x15 ^= R((x14+x13),18); } - for (int i = 0; i < StateSize; ++i) - { - x[i] += state[i]; - } + x[ 0] = x00 + input[ 0]; + x[ 1] = x01 + input[ 1]; + x[ 2] = x02 + input[ 2]; + x[ 3] = x03 + input[ 3]; + x[ 4] = x04 + input[ 4]; + x[ 5] = x05 + input[ 5]; + x[ 6] = x06 + input[ 6]; + x[ 7] = x07 + input[ 7]; + x[ 8] = x08 + input[ 8]; + x[ 9] = x09 + input[ 9]; + x[10] = x10 + input[10]; + x[11] = x11 + input[11]; + x[12] = x12 + input[12]; + x[13] = x13 + input[13]; + x[14] = x14 + input[14]; + x[15] = x15 + input[15]; } - private static uint R(uint x, int y) + /** + * Rotate left + * + * @param x value to rotate + * @param y amount to rotate x + * + * @return rotated x + */ + internal static uint R(uint x, int y) { return (x << y) | (x >> (32 - y)); } - private void ResetCounter() + private void ResetLimitCounter() { cW0 = 0; cW1 = 0; diff --git a/crypto/src/crypto/engines/XSalsa20Engine.cs b/crypto/src/crypto/engines/XSalsa20Engine.cs new file mode 100644 index 000000000..fc6630905 --- /dev/null +++ b/crypto/src/crypto/engines/XSalsa20Engine.cs @@ -0,0 +1,71 @@ +using System; + +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + /// <summary> + /// Implementation of Daniel J. Bernstein's XSalsa20 stream cipher - Salsa20 with an extended nonce. + /// </summary> + /// <remarks> + /// XSalsa20 requires a 256 bit key, and a 192 bit nonce. + /// </remarks> + public class XSalsa20Engine + : Salsa20Engine + { + + public override string AlgorithmName + { + get { return "XSalsa20"; } + } + + protected override int NonceSize + { + get { return 24; } + } + + /// <summary> + /// XSalsa20 key generation: process 256 bit input key and 128 bits of the input nonce + /// using a core Salsa20 function without input addition to produce 256 bit working key + /// and use that with the remaining 64 bits of nonce to initialize a standard Salsa20 engine state. + /// </summary> + protected override void SetKey(byte[] keyBytes, byte[] ivBytes) + { + if (keyBytes.Length != 32) + { + throw new ArgumentException(AlgorithmName + " requires a 256 bit key"); + } + + // Set key for HSalsa20 + base.SetKey(keyBytes, ivBytes); + + // Pack next 64 bits of IV into engine state instead of counter + engineState[8] = Pack.LE_To_UInt32(ivBytes, 8); + engineState[9] = Pack.LE_To_UInt32(ivBytes, 12); + + // Process engine state to generate Salsa20 key + uint[] hsalsa20Out = new uint[engineState.Length]; + SalsaCore(20, engineState, hsalsa20Out); + + // Set new key, removing addition in last round of salsaCore + engineState[1] = hsalsa20Out[0] - engineState[0]; + engineState[2] = hsalsa20Out[5] - engineState[5]; + engineState[3] = hsalsa20Out[10] - engineState[10]; + engineState[4] = hsalsa20Out[15] - engineState[15]; + + engineState[11] = hsalsa20Out[6] - engineState[6]; + engineState[12] = hsalsa20Out[7] - engineState[7]; + engineState[13] = hsalsa20Out[8] - engineState[8]; + engineState[14] = hsalsa20Out[9] - engineState[9]; + + // Last 64 bits of input IV + engineState[6] = Pack.LE_To_UInt32(ivBytes, 16); + engineState[7] = Pack.LE_To_UInt32(ivBytes, 20); + + // Counter reset + ResetCounter(); + } + + } +} + |