diff --git a/crypto/src/asn1/DerEnumerated.cs b/crypto/src/asn1/DerEnumerated.cs
index 0e67e6dbe..a62afb301 100644
--- a/crypto/src/asn1/DerEnumerated.cs
+++ b/crypto/src/asn1/DerEnumerated.cs
@@ -64,7 +64,7 @@ namespace Org.BouncyCastle.Asn1
public DerEnumerated(
byte[] bytes)
{
- this.bytes = bytes;
+ this.bytes = Arrays.Clone(bytes);
}
public BigInteger Value
diff --git a/crypto/src/asn1/isismtt/x509/AdmissionSyntax.cs b/crypto/src/asn1/isismtt/x509/AdmissionSyntax.cs
index 0185b2a62..f322ef88f 100644
--- a/crypto/src/asn1/isismtt/x509/AdmissionSyntax.cs
+++ b/crypto/src/asn1/isismtt/x509/AdmissionSyntax.cs
Binary files differdiff --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();
+ }
+
+ }
+}
+
diff --git a/crypto/src/crypto/macs/CMac.cs b/crypto/src/crypto/macs/CMac.cs
index b55a05605..682c12bac 100644
--- a/crypto/src/crypto/macs/CMac.cs
+++ b/crypto/src/crypto/macs/CMac.cs
@@ -107,7 +107,7 @@ namespace Org.BouncyCastle.Crypto.Macs
private static int ShiftLeft(byte[] block, byte[] output)
{
- int i = 16;
+ int i = block.Length;
uint bit = 0;
while (--i >= 0)
{
diff --git a/crypto/src/crypto/macs/GMac.cs b/crypto/src/crypto/macs/GMac.cs
new file mode 100644
index 000000000..eb340ddbc
--- /dev/null
+++ b/crypto/src/crypto/macs/GMac.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Macs
+{
+ /// <summary>
+ /// The GMAC specialisation of Galois/Counter mode (GCM) detailed in NIST Special Publication
+ /// 800-38D.
+ /// </summary>
+ /// <remarks>
+ /// GMac is an invocation of the GCM mode where no data is encrypted (i.e. all input data to the Mac
+ /// is processed as additional authenticated data with the underlying GCM block cipher).
+ /// </remarks>
+ public class GMac
+ : IMac
+ {
+ private readonly GcmBlockCipher cipher;
+ private readonly int macSizeBits;
+
+ /// <summary>
+ /// Creates a GMAC based on the operation of a block cipher in GCM mode.
+ /// </summary>
+ /// <remarks>
+ /// This will produce an authentication code the length of the block size of the cipher.
+ /// </remarks>
+ /// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param>
+ public GMac(GcmBlockCipher cipher)
+ : this(cipher, 128)
+ {
+ }
+
+ /// <summary>
+ /// Creates a GMAC based on the operation of a 128 bit block cipher in GCM mode.
+ /// </summary>
+ /// <remarks>
+ /// This will produce an authentication code the length of the block size of the cipher.
+ /// </remarks>
+ /// <param name="cipher">the cipher to be used in GCM mode to generate the MAC.</param>
+ /// <param name="macSizeBits">the mac size to generate, in bits. Must be a multiple of 8, between 96 and 128 (inclusive).</param>
+ public GMac(GcmBlockCipher cipher, int macSizeBits)
+ {
+ this.cipher = cipher;
+ this.macSizeBits = macSizeBits;
+ }
+
+ /// <summary>
+ /// Initialises the GMAC - requires a <see cref="Org.BouncyCastle.Crypto.Parameters.ParametersWithIV"/>
+ /// providing a <see cref="Org.BouncyCastle.Crypto.Parameters.KeyParameter"/> and a nonce.
+ /// </summary>
+ public void Init(ICipherParameters parameters)
+ {
+ if (parameters is ParametersWithIV)
+ {
+ ParametersWithIV param = (ParametersWithIV)parameters;
+
+ byte[] iv = param.GetIV();
+ KeyParameter keyParam = (KeyParameter)param.Parameters;
+
+ // GCM is always operated in encrypt mode to calculate MAC
+ cipher.Init(true, new AeadParameters(keyParam, macSizeBits, iv));
+ }
+ else
+ {
+ throw new ArgumentException("GMAC requires ParametersWithIV");
+ }
+ }
+
+ public string AlgorithmName
+ {
+ get { return cipher.GetUnderlyingCipher().AlgorithmName + "-GMAC"; }
+ }
+
+ public int GetMacSize()
+ {
+ return macSizeBits / 8;
+ }
+
+ public void Update(byte input)
+ {
+ cipher.ProcessAadByte(input);
+ }
+
+ public void BlockUpdate(byte[] input, int inOff, int len)
+ {
+ cipher.ProcessAadBytes(input, inOff, len);
+ }
+
+ public int DoFinal(byte[] output, int outOff)
+ {
+ try
+ {
+ return cipher.DoFinal(output, outOff);
+ }
+ catch (InvalidCipherTextException e)
+ {
+ // Impossible in encrypt mode
+ throw new InvalidOperationException(e.ToString());
+ }
+ }
+
+ public void Reset()
+ {
+ cipher.Reset();
+ }
+ }
+}
diff --git a/crypto/src/crypto/modes/EAXBlockCipher.cs b/crypto/src/crypto/modes/EAXBlockCipher.cs
index bb027b597..5ccc69b66 100644
--- a/crypto/src/crypto/modes/EAXBlockCipher.cs
+++ b/crypto/src/crypto/modes/EAXBlockCipher.cs
@@ -65,6 +65,11 @@ namespace Org.BouncyCastle.Crypto.Modes
get { return cipher.GetUnderlyingCipher().AlgorithmName + "/EAX"; }
}
+ public IBlockCipher GetUnderlyingCipher()
+ {
+ return cipher;
+ }
+
public virtual int GetBlockSize()
{
return cipher.GetBlockSize();
diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs
index 95fe6f7ec..74b895e7b 100644
--- a/crypto/src/crypto/modes/GCMBlockCipher.cs
+++ b/crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -69,6 +69,11 @@ namespace Org.BouncyCastle.Crypto.Modes
get { return cipher.AlgorithmName + "/GCM"; }
}
+ public IBlockCipher GetUnderlyingCipher()
+ {
+ return cipher;
+ }
+
public virtual int GetBlockSize()
{
return BlockSize;
diff --git a/crypto/src/crypto/modes/IAeadBlockCipher.cs b/crypto/src/crypto/modes/IAeadBlockCipher.cs
index 06bc50488..52c4ff428 100644
--- a/crypto/src/crypto/modes/IAeadBlockCipher.cs
+++ b/crypto/src/crypto/modes/IAeadBlockCipher.cs
@@ -11,6 +11,9 @@ namespace Org.BouncyCastle.Crypto.Modes
/// <summary>The name of the algorithm this cipher implements.</summary>
string AlgorithmName { get; }
+ /// <summary>The block cipher underlying this algorithm.</summary>
+ IBlockCipher GetUnderlyingCipher();
+
/// <summary>Initialise the cipher.</summary>
/// <remarks>Parameter can either be an AeadParameters or a ParametersWithIV object.</remarks>
/// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
|