diff --git a/crypto/src/crypto/engines/IesEngine.cs b/crypto/src/crypto/engines/IesEngine.cs
index 6520c86f8..70df3077c 100644
--- a/crypto/src/crypto/engines/IesEngine.cs
+++ b/crypto/src/crypto/engines/IesEngine.cs
@@ -133,7 +133,7 @@ namespace Org.BouncyCastle.Crypto.Engines
inOff += inLen;
- byte[] T1 = Arrays.Copy(in_enc, inOff, macBuf.Length);
+ byte[] T1 = Arrays.CopyOfRange(in_enc, inOff, inOff + macBuf.Length);
if (!Arrays.ConstantTimeAreEqual(T1, macBuf))
throw (new InvalidCipherTextException("Invalid MAC."));
diff --git a/crypto/src/crypto/macs/Poly1305.cs b/crypto/src/crypto/macs/Poly1305.cs
index 2d453b6ad..1a951ca04 100644
--- a/crypto/src/crypto/macs/Poly1305.cs
+++ b/crypto/src/crypto/macs/Poly1305.cs
@@ -7,266 +7,285 @@ using Org.BouncyCastle.Crypto.Utilities;
namespace Org.BouncyCastle.Crypto.Macs
{
- /// <summary>
- /// Poly1305 message authentication code, designed by D. J. Bernstein.
- /// </summary>
- /// <remarks>
- /// Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key
- /// consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106
- /// effective key bits) used in the authenticator.
- ///
- /// The polynomial calculation in this implementation is adapted from the public domain <a
- /// href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation
- /// by Andrew M (@floodyberry).
- /// </remarks>
- /// <seealso cref="Org.BouncyCastle.Crypto.Generators.Poly1305KeyGenerator"/>
- public class Poly1305
- : IMac
- {
- private const int BLOCK_SIZE = 16;
-
- private readonly IBlockCipher cipher;
-
- private readonly byte[] singleByte = new byte[1];
-
- // Initialised state
-
- /** Polynomial key */
- private uint r0, r1, r2, r3, r4;
-
- /** Precomputed 5 * r[1..4] */
- private uint s1, s2, s3, s4;
-
- /** Encrypted nonce */
- private uint k0, k1, k2, k3;
-
- // Accumulating state
-
- /** Current block of buffered input */
- private byte[] currentBlock = new byte[BLOCK_SIZE];
-
- /** Current offset in input buffer */
- private int currentBlockOffset = 0;
-
- /** Polynomial accumulator */
- private uint h0, h1, h2, h3, h4;
-
- /**
- * Constructs a Poly1305 MAC, using a 128 bit block cipher.
- */
- public Poly1305(IBlockCipher cipher)
- {
- if (cipher.GetBlockSize() != BLOCK_SIZE)
- {
- throw new ArgumentException("Poly1305 requires a 128 bit block cipher.");
- }
- this.cipher = cipher;
- }
-
- /// <summary>
- /// Initialises the Poly1305 MAC.
- /// </summary>
- /// <param name="parameters">a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with
- /// a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.</param>
- public void Init(ICipherParameters parameters)
- {
- byte[] nonce;
- byte[] key;
- if ((parameters is ParametersWithIV) && ((ParametersWithIV)parameters).Parameters is KeyParameter)
- {
- nonce = ((ParametersWithIV)parameters).GetIV();
- key = ((KeyParameter)((ParametersWithIV)parameters).Parameters).GetKey();
- }
- else
- {
- throw new ArgumentException("Poly1305 requires a key and and IV.");
- }
-
- setKey(key, nonce);
- Reset();
- }
-
- private void setKey(byte[] key, byte[] nonce)
- {
- if (nonce.Length != BLOCK_SIZE)
- {
- throw new ArgumentException("Poly1305 requires a 128 bit IV.");
- }
- Poly1305KeyGenerator.CheckKey(key);
-
- // Extract r portion of key
- uint t0 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 0);
- uint t1 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 4);
- uint t2 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 8);
- uint t3 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 12);
-
- r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6;
- r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12;
- r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18;
- r3 = t2 & 0x3f03fff; t3 >>= 8;
- r4 = t3 & 0x00fffff;
-
- // Precompute multipliers
- s1 = r1 * 5;
- s2 = r2 * 5;
- s3 = r3 * 5;
- s4 = r4 * 5;
-
- // Compute encrypted nonce
- byte[] cipherKey = new byte[BLOCK_SIZE];
- Array.Copy(key, 0, cipherKey, 0, cipherKey.Length);
-
- cipher.Init(true, new KeyParameter(cipherKey));
- cipher.ProcessBlock(nonce, 0, cipherKey, 0);
-
- k0 = Pack.LE_To_UInt32(cipherKey, 0);
- k1 = Pack.LE_To_UInt32(cipherKey, 4);
- k2 = Pack.LE_To_UInt32(cipherKey, 8);
- k3 = Pack.LE_To_UInt32(cipherKey, 12);
- }
-
- public string AlgorithmName
- {
- get { return "Poly1305-" + cipher.AlgorithmName; }
- }
-
- public int GetMacSize()
- {
- return BLOCK_SIZE;
- }
-
- public void Update(byte input)
- {
- singleByte[0] = input;
- BlockUpdate(singleByte, 0, 1);
- }
-
- public void BlockUpdate(byte[] input, int inOff, int len)
- {
- int copied = 0;
- while (len > copied)
- {
- if (currentBlockOffset == BLOCK_SIZE)
- {
- processBlock();
- currentBlockOffset = 0;
- }
-
- int toCopy = System.Math.Min((len - copied), BLOCK_SIZE - currentBlockOffset);
- Array.Copy(input, copied + inOff, currentBlock, currentBlockOffset, toCopy);
- copied += toCopy;
- currentBlockOffset += toCopy;
- }
-
- }
-
- private void processBlock()
- {
- if (currentBlockOffset < BLOCK_SIZE)
- {
- currentBlock[currentBlockOffset] = 1;
- for (int i = currentBlockOffset + 1; i < BLOCK_SIZE; i++)
- {
- currentBlock[i] = 0;
- }
- }
-
- ulong t0 = Pack.LE_To_UInt32(currentBlock, 0);
- ulong t1 = Pack.LE_To_UInt32(currentBlock, 4);
- ulong t2 = Pack.LE_To_UInt32(currentBlock, 8);
- ulong t3 = Pack.LE_To_UInt32(currentBlock, 12);
-
- h0 += (uint)(t0 & 0x3ffffffU);
- h1 += (uint)((((t1 << 32) | t0) >> 26) & 0x3ffffff);
- h2 += (uint)((((t2 << 32) | t1) >> 20) & 0x3ffffff);
- h3 += (uint)((((t3 << 32) | t2) >> 14) & 0x3ffffff);
- h4 += (uint)(t3 >> 8);
-
- if (currentBlockOffset == BLOCK_SIZE)
- {
- h4 += (1 << 24);
- }
-
- ulong tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
- ulong tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
- ulong tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
- ulong tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
- ulong tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
-
- ulong b;
- h0 = (uint)tp0 & 0x3ffffff; b = (tp0 >> 26);
- tp1 += b; h1 = (uint)tp1 & 0x3ffffff; b = (tp1 >> 26);
- tp2 += b; h2 = (uint)tp2 & 0x3ffffff; b = (tp2 >> 26);
- tp3 += b; h3 = (uint)tp3 & 0x3ffffff; b = (tp3 >> 26);
- tp4 += b; h4 = (uint)tp4 & 0x3ffffff; b = (tp4 >> 26);
- h0 += (uint)(b * 5);
- }
-
- public int DoFinal(byte[] output, int outOff)
- {
- if (outOff + BLOCK_SIZE > output.Length)
- {
- throw new DataLengthException("Output buffer is too short.");
- }
-
- if (currentBlockOffset > 0)
- {
- // Process padded block
- processBlock();
- }
-
- ulong f0, f1, f2, f3;
-
- uint b = h0 >> 26;
- h0 = h0 & 0x3ffffff;
- h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff;
- h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff;
- h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff;
- h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff;
- h0 += b * 5;
-
- uint g0, g1, g2, g3, g4;
- g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff;
- g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff;
- g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff;
- g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff;
- g4 = h4 + b - (1 << 26);
-
- b = (g4 >> 31) - 1;
- uint nb = ~b;
- h0 = (h0 & nb) | (g0 & b);
- h1 = (h1 & nb) | (g1 & b);
- h2 = (h2 & nb) | (g2 & b);
- h3 = (h3 & nb) | (g3 & b);
- h4 = (h4 & nb) | (g4 & b);
-
- f0 = ((h0 ) | (h1 << 26)) + (ulong)k0;
- f1 = ((h1 >> 6 ) | (h2 << 20)) + (ulong)k1;
- f2 = ((h2 >> 12) | (h3 << 14)) + (ulong)k2;
- f3 = ((h3 >> 18) | (h4 << 8 )) + (ulong)k3;
-
- Pack.UInt32_To_LE((uint)f0, output, outOff);
- f1 += (f0 >> 32);
- Pack.UInt32_To_LE((uint)f1, output, outOff + 4);
- f2 += (f1 >> 32);
- Pack.UInt32_To_LE((uint)f2, output, outOff + 8);
- f3 += (f2 >> 32);
- Pack.UInt32_To_LE((uint)f3, output, outOff + 12);
-
- Reset();
- return BLOCK_SIZE;
- }
-
- public void Reset()
- {
- currentBlockOffset = 0;
-
- h0 = h1 = h2 = h3 = h4 = 0;
- }
-
- private static ulong mul32x32_64(uint i1, uint i2)
- {
- return ((ulong)i1) * i2;
- }
- }
+ /// <summary>
+ /// Poly1305 message authentication code, designed by D. J. Bernstein.
+ /// </summary>
+ /// <remarks>
+ /// Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key
+ /// consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106
+ /// effective key bits) used in the authenticator.
+ ///
+ /// The polynomial calculation in this implementation is adapted from the public domain <a
+ /// href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation
+ /// by Andrew M (@floodyberry).
+ /// </remarks>
+ /// <seealso cref="Org.BouncyCastle.Crypto.Generators.Poly1305KeyGenerator"/>
+ public class Poly1305
+ : IMac
+ {
+ private const int BLOCK_SIZE = 16;
+
+ private readonly IBlockCipher cipher;
+
+ private readonly byte[] singleByte = new byte[1];
+
+ // Initialised state
+
+ /** Polynomial key */
+ private uint r0, r1, r2, r3, r4;
+
+ /** Precomputed 5 * r[1..4] */
+ private uint s1, s2, s3, s4;
+
+ /** Encrypted nonce */
+ private uint k0, k1, k2, k3;
+
+ // Accumulating state
+
+ /** Current block of buffered input */
+ private byte[] currentBlock = new byte[BLOCK_SIZE];
+
+ /** Current offset in input buffer */
+ private int currentBlockOffset = 0;
+
+ /** Polynomial accumulator */
+ private uint h0, h1, h2, h3, h4;
+
+ /**
+ * Constructs a Poly1305 MAC, where the key passed to init() will be used directly.
+ */
+ public Poly1305()
+ {
+ this.cipher = null;
+ }
+
+ /**
+ * Constructs a Poly1305 MAC, using a 128 bit block cipher.
+ */
+ public Poly1305(IBlockCipher cipher)
+ {
+ if (cipher.GetBlockSize() != BLOCK_SIZE)
+ {
+ throw new ArgumentException("Poly1305 requires a 128 bit block cipher.");
+ }
+ this.cipher = cipher;
+ }
+
+ /// <summary>
+ /// Initialises the Poly1305 MAC.
+ /// </summary>
+ /// <param name="parameters">a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with
+ /// a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.</param>
+ public void Init(ICipherParameters parameters)
+ {
+ byte[] nonce = null;
+
+ if (cipher != null)
+ {
+ if (!(parameters is ParametersWithIV))
+ throw new ArgumentException("Poly1305 requires an IV when used with a block cipher.", "parameters");
+
+ ParametersWithIV ivParams = (ParametersWithIV)parameters;
+ nonce = ivParams.GetIV();
+ parameters = ivParams.Parameters;
+ }
+
+ if (!(parameters is KeyParameter))
+ throw new ArgumentException("Poly1305 requires a key.");
+
+ KeyParameter keyParams = (KeyParameter)parameters;
+
+ SetKey(keyParams.GetKey(), nonce);
+
+ Reset();
+ }
+
+ private void SetKey(byte[] key, byte[] nonce)
+ {
+ if (cipher != null && (nonce == null || nonce.Length != BLOCK_SIZE))
+ throw new ArgumentException("Poly1305 requires a 128 bit IV.");
+
+ Poly1305KeyGenerator.CheckKey(key);
+
+ // Extract r portion of key
+ uint t0 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 0);
+ uint t1 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 4);
+ uint t2 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 8);
+ uint t3 = Pack.LE_To_UInt32(key, BLOCK_SIZE + 12);
+
+ r0 = t0 & 0x3ffffff; t0 >>= 26; t0 |= t1 << 6;
+ r1 = t0 & 0x3ffff03; t1 >>= 20; t1 |= t2 << 12;
+ r2 = t1 & 0x3ffc0ff; t2 >>= 14; t2 |= t3 << 18;
+ r3 = t2 & 0x3f03fff; t3 >>= 8;
+ r4 = t3 & 0x00fffff;
+
+ // Precompute multipliers
+ s1 = r1 * 5;
+ s2 = r2 * 5;
+ s3 = r3 * 5;
+ s4 = r4 * 5;
+
+ byte[] kBytes;
+ if (cipher == null)
+ {
+ kBytes = key;
+ }
+ else
+ {
+ // Compute encrypted nonce
+ kBytes = new byte[BLOCK_SIZE];
+ cipher.Init(true, new KeyParameter(key, 0, BLOCK_SIZE));
+ cipher.ProcessBlock(nonce, 0, kBytes, 0);
+ }
+
+ k0 = Pack.LE_To_UInt32(kBytes, 0);
+ k1 = Pack.LE_To_UInt32(kBytes, 4);
+ k2 = Pack.LE_To_UInt32(kBytes, 8);
+ k3 = Pack.LE_To_UInt32(kBytes, 12);
+ }
+
+ public string AlgorithmName
+ {
+ get { return cipher == null ? "Poly1305" : "Poly1305-" + cipher.AlgorithmName; }
+ }
+
+ public int GetMacSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public void Update(byte input)
+ {
+ singleByte[0] = input;
+ BlockUpdate(singleByte, 0, 1);
+ }
+
+ public void BlockUpdate(byte[] input, int inOff, int len)
+ {
+ int copied = 0;
+ while (len > copied)
+ {
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ processBlock();
+ currentBlockOffset = 0;
+ }
+
+ int toCopy = System.Math.Min((len - copied), BLOCK_SIZE - currentBlockOffset);
+ Array.Copy(input, copied + inOff, currentBlock, currentBlockOffset, toCopy);
+ copied += toCopy;
+ currentBlockOffset += toCopy;
+ }
+
+ }
+
+ private void processBlock()
+ {
+ if (currentBlockOffset < BLOCK_SIZE)
+ {
+ currentBlock[currentBlockOffset] = 1;
+ for (int i = currentBlockOffset + 1; i < BLOCK_SIZE; i++)
+ {
+ currentBlock[i] = 0;
+ }
+ }
+
+ ulong t0 = Pack.LE_To_UInt32(currentBlock, 0);
+ ulong t1 = Pack.LE_To_UInt32(currentBlock, 4);
+ ulong t2 = Pack.LE_To_UInt32(currentBlock, 8);
+ ulong t3 = Pack.LE_To_UInt32(currentBlock, 12);
+
+ h0 += (uint)(t0 & 0x3ffffffU);
+ h1 += (uint)((((t1 << 32) | t0) >> 26) & 0x3ffffff);
+ h2 += (uint)((((t2 << 32) | t1) >> 20) & 0x3ffffff);
+ h3 += (uint)((((t3 << 32) | t2) >> 14) & 0x3ffffff);
+ h4 += (uint)(t3 >> 8);
+
+ if (currentBlockOffset == BLOCK_SIZE)
+ {
+ h4 += (1 << 24);
+ }
+
+ ulong tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
+ ulong tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
+ ulong tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
+ ulong tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
+ ulong tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
+
+ ulong b;
+ h0 = (uint)tp0 & 0x3ffffff; b = (tp0 >> 26);
+ tp1 += b; h1 = (uint)tp1 & 0x3ffffff; b = (tp1 >> 26);
+ tp2 += b; h2 = (uint)tp2 & 0x3ffffff; b = (tp2 >> 26);
+ tp3 += b; h3 = (uint)tp3 & 0x3ffffff; b = (tp3 >> 26);
+ tp4 += b; h4 = (uint)tp4 & 0x3ffffff; b = (tp4 >> 26);
+ h0 += (uint)(b * 5);
+ }
+
+ public int DoFinal(byte[] output, int outOff)
+ {
+ if (outOff + BLOCK_SIZE > output.Length)
+ {
+ throw new DataLengthException("Output buffer is too short.");
+ }
+
+ if (currentBlockOffset > 0)
+ {
+ // Process padded block
+ processBlock();
+ }
+
+ ulong f0, f1, f2, f3;
+
+ uint b = h0 >> 26;
+ h0 = h0 & 0x3ffffff;
+ h1 += b; b = h1 >> 26; h1 = h1 & 0x3ffffff;
+ h2 += b; b = h2 >> 26; h2 = h2 & 0x3ffffff;
+ h3 += b; b = h3 >> 26; h3 = h3 & 0x3ffffff;
+ h4 += b; b = h4 >> 26; h4 = h4 & 0x3ffffff;
+ h0 += b * 5;
+
+ uint g0, g1, g2, g3, g4;
+ g0 = h0 + 5; b = g0 >> 26; g0 &= 0x3ffffff;
+ g1 = h1 + b; b = g1 >> 26; g1 &= 0x3ffffff;
+ g2 = h2 + b; b = g2 >> 26; g2 &= 0x3ffffff;
+ g3 = h3 + b; b = g3 >> 26; g3 &= 0x3ffffff;
+ g4 = h4 + b - (1 << 26);
+
+ b = (g4 >> 31) - 1;
+ uint nb = ~b;
+ h0 = (h0 & nb) | (g0 & b);
+ h1 = (h1 & nb) | (g1 & b);
+ h2 = (h2 & nb) | (g2 & b);
+ h3 = (h3 & nb) | (g3 & b);
+ h4 = (h4 & nb) | (g4 & b);
+
+ f0 = ((h0 ) | (h1 << 26)) + (ulong)k0;
+ f1 = ((h1 >> 6 ) | (h2 << 20)) + (ulong)k1;
+ f2 = ((h2 >> 12) | (h3 << 14)) + (ulong)k2;
+ f3 = ((h3 >> 18) | (h4 << 8 )) + (ulong)k3;
+
+ Pack.UInt32_To_LE((uint)f0, output, outOff);
+ f1 += (f0 >> 32);
+ Pack.UInt32_To_LE((uint)f1, output, outOff + 4);
+ f2 += (f1 >> 32);
+ Pack.UInt32_To_LE((uint)f2, output, outOff + 8);
+ f3 += (f2 >> 32);
+ Pack.UInt32_To_LE((uint)f3, output, outOff + 12);
+
+ Reset();
+ return BLOCK_SIZE;
+ }
+
+ public void Reset()
+ {
+ currentBlockOffset = 0;
+
+ h0 = h1 = h2 = h3 = h4 = 0;
+ }
+
+ private static ulong mul32x32_64(uint i1, uint i2)
+ {
+ return ((ulong)i1) * i2;
+ }
+ }
}
diff --git a/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs b/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs
new file mode 100644
index 000000000..141ee6507
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsCipherFactory.cs
@@ -0,0 +1,15 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class AbstractTlsCipherFactory
+ : TlsCipherFactory
+ {
+ /// <exception cref="IOException"></exception>
+ public virtual TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/Chacha20Poly1305.cs b/crypto/src/crypto/tls/Chacha20Poly1305.cs
new file mode 100644
index 000000000..e4e4c7ee2
--- /dev/null
+++ b/crypto/src/crypto/tls/Chacha20Poly1305.cs
@@ -0,0 +1,153 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class Chacha20Poly1305
+ : TlsCipher
+ {
+ protected readonly TlsContext context;
+
+ protected readonly ChaChaEngine encryptCipher;
+ protected readonly ChaChaEngine decryptCipher;
+
+ /// <exception cref="IOException"></exception>
+ public Chacha20Poly1305(TlsContext context)
+ {
+ if (!TlsUtilities.IsTlsV12(context))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.context = context;
+
+ byte[] key_block = TlsUtilities.CalculateKeyBlock(context, 64);
+
+ KeyParameter client_write_key = new KeyParameter(key_block, 0, 32);
+ KeyParameter server_write_key = new KeyParameter(key_block, 32, 32);
+
+ this.encryptCipher = new ChaChaEngine(20);
+ this.decryptCipher = new ChaChaEngine(20);
+
+ KeyParameter encryptKey, decryptKey;
+ if (context.IsServer)
+ {
+ encryptKey = server_write_key;
+ decryptKey = client_write_key;
+ }
+ else
+ {
+ encryptKey = client_write_key;
+ decryptKey = server_write_key;
+ }
+
+ byte[] dummyNonce = new byte[8];
+
+ this.encryptCipher.Init(true, new ParametersWithIV(encryptKey, dummyNonce));
+ this.decryptCipher.Init(false, new ParametersWithIV(decryptKey, dummyNonce));
+ }
+
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
+ {
+ return ciphertextLimit - 16;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len)
+ {
+ int ciphertextLength = len + 16;
+
+ KeyParameter macKey = InitRecordMac(encryptCipher, true, seqNo);
+
+ byte[] output = new byte[ciphertextLength];
+ encryptCipher.ProcessBytes(plaintext, offset, len, output, 0);
+
+ byte[] additionalData = GetAdditionalData(seqNo, type, len);
+ byte[] mac = CalculateRecordMac(macKey, additionalData, output, 0, len);
+ Array.Copy(mac, 0, output, len, mac.Length);
+
+ return output;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len)
+ {
+ if (GetPlaintextLimit(len) < 0)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int plaintextLength = len - 16;
+
+ byte[] receivedMAC = Arrays.CopyOfRange(ciphertext, offset + plaintextLength, offset + len);
+
+ KeyParameter macKey = InitRecordMac(decryptCipher, false, seqNo);
+
+ byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength);
+ byte[] calculatedMAC = CalculateRecordMac(macKey, additionalData, ciphertext, offset, plaintextLength);
+
+ if (!Arrays.ConstantTimeAreEqual(calculatedMAC, receivedMAC))
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+
+ byte[] output = new byte[plaintextLength];
+ decryptCipher.ProcessBytes(ciphertext, offset, plaintextLength, output, 0);
+
+ return output;
+ }
+
+ protected virtual KeyParameter InitRecordMac(ChaChaEngine cipher, bool forEncryption, long seqNo)
+ {
+ byte[] nonce = new byte[8];
+ TlsUtilities.WriteUint64(seqNo, nonce, 0);
+
+ cipher.Init(forEncryption, new ParametersWithIV(null, nonce));
+
+ byte[] firstBlock = new byte[64];
+ cipher.ProcessBytes(firstBlock, 0, firstBlock.Length, firstBlock, 0);
+
+ // NOTE: The BC implementation puts 'r' after 'k'
+ Array.Copy(firstBlock, 0, firstBlock, 32, 16);
+ KeyParameter macKey = new KeyParameter(firstBlock, 16, 32);
+ Poly1305KeyGenerator.Clamp(macKey.GetKey());
+ return macKey;
+ }
+
+ protected virtual byte[] CalculateRecordMac(KeyParameter macKey, byte[] additionalData, byte[] buf, int off, int len)
+ {
+ IMac mac = new Poly1305();
+ mac.Init(macKey);
+
+ UpdateRecordMac(mac, additionalData, 0, additionalData.Length);
+ UpdateRecordMac(mac, buf, off, len);
+ return MacUtilities.DoFinal(mac);
+ }
+
+ protected virtual void UpdateRecordMac(IMac mac, byte[] buf, int off, int len)
+ {
+ mac.BlockUpdate(buf, off, len);
+
+ byte[] longLen = Pack.UInt64_To_LE((ulong)len);
+ mac.BlockUpdate(longLen, 0, longLen.Length);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual byte[] GetAdditionalData(long seqNo, byte type, int len)
+ {
+ /*
+ * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version +
+ * TLSCompressed.length
+ */
+ byte[] additional_data = new byte[13];
+ TlsUtilities.WriteUint64(seqNo, additional_data, 0);
+ TlsUtilities.WriteUint8(type, additional_data, 8);
+ TlsUtilities.WriteVersion(context.ServerVersion, additional_data, 9);
+ TlsUtilities.WriteUint16(len, additional_data, 11);
+
+ return additional_data;
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
index cc34b3028..7c4213c25 100644
--- a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
+++ b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
@@ -1,53 +1,146 @@
using System;
using System.IO;
-using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
namespace Org.BouncyCastle.Crypto.Tls
{
public class DefaultTlsCipherFactory
- : TlsCipherFactory
+ : AbstractTlsCipherFactory
{
- public virtual TlsCipher CreateCipher(TlsContext context,
- int encryptionAlgorithm, DigestAlgorithm digestAlgorithm)
+ /// <exception cref="IOException"></exception>
+ public override TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm)
{
switch (encryptionAlgorithm)
{
- case EncryptionAlgorithm.cls_3DES_EDE_CBC:
- return CreateDesEdeCipher(context, 24, digestAlgorithm);
- case EncryptionAlgorithm.AES_128_CBC:
- return CreateAesCipher(context, 16, digestAlgorithm);
- case EncryptionAlgorithm.AES_256_CBC:
- return CreateAesCipher(context, 32, digestAlgorithm);
- case EncryptionAlgorithm.RC4_128:
- return CreateRC4Cipher(context, 16, digestAlgorithm);
- default:
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ case EncryptionAlgorithm.cls_3DES_EDE_CBC:
+ return CreateDesEdeCipher(context, macAlgorithm);
+ case EncryptionAlgorithm.AEAD_CHACHA20_POLY1305:
+ // NOTE: Ignores macAlgorithm
+ return CreateChaCha20Poly1305(context);
+ case EncryptionAlgorithm.AES_128_CBC:
+ return CreateAESCipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.AES_128_CCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(context, 16, 16);
+ case EncryptionAlgorithm.AES_128_CCM_8:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(context, 16, 8);
+ case EncryptionAlgorithm.AES_256_CCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(context, 32, 16);
+ case EncryptionAlgorithm.AES_256_CCM_8:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Ccm(context, 32, 8);
+ case EncryptionAlgorithm.AES_128_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Gcm(context, 16, 16);
+ case EncryptionAlgorithm.AES_256_CBC:
+ return CreateAESCipher(context, 32, macAlgorithm);
+ case EncryptionAlgorithm.AES_256_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Aes_Gcm(context, 32, 16);
+ case EncryptionAlgorithm.CAMELLIA_128_CBC:
+ return CreateCamelliaCipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.CAMELLIA_128_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Camellia_Gcm(context, 16, 16);
+ case EncryptionAlgorithm.CAMELLIA_256_CBC:
+ return CreateCamelliaCipher(context, 32, macAlgorithm);
+ case EncryptionAlgorithm.CAMELLIA_256_GCM:
+ // NOTE: Ignores macAlgorithm
+ return CreateCipher_Camellia_Gcm(context, 32, 16);
+ case EncryptionAlgorithm.ESTREAM_SALSA20:
+ return CreateSalsa20Cipher(context, 12, 32, macAlgorithm);
+ case EncryptionAlgorithm.NULL:
+ return CreateNullCipher(context, macAlgorithm);
+ case EncryptionAlgorithm.RC4_128:
+ return CreateRC4Cipher(context, 16, macAlgorithm);
+ case EncryptionAlgorithm.SALSA20:
+ return CreateSalsa20Cipher(context, 20, 32, macAlgorithm);
+ case EncryptionAlgorithm.SEED_CBC:
+ return CreateSeedCipher(context, macAlgorithm);
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
/// <exception cref="IOException"></exception>
- protected virtual TlsCipher CreateRC4Cipher(TlsContext context, int cipherKeySize, DigestAlgorithm digestAlgorithm)
+ protected virtual TlsBlockCipher CreateAESCipher(TlsContext context, int cipherKeySize, int macAlgorithm)
{
- return new TlsStreamCipher(context, CreateRC4StreamCipher(), CreateRC4StreamCipher(), CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize);
+ return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(),
+ CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize);
}
/// <exception cref="IOException"></exception>
- protected virtual TlsCipher CreateAesCipher(TlsContext context, int cipherKeySize,
- DigestAlgorithm digestAlgorithm)
+ protected virtual TlsBlockCipher CreateCamelliaCipher(TlsContext context, int cipherKeySize, int macAlgorithm)
{
- return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(),
- CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize);
+ return new TlsBlockCipher(context, CreateCamelliaBlockCipher(),
+ CreateCamelliaBlockCipher(), CreateHMacDigest(macAlgorithm),
+ CreateHMacDigest(macAlgorithm), cipherKeySize);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsCipher CreateChaCha20Poly1305(TlsContext context)
+ {
+ return new Chacha20Poly1305(context);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsAeadCipher CreateCipher_Aes_Ccm(TlsContext context, int cipherKeySize, int macSize)
+ {
+ return new TlsAeadCipher(context, CreateAeadBlockCipher_Aes_Ccm(),
+ CreateAeadBlockCipher_Aes_Ccm(), cipherKeySize, macSize);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsAeadCipher CreateCipher_Aes_Gcm(TlsContext context, int cipherKeySize, int macSize)
+ {
+ return new TlsAeadCipher(context, CreateAeadBlockCipher_Aes_Gcm(),
+ CreateAeadBlockCipher_Aes_Gcm(), cipherKeySize, macSize);
}
/// <exception cref="IOException"></exception>
- protected virtual TlsCipher CreateDesEdeCipher(TlsContext context, int cipherKeySize,
- DigestAlgorithm digestAlgorithm)
+ protected virtual TlsAeadCipher CreateCipher_Camellia_Gcm(TlsContext context, int cipherKeySize, int macSize)
+ {
+ return new TlsAeadCipher(context, CreateAeadBlockCipher_Camellia_Gcm(),
+ CreateAeadBlockCipher_Camellia_Gcm(), cipherKeySize, macSize);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsBlockCipher CreateDesEdeCipher(TlsContext context, int macAlgorithm)
{
return new TlsBlockCipher(context, CreateDesEdeBlockCipher(), CreateDesEdeBlockCipher(),
- CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize);
+ CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), 24);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsNullCipher CreateNullCipher(TlsContext context, int macAlgorithm)
+ {
+ return new TlsNullCipher(context, CreateHMacDigest(macAlgorithm),
+ CreateHMacDigest(macAlgorithm));
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsStreamCipher CreateRC4Cipher(TlsContext context, int cipherKeySize, int macAlgorithm)
+ {
+ return new TlsStreamCipher(context, CreateRC4StreamCipher(), CreateRC4StreamCipher(),
+ CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize, false);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsStreamCipher CreateSalsa20Cipher(TlsContext context, int rounds, int cipherKeySize, int macAlgorithm)
+ {
+ return new TlsStreamCipher(context, CreateSalsa20StreamCipher(rounds), CreateSalsa20StreamCipher(rounds),
+ CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), cipherKeySize, true);
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual TlsBlockCipher CreateSeedCipher(TlsContext context, int macAlgorithm)
+ {
+ return new TlsBlockCipher(context, CreateSeedBlockCipher(), CreateSeedBlockCipher(),
+ CreateHMacDigest(macAlgorithm), CreateHMacDigest(macAlgorithm), 16);
}
protected virtual IBlockCipher CreateAesEngine()
@@ -55,11 +148,38 @@ namespace Org.BouncyCastle.Crypto.Tls
return new AesEngine();
}
+ protected virtual IBlockCipher CreateCamelliaEngine()
+ {
+ return new CamelliaEngine();
+ }
+
protected virtual IBlockCipher CreateAesBlockCipher()
{
return new CbcBlockCipher(CreateAesEngine());
}
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Ccm()
+ {
+ return new CcmBlockCipher(CreateAesEngine());
+ }
+
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_Aes_Gcm()
+ {
+ // TODO Consider allowing custom configuration of multiplier
+ return new GcmBlockCipher(CreateAesEngine());
+ }
+
+ protected virtual IAeadBlockCipher CreateAeadBlockCipher_Camellia_Gcm()
+ {
+ // TODO Consider allowing custom configuration of multiplier
+ return new GcmBlockCipher(CreateCamelliaEngine());
+ }
+
+ protected virtual IBlockCipher CreateCamelliaBlockCipher()
+ {
+ return new CbcBlockCipher(CreateCamelliaEngine());
+ }
+
protected virtual IBlockCipher CreateDesEdeBlockCipher()
{
return new CbcBlockCipher(new DesEdeEngine());
@@ -70,21 +190,35 @@ namespace Org.BouncyCastle.Crypto.Tls
return new RC4Engine();
}
+ protected virtual IStreamCipher CreateSalsa20StreamCipher(int rounds)
+ {
+ return new Salsa20Engine(rounds);
+ }
+
+ protected virtual IBlockCipher CreateSeedBlockCipher()
+ {
+ return new CbcBlockCipher(new SeedEngine());
+ }
+
/// <exception cref="IOException"></exception>
- protected virtual IDigest CreateDigest(DigestAlgorithm digestAlgorithm)
+ protected virtual IDigest CreateHMacDigest(int macAlgorithm)
{
- switch (digestAlgorithm)
+ switch (macAlgorithm)
{
- case DigestAlgorithm.MD5:
- return new MD5Digest();
- case DigestAlgorithm.SHA:
- return new Sha1Digest();
- case DigestAlgorithm.SHA256:
- return new Sha256Digest();
- case DigestAlgorithm.SHA384:
- return new Sha384Digest();
- default:
- throw new TlsFatalAlert(AlertDescription.internal_error);
+ case MacAlgorithm.cls_null:
+ return null;
+ case MacAlgorithm.hmac_md5:
+ return TlsUtilities.CreateHash(HashAlgorithm.md5);
+ case MacAlgorithm.hmac_sha1:
+ return TlsUtilities.CreateHash(HashAlgorithm.sha1);
+ case MacAlgorithm.hmac_sha256:
+ return TlsUtilities.CreateHash(HashAlgorithm.sha256);
+ case MacAlgorithm.hmac_sha384:
+ return TlsUtilities.CreateHash(HashAlgorithm.sha384);
+ case MacAlgorithm.hmac_sha512:
+ return TlsUtilities.CreateHash(HashAlgorithm.sha512);
+ default:
+ throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
}
diff --git a/crypto/src/crypto/tls/DefaultTlsClient.cs b/crypto/src/crypto/tls/DefaultTlsClient.cs
index d59fae164..924e6ee2e 100644
--- a/crypto/src/crypto/tls/DefaultTlsClient.cs
+++ b/crypto/src/crypto/tls/DefaultTlsClient.cs
@@ -213,14 +213,15 @@ namespace Org.BouncyCastle.Crypto.Tls
case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, DigestAlgorithm.SHA);
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
+ MacAlgorithm.hmac_sha1);
case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.RC4_128, DigestAlgorithm.SHA);
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
@@ -231,7 +232,8 @@ namespace Org.BouncyCastle.Crypto.Tls
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, DigestAlgorithm.SHA);
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
+ MacAlgorithm.hmac_sha1);
case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
@@ -242,7 +244,8 @@ namespace Org.BouncyCastle.Crypto.Tls
case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, DigestAlgorithm.SHA);
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
+ MacAlgorithm.hmac_sha1);
default:
/*
diff --git a/crypto/src/crypto/tls/PskTlsClient.cs b/crypto/src/crypto/tls/PskTlsClient.cs
index e60688155..f6d83b5be 100644
--- a/crypto/src/crypto/tls/PskTlsClient.cs
+++ b/crypto/src/crypto/tls/PskTlsClient.cs
@@ -163,25 +163,25 @@ namespace Org.BouncyCastle.Crypto.Tls
case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
- DigestAlgorithm.SHA);
+ MacAlgorithm.hmac_sha1);
case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
- DigestAlgorithm.SHA);
+ MacAlgorithm.hmac_sha1);
case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
- DigestAlgorithm.SHA);
+ MacAlgorithm.hmac_sha1);
case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
return cipherFactory.CreateCipher(context, EncryptionAlgorithm.RC4_128,
- DigestAlgorithm.SHA);
+ MacAlgorithm.hmac_sha1);
default:
/*
diff --git a/crypto/src/crypto/tls/RecordStream.cs b/crypto/src/crypto/tls/RecordStream.cs
index ce8882cbe..d05c6c1c2 100644
--- a/crypto/src/crypto/tls/RecordStream.cs
+++ b/crypto/src/crypto/tls/RecordStream.cs
@@ -14,8 +14,11 @@ namespace Org.BouncyCastle.Crypto.Tls
private TlsCompression writeCompression = null;
private TlsCipher readCipher = null;
private TlsCipher writeCipher = null;
+ private long readSeqNo = 0, writeSeqNo = 0;
private MemoryStream buffer = new MemoryStream();
+ private TlsContext context = null;
+
internal RecordStream(
TlsProtocolHandler handler,
Stream inStr,
@@ -24,23 +27,30 @@ namespace Org.BouncyCastle.Crypto.Tls
this.handler = handler;
this.inStr = inStr;
this.outStr = outStr;
- this.hash = new CombinedHash();
this.readCompression = new TlsNullCompression();
this.writeCompression = this.readCompression;
- this.readCipher = new TlsNullCipher();
+ }
+
+ internal void Init(TlsContext context)
+ {
+ this.context = context;
+ this.readCipher = new TlsNullCipher(context);
this.writeCipher = this.readCipher;
+ this.hash = new CombinedHash();
}
internal void ClientCipherSpecDecided(TlsCompression tlsCompression, TlsCipher tlsCipher)
{
this.writeCompression = tlsCompression;
this.writeCipher = tlsCipher;
+ this.writeSeqNo = 0;
}
internal void ServerClientSpecReceived()
{
this.readCompression = this.writeCompression;
this.readCipher = this.writeCipher;
+ this.readSeqNo = 0;
}
public void ReadData()
@@ -59,7 +69,7 @@ namespace Org.BouncyCastle.Crypto.Tls
{
byte[] buf = new byte[len];
TlsUtilities.ReadFully(buf, inStr);
- byte[] decoded = readCipher.DecodeCiphertext(contentType, buf, 0, buf.Length);
+ byte[] decoded = readCipher.DecodeCiphertext(readSeqNo++, contentType, buf, 0, buf.Length);
Stream cOut = readCompression.Decompress(buffer);
@@ -91,13 +101,13 @@ namespace Org.BouncyCastle.Crypto.Tls
byte[] ciphertext;
if (cOut == buffer)
{
- ciphertext = writeCipher.EncodePlaintext(type, message, offset, len);
+ ciphertext = writeCipher.EncodePlaintext(writeSeqNo++, type, message, offset, len);
}
else
{
cOut.Write(message, offset, len);
cOut.Flush();
- ciphertext = writeCipher.EncodePlaintext(type, buffer.GetBuffer(), 0, (int)buffer.Position);
+ ciphertext = writeCipher.EncodePlaintext(writeSeqNo++, type, buffer.GetBuffer(), 0, (int)buffer.Position);
buffer.SetLength(0);
}
diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs
index 0707f3d40..de8c6f13a 100644
--- a/crypto/src/crypto/tls/SecurityParameters.cs
+++ b/crypto/src/crypto/tls/SecurityParameters.cs
@@ -6,18 +6,27 @@ namespace Org.BouncyCastle.Crypto.Tls
{
public class SecurityParameters
{
+ internal int entity = -1;
+ internal int cipherSuite = -1;
+ internal byte compressionAlgorithm = CompressionMethod.NULL;
internal int prfAlgorithm = -1;
+ internal int verifyDataLength = -1;
internal byte[] masterSecret = null;
internal byte[] clientRandom = null;
internal byte[] serverRandom = null;
+ // TODO Keep these internal, since it's maybe not the ideal place for them
+ internal short maxFragmentLength = -1;
+ internal bool truncatedHMac = false;
+ internal bool encryptThenMac = false;
+
internal void CopySessionParametersFrom(SecurityParameters other)
{
- //this.entity = other.entity;
- //this.cipherSuite = other.cipherSuite;
- //this.compressionAlgorithm = other.compressionAlgorithm;
+ this.entity = other.entity;
+ this.cipherSuite = other.cipherSuite;
+ this.compressionAlgorithm = other.compressionAlgorithm;
this.prfAlgorithm = other.prfAlgorithm;
- //this.verifyDataLength = other.verifyDataLength;
+ this.verifyDataLength = other.verifyDataLength;
this.masterSecret = Arrays.Clone(other.masterSecret);
}
@@ -31,6 +40,30 @@ namespace Org.BouncyCastle.Crypto.Tls
}
/**
+ * @return {@link ConnectionEnd}
+ */
+ public virtual int Entity
+ {
+ get { return entity; }
+ }
+
+ /**
+ * @return {@link CipherSuite}
+ */
+ public virtual int CipherSuite
+ {
+ get { return cipherSuite; }
+ }
+
+ /**
+ * @return {@link CompressionMethod}
+ */
+ public byte CompressionAlgorithm
+ {
+ get { return compressionAlgorithm; }
+ }
+
+ /**
* @return {@link PRFAlgorithm}
*/
public virtual int PrfAlgorithm
@@ -38,6 +71,11 @@ namespace Org.BouncyCastle.Crypto.Tls
get { return prfAlgorithm; }
}
+ public virtual int VerifyDataLength
+ {
+ get { return verifyDataLength; }
+ }
+
public virtual byte[] MasterSecret
{
get { return masterSecret; }
diff --git a/crypto/src/crypto/tls/SrpTlsClient.cs b/crypto/src/crypto/tls/SrpTlsClient.cs
index 3769fc85d..dfd7603b8 100644
--- a/crypto/src/crypto/tls/SrpTlsClient.cs
+++ b/crypto/src/crypto/tls/SrpTlsClient.cs
@@ -168,17 +168,20 @@ namespace Org.BouncyCastle.Crypto.Tls
case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, DigestAlgorithm.SHA);
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
+ MacAlgorithm.hmac_sha1);
case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, DigestAlgorithm.SHA);
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
+ MacAlgorithm.hmac_sha1);
case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
- return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, DigestAlgorithm.SHA);
+ return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
+ MacAlgorithm.hmac_sha1);
default:
/*
diff --git a/crypto/src/crypto/tls/Ssl3Mac.cs b/crypto/src/crypto/tls/Ssl3Mac.cs
index b2f3f309e..8bdb342dc 100644
--- a/crypto/src/crypto/tls/Ssl3Mac.cs
+++ b/crypto/src/crypto/tls/Ssl3Mac.cs
@@ -6,109 +6,105 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
- /**
- * HMAC implementation based on original internet draft for HMAC (RFC 2104)
- *
- * The difference is that padding is concatentated versus XORed with the key
- *
- * H(K + opad, H(K + ipad, text))
- */
- public class Ssl3Mac
- : IMac
- {
- private const byte IPAD = 0x36;
- private const byte OPAD = 0x5C;
-
- internal static readonly byte[] MD5_IPAD = GenPad(IPAD, 48);
- internal static readonly byte[] MD5_OPAD = GenPad(OPAD, 48);
- internal static readonly byte[] SHA1_IPAD = GenPad(IPAD, 40);
- internal static readonly byte[] SHA1_OPAD = GenPad(OPAD, 40);
-
- private IDigest digest;
-
- private byte[] secret;
- private byte[] ipad, opad;
-
- /**
- * Base constructor for one of the standard digest algorithms that the byteLength of
- * the algorithm is know for. Behaviour is undefined for digests other than MD5 or SHA1.
- *
- * @param digest the digest.
- */
- public Ssl3Mac(IDigest digest)
- {
- this.digest = digest;
-
- if (digest.GetDigestSize() == 20)
- {
- this.ipad = SHA1_IPAD;
- this.opad = SHA1_OPAD;
- }
- else
- {
- this.ipad = MD5_IPAD;
- this.opad = MD5_OPAD;
- }
- }
-
- public virtual string AlgorithmName
- {
- get { return digest.AlgorithmName + "/SSL3MAC"; }
- }
-
- public virtual void Init(ICipherParameters parameters)
- {
- secret = Arrays.Clone(((KeyParameter)parameters).GetKey());
-
- Reset();
- }
-
- public virtual int GetMacSize()
- {
- return digest.GetDigestSize();
- }
-
- public virtual void Update(byte input)
- {
- digest.Update(input);
- }
-
- public virtual void BlockUpdate(byte[] input, int inOff, int len)
- {
- digest.BlockUpdate(input, inOff, len);
- }
-
- public virtual int DoFinal(byte[] output, int outOff)
- {
- byte[] tmp = new byte[digest.GetDigestSize()];
- digest.DoFinal(tmp, 0);
-
- digest.BlockUpdate(secret, 0, secret.Length);
- digest.BlockUpdate(opad, 0, opad.Length);
- digest.BlockUpdate(tmp, 0, tmp.Length);
-
- int len = digest.DoFinal(output, outOff);
-
- Reset();
-
- return len;
- }
-
- /**
- * Reset the mac generator.
- */
- public virtual void Reset()
- {
- digest.Reset();
- digest.BlockUpdate(secret, 0, secret.Length);
- digest.BlockUpdate(ipad, 0, ipad.Length);
- }
-
- private static byte[] GenPad(byte b, int count)
- {
- byte[] padding = new byte[count];
- Arrays.Fill(padding, b);
- return padding;
- }
- }
+ /**
+ * HMAC implementation based on original internet draft for HMAC (RFC 2104)
+ *
+ * The difference is that padding is concatentated versus XORed with the key
+ *
+ * H(K + opad, H(K + ipad, text))
+ */
+ public class Ssl3Mac
+ : IMac
+ {
+ private const byte IPAD_BYTE = 0x36;
+ private const byte OPAD_BYTE = 0x5C;
+
+ internal static readonly byte[] IPAD = GenPad(IPAD_BYTE, 48);
+ internal static readonly byte[] OPAD = GenPad(OPAD_BYTE, 48);
+
+ private readonly IDigest digest;
+ private readonly int padLength;
+
+ private byte[] secret;
+
+ /**
+ * Base constructor for one of the standard digest algorithms that the byteLength of
+ * the algorithm is know for. Behaviour is undefined for digests other than MD5 or SHA1.
+ *
+ * @param digest the digest.
+ */
+ public Ssl3Mac(IDigest digest)
+ {
+ this.digest = digest;
+
+ if (digest.GetDigestSize() == 20)
+ {
+ this.padLength = 40;
+ }
+ else
+ {
+ this.padLength = 48;
+ }
+ }
+
+ public virtual string AlgorithmName
+ {
+ get { return digest.AlgorithmName + "/SSL3MAC"; }
+ }
+
+ public virtual void Init(ICipherParameters parameters)
+ {
+ secret = Arrays.Clone(((KeyParameter)parameters).GetKey());
+
+ Reset();
+ }
+
+ public virtual int GetMacSize()
+ {
+ return digest.GetDigestSize();
+ }
+
+ public virtual void Update(byte input)
+ {
+ digest.Update(input);
+ }
+
+ public virtual void BlockUpdate(byte[] input, int inOff, int len)
+ {
+ digest.BlockUpdate(input, inOff, len);
+ }
+
+ public virtual int DoFinal(byte[] output, int outOff)
+ {
+ byte[] tmp = new byte[digest.GetDigestSize()];
+ digest.DoFinal(tmp, 0);
+
+ digest.BlockUpdate(secret, 0, secret.Length);
+ digest.BlockUpdate(OPAD, 0, padLength);
+ digest.BlockUpdate(tmp, 0, tmp.Length);
+
+ int len = digest.DoFinal(output, outOff);
+
+ Reset();
+
+ return len;
+ }
+
+ /**
+ * Reset the mac generator.
+ */
+ public virtual void Reset()
+ {
+ digest.Reset();
+ digest.BlockUpdate(secret, 0, secret.Length);
+ digest.BlockUpdate(IPAD, 0, padLength);
+ }
+
+ private static byte[] GenPad(byte b, int count)
+ {
+ byte[] padding = new byte[count];
+ Arrays.Fill(padding, b);
+ return padding;
+ }
+ }
}
diff --git a/crypto/src/crypto/tls/TlsAeadCipher.cs b/crypto/src/crypto/tls/TlsAeadCipher.cs
new file mode 100644
index 000000000..e66f92317
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsAeadCipher.cs
@@ -0,0 +1,189 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public class TlsAeadCipher
+ : TlsCipher
+ {
+ protected readonly TlsContext context;
+ protected readonly int macSize;
+ protected readonly int nonce_explicit_length;
+
+ protected readonly IAeadBlockCipher encryptCipher;
+ protected readonly IAeadBlockCipher decryptCipher;
+
+ protected readonly byte[] encryptImplicitNonce, decryptImplicitNonce;
+
+ /// <exception cref="IOException"></exception>
+ public TlsAeadCipher(TlsContext context, IAeadBlockCipher clientWriteCipher, IAeadBlockCipher serverWriteCipher,
+ int cipherKeySize, int macSize)
+ {
+ if (!TlsUtilities.IsTlsV12(context))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.context = context;
+ this.macSize = macSize;
+
+ // NOTE: Valid for RFC 5288/6655 ciphers but may need review for other AEAD ciphers
+ this.nonce_explicit_length = 8;
+
+ // TODO SecurityParameters.fixed_iv_length
+ int fixed_iv_length = 4;
+
+ int key_block_size = (2 * cipherKeySize) + (2 * fixed_iv_length);
+
+ byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size);
+
+ int offset = 0;
+
+ KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ byte[] client_write_IV = Arrays.CopyOfRange(key_block, offset, offset + fixed_iv_length);
+ offset += fixed_iv_length;
+ byte[] server_write_IV = Arrays.CopyOfRange(key_block, offset, offset + fixed_iv_length);
+ offset += fixed_iv_length;
+
+ if (offset != key_block_size)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ KeyParameter encryptKey, decryptKey;
+ if (context.IsServer)
+ {
+ this.encryptCipher = serverWriteCipher;
+ this.decryptCipher = clientWriteCipher;
+ this.encryptImplicitNonce = server_write_IV;
+ this.decryptImplicitNonce = client_write_IV;
+ encryptKey = server_write_key;
+ decryptKey = client_write_key;
+ }
+ else
+ {
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
+ this.encryptImplicitNonce = client_write_IV;
+ this.decryptImplicitNonce = server_write_IV;
+ encryptKey = client_write_key;
+ decryptKey = server_write_key;
+ }
+
+ byte[] dummyNonce = new byte[fixed_iv_length + nonce_explicit_length];
+
+ this.encryptCipher.Init(true, new AeadParameters(encryptKey, 8 * macSize, dummyNonce));
+ this.decryptCipher.Init(false, new AeadParameters(decryptKey, 8 * macSize, dummyNonce));
+ }
+
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
+ {
+ // TODO We ought to be able to ask the decryptCipher (independently of it's current state!)
+ return ciphertextLimit - macSize - nonce_explicit_length;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len)
+ {
+ byte[] nonce = new byte[this.encryptImplicitNonce.Length + nonce_explicit_length];
+ Array.Copy(encryptImplicitNonce, 0, nonce, 0, encryptImplicitNonce.Length);
+
+ /*
+ * RFC 5288/6655 The nonce_explicit MAY be the 64-bit sequence number.
+ *
+ * (May need review for other AEAD ciphers).
+ */
+ TlsUtilities.WriteUint64(seqNo, nonce, encryptImplicitNonce.Length);
+
+ int plaintextOffset = offset;
+ int plaintextLength = len;
+ int ciphertextLength = encryptCipher.GetOutputSize(plaintextLength);
+
+ byte[] output = new byte[nonce_explicit_length + ciphertextLength];
+ Array.Copy(nonce, encryptImplicitNonce.Length, output, 0, nonce_explicit_length);
+ int outputPos = nonce_explicit_length;
+
+ byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength);
+ AeadParameters parameters = new AeadParameters(null, 8 * macSize, nonce, additionalData);
+
+ try
+ {
+ encryptCipher.Init(true, parameters);
+ outputPos += encryptCipher.ProcessBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos);
+ outputPos += encryptCipher.DoFinal(output, outputPos);
+ }
+ catch (Exception)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ if (outputPos != output.Length)
+ {
+ // NOTE: Existing AEAD cipher implementations all give exact output lengths
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return output;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len)
+ {
+ if (GetPlaintextLimit(len) < 0)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ byte[] nonce = new byte[this.decryptImplicitNonce.Length + nonce_explicit_length];
+ Array.Copy(decryptImplicitNonce, 0, nonce, 0, decryptImplicitNonce.Length);
+ Array.Copy(ciphertext, offset, nonce, decryptImplicitNonce.Length, nonce_explicit_length);
+
+ int ciphertextOffset = offset + nonce_explicit_length;
+ int ciphertextLength = len - nonce_explicit_length;
+ int plaintextLength = decryptCipher.GetOutputSize(ciphertextLength);
+
+ byte[] output = new byte[plaintextLength];
+ int outputPos = 0;
+
+ byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength);
+ AeadParameters parameters = new AeadParameters(null, 8 * macSize, nonce, additionalData);
+
+ try
+ {
+ decryptCipher.Init(false, parameters);
+ outputPos += decryptCipher.ProcessBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos);
+ outputPos += decryptCipher.DoFinal(output, outputPos);
+ }
+ catch (Exception)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+
+ if (outputPos != output.Length)
+ {
+ // NOTE: Existing AEAD cipher implementations all give exact output lengths
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+
+ return output;
+ }
+
+ /// <exception cref="IOException"></exception>
+ protected virtual byte[] GetAdditionalData(long seqNo, byte type, int len)
+ {
+ /*
+ * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version +
+ * TLSCompressed.length
+ */
+
+ byte[] additional_data = new byte[13];
+ TlsUtilities.WriteUint64(seqNo, additional_data, 0);
+ TlsUtilities.WriteUint8(type, additional_data, 8);
+ TlsUtilities.WriteVersion(context.ServerVersion, additional_data, 9);
+ TlsUtilities.WriteUint16(len, additional_data, 11);
+
+ return additional_data;
+ }
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsBlockCipher.cs b/crypto/src/crypto/tls/TlsBlockCipher.cs
index b2c69127c..82c0318b2 100644
--- a/crypto/src/crypto/tls/TlsBlockCipher.cs
+++ b/crypto/src/crypto/tls/TlsBlockCipher.cs
@@ -1,8 +1,6 @@
using System;
using System.IO;
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
@@ -10,169 +8,302 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
/// <summary>
- /// A generic TLS 1.0 block cipher. This can be used for AES or 3DES for example.
+ /// A generic TLS 1.0-1.2 / SSLv3 block cipher. This can be used for AES or 3DES for example.
/// </summary>
public class TlsBlockCipher
- : TlsCipher
+ : TlsCipher
{
- protected TlsContext context;
- protected byte[] randomData;
+ protected readonly TlsContext context;
+ protected readonly byte[] randomData;
+ protected readonly bool useExplicitIV;
+ protected readonly bool encryptThenMac;
- protected IBlockCipher encryptCipher;
- protected IBlockCipher decryptCipher;
+ protected readonly IBlockCipher encryptCipher;
+ protected readonly IBlockCipher decryptCipher;
- protected TlsMac wMac;
- protected TlsMac rMac;
+ protected readonly TlsMac mWriteMac;
+ protected readonly TlsMac mReadMac;
public virtual TlsMac WriteMac
{
- get { return wMac; }
+ get { return mWriteMac; }
}
public virtual TlsMac ReadMac
{
- get { return rMac; }
+ get { return mReadMac; }
}
- public TlsBlockCipher(TlsContext context, IBlockCipher encryptCipher,
- IBlockCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize)
+ /// <exception cref="IOException"></exception>
+ public TlsBlockCipher(TlsContext context, IBlockCipher clientWriteCipher, IBlockCipher serverWriteCipher,
+ IDigest clientWriteDigest, IDigest serverWriteDigest, int cipherKeySize)
{
this.context = context;
this.randomData = new byte[256];
- context.SecureRandom.NextBytes(randomData);
+ context.NonceRandomGenerator.NextBytes(randomData);
- this.encryptCipher = encryptCipher;
- this.decryptCipher = decryptCipher;
+ this.useExplicitIV = TlsUtilities.IsTlsV11(context);
+ this.encryptThenMac = context.SecurityParameters.encryptThenMac;
- int prfSize = (2 * cipherKeySize) + writeDigest.GetDigestSize()
- + readDigest.GetDigestSize() + encryptCipher.GetBlockSize()
- + decryptCipher.GetBlockSize();
+ int key_block_size = (2 * cipherKeySize) + clientWriteDigest.GetDigestSize()
+ + serverWriteDigest.GetDigestSize();
- SecurityParameters securityParameters = context.SecurityParameters;
+ // From TLS 1.1 onwards, block ciphers don't need client_write_IV
+ if (!useExplicitIV)
+ {
+ key_block_size += clientWriteCipher.GetBlockSize() + serverWriteCipher.GetBlockSize();
+ }
- byte[] keyBlock = TlsUtilities.PRF(context, securityParameters.masterSecret, ExporterLabel.key_expansion,
- TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom),
- prfSize);
+ byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size);
int offset = 0;
- // Init MACs
- wMac = CreateTlsMac(writeDigest, keyBlock, ref offset);
- rMac = CreateTlsMac(readDigest, keyBlock, ref offset);
+ TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset,
+ clientWriteDigest.GetDigestSize());
+ offset += clientWriteDigest.GetDigestSize();
+ TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset,
+ serverWriteDigest.GetDigestSize());
+ offset += serverWriteDigest.GetDigestSize();
- // Build keys
- KeyParameter encryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
- KeyParameter decryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
+ KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
- // Add IVs
- ParametersWithIV encryptParams = CreateParametersWithIV(encryptKey,
- keyBlock, ref offset, encryptCipher.GetBlockSize());
- ParametersWithIV decryptParams = CreateParametersWithIV(decryptKey,
- keyBlock, ref offset, decryptCipher.GetBlockSize());
+ byte[] client_write_IV, server_write_IV;
+ if (useExplicitIV)
+ {
+ client_write_IV = new byte[clientWriteCipher.GetBlockSize()];
+ server_write_IV = new byte[serverWriteCipher.GetBlockSize()];
+ }
+ else
+ {
+ client_write_IV = Arrays.CopyOfRange(key_block, offset, offset + clientWriteCipher.GetBlockSize());
+ offset += clientWriteCipher.GetBlockSize();
+ server_write_IV = Arrays.CopyOfRange(key_block, offset, offset + serverWriteCipher.GetBlockSize());
+ offset += serverWriteCipher.GetBlockSize();
+ }
- if (offset != prfSize)
+ if (offset != key_block_size)
+ {
throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
- // Init Ciphers
- encryptCipher.Init(true, encryptParams);
- decryptCipher.Init(false, decryptParams);
- }
+ ICipherParameters encryptParams, decryptParams;
+ if (context.IsServer)
+ {
+ this.mWriteMac = serverWriteMac;
+ this.mReadMac = clientWriteMac;
+ this.encryptCipher = serverWriteCipher;
+ this.decryptCipher = clientWriteCipher;
+ encryptParams = new ParametersWithIV(server_write_key, server_write_IV);
+ decryptParams = new ParametersWithIV(client_write_key, client_write_IV);
+ }
+ else
+ {
+ this.mWriteMac = clientWriteMac;
+ this.mReadMac = serverWriteMac;
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
+ encryptParams = new ParametersWithIV(client_write_key, client_write_IV);
+ decryptParams = new ParametersWithIV(server_write_key, server_write_IV);
+ }
- protected virtual TlsMac CreateTlsMac(IDigest digest, byte[] buf, ref int off)
- {
- int len = digest.GetDigestSize();
- TlsMac mac = new TlsMac(digest, buf, off, len);
- off += len;
- return mac;
+ this.encryptCipher.Init(true, encryptParams);
+ this.decryptCipher.Init(false, decryptParams);
}
- protected virtual KeyParameter CreateKeyParameter(byte[] buf, ref int off, int len)
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
{
- KeyParameter key = new KeyParameter(buf, off, len);
- off += len;
- return key;
- }
+ int blockSize = encryptCipher.GetBlockSize();
+ int macSize = mWriteMac.Size;
- protected virtual ParametersWithIV CreateParametersWithIV(KeyParameter key,
- byte[] buf, ref int off, int len)
- {
- ParametersWithIV ivParams = new ParametersWithIV(key, buf, off, len);
- off += len;
- return ivParams;
+ int plaintextLimit = ciphertextLimit;
+
+ // An explicit IV consumes 1 block
+ if (useExplicitIV)
+ {
+ plaintextLimit -= blockSize;
+ }
+
+ // Leave room for the MAC, and require block-alignment
+ if (encryptThenMac)
+ {
+ plaintextLimit -= macSize;
+ plaintextLimit -= plaintextLimit % blockSize;
+ }
+ else
+ {
+ plaintextLimit -= plaintextLimit % blockSize;
+ plaintextLimit -= macSize;
+ }
+
+ // Minimum 1 byte of padding
+ --plaintextLimit;
+
+ return plaintextLimit;
}
- public virtual byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len)
+ public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len)
{
- int blocksize = encryptCipher.GetBlockSize();
- int padding_length = blocksize - 1 - ((len + wMac.Size) % blocksize);
+ int blockSize = encryptCipher.GetBlockSize();
+ int macSize = mWriteMac.Size;
+
+ ProtocolVersion version = context.ServerVersion;
+
+ int enc_input_length = len;
+ if (!encryptThenMac)
+ {
+ enc_input_length += macSize;
+ }
- //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion;
- bool isTls = true;
+ int padding_length = blockSize - 1 - (enc_input_length % blockSize);
- if (isTls)
+ // TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though)
+ if (!version.IsDtls && !version.IsSsl)
{
// Add a random number of extra blocks worth of padding
- int maxExtraPadBlocks = (255 - padding_length) / blocksize;
+ int maxExtraPadBlocks = (255 - padding_length) / blockSize;
int actualExtraPadBlocks = ChooseExtraPadBlocks(context.SecureRandom, maxExtraPadBlocks);
- padding_length += actualExtraPadBlocks * blocksize;
+ padding_length += actualExtraPadBlocks * blockSize;
+ }
+
+ int totalSize = len + macSize + padding_length + 1;
+ if (useExplicitIV)
+ {
+ totalSize += blockSize;
+ }
+
+ byte[] outBuf = new byte[totalSize];
+ int outOff = 0;
+
+ if (useExplicitIV)
+ {
+ byte[] explicitIV = new byte[blockSize];
+ context.NonceRandomGenerator.NextBytes(explicitIV);
+
+ encryptCipher.Init(true, new ParametersWithIV(null, explicitIV));
+
+ Array.Copy(explicitIV, 0, outBuf, outOff, blockSize);
+ outOff += blockSize;
+ }
+
+ int blocks_start = outOff;
+
+ Array.Copy(plaintext, offset, outBuf, outOff, len);
+ outOff += len;
+
+ if (!encryptThenMac)
+ {
+ byte[] mac = mWriteMac.CalculateMac(seqNo, type, plaintext, offset, len);
+ Array.Copy(mac, 0, outBuf, outOff, mac.Length);
+ outOff += mac.Length;
}
- int totalsize = len + wMac.Size + padding_length + 1;
- byte[] outbuf = new byte[totalsize];
- Array.Copy(plaintext, offset, outbuf, 0, len);
- byte[] mac = wMac.CalculateMac(type, plaintext, offset, len);
- Array.Copy(mac, 0, outbuf, len, mac.Length);
- int paddoffset = len + mac.Length;
for (int i = 0; i <= padding_length; i++)
{
- outbuf[i + paddoffset] = (byte)padding_length;
+ outBuf[outOff++] = (byte)padding_length;
}
- for (int i = 0; i < totalsize; i += blocksize)
+
+ for (int i = blocks_start; i < outOff; i += blockSize)
+ {
+ encryptCipher.ProcessBlock(outBuf, i, outBuf, i);
+ }
+
+ if (encryptThenMac)
{
- encryptCipher.ProcessBlock(outbuf, i, outbuf, i);
+ byte[] mac = mWriteMac.CalculateMac(seqNo, type, outBuf, 0, outOff);
+ Array.Copy(mac, 0, outBuf, outOff, mac.Length);
+ outOff += mac.Length;
}
- return outbuf;
+
+ // assert outBuf.length == outOff;
+
+ return outBuf;
}
- public virtual byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len)
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len)
{
int blockSize = decryptCipher.GetBlockSize();
- int macSize = rMac.Size;
+ int macSize = mReadMac.Size;
+
+ int minLen = blockSize;
+ if (encryptThenMac)
+ {
+ minLen += macSize;
+ }
+ else
+ {
+ minLen = System.Math.Max(minLen, macSize + 1);
+ }
- /*
- * TODO[TLS 1.1] Explicit IV implies minLen = blockSize + max(blockSize, macSize + 1),
- * and will need further changes to offset and plen variables below.
- */
+ if (useExplicitIV)
+ {
+ minLen += blockSize;
+ }
- int minLen = System.Math.Max(blockSize, macSize + 1);
if (len < minLen)
throw new TlsFatalAlert(AlertDescription.decode_error);
- if (len % blockSize != 0)
+ int blocks_length = len;
+ if (encryptThenMac)
+ {
+ blocks_length -= macSize;
+ }
+
+ if (blocks_length % blockSize != 0)
throw new TlsFatalAlert(AlertDescription.decryption_failed);
- for (int i = 0; i < len; i += blockSize)
+ if (encryptThenMac)
{
- decryptCipher.ProcessBlock(ciphertext, offset + i, ciphertext, offset + i);
+ int end = offset + len;
+ byte[] receivedMac = Arrays.CopyOfRange(ciphertext, end - macSize, end);
+ byte[] calculatedMac = mReadMac.CalculateMac(seqNo, type, ciphertext, offset, len - macSize);
+
+ bool badMac = !Arrays.ConstantTimeAreEqual(calculatedMac, receivedMac);
+
+ if (badMac)
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+
+ if (useExplicitIV)
+ {
+ decryptCipher.Init(false, new ParametersWithIV(null, ciphertext, offset, blockSize));
+
+ offset += blockSize;
+ blocks_length -= blockSize;
}
- int plen = len;
+ for (int i = 0; i < blocks_length; i += blockSize)
+ {
+ decryptCipher.ProcessBlock(ciphertext, offset + i, ciphertext, offset + i);
+ }
// If there's anything wrong with the padding, this will return zero
- int totalPad = CheckPaddingConstantTime(ciphertext, offset, plen, blockSize, macSize);
+ int totalPad = CheckPaddingConstantTime(ciphertext, offset, blocks_length, blockSize, encryptThenMac ? 0 : macSize);
- int macInputLen = plen - totalPad - macSize;
+ int dec_output_length = blocks_length - totalPad;
- byte[] decryptedMac = Arrays.Copy(ciphertext, offset + macInputLen, macSize);
- byte[] calculatedMac = rMac.CalculateMacConstantTime(type, ciphertext, offset, macInputLen, plen - macSize, randomData);
+ if (!encryptThenMac)
+ {
+ dec_output_length -= macSize;
+ int macInputLen = dec_output_length;
+ int macOff = offset + macInputLen;
+ byte[] receivedMac = Arrays.CopyOfRange(ciphertext, macOff, macOff + macSize);
+ byte[] calculatedMac = mReadMac.CalculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen,
+ blocks_length - macSize, randomData);
- bool badMac = !Arrays.ConstantTimeAreEqual(calculatedMac, decryptedMac);
+ bool badMac = !Arrays.ConstantTimeAreEqual(calculatedMac, receivedMac);
- if (badMac || totalPad == 0)
- throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ if (badMac || totalPad == 0)
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+ }
+ }
- return Arrays.Copy(ciphertext, offset, macInputLen);
+ return Arrays.CopyOfRange(ciphertext, offset, offset + dec_output_length);
}
protected virtual int CheckPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize)
@@ -185,10 +316,7 @@ namespace Org.BouncyCastle.Crypto.Tls
int dummyIndex = 0;
byte padDiff = 0;
- //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion;
- bool isTls = true;
-
- if ((!isTls && totalPad > blockSize) || (macSize + totalPad > len))
+ if ((TlsUtilities.IsSsl(context) && totalPad > blockSize) || (macSize + totalPad > len))
{
totalPad = 0;
}
@@ -225,25 +353,24 @@ namespace Org.BouncyCastle.Crypto.Tls
protected virtual int ChooseExtraPadBlocks(SecureRandom r, int max)
{
-// return r.NextInt(max + 1);
+ // return r.NextInt(max + 1);
- uint x = (uint)r.NextInt();
+ int x = r.NextInt();
int n = LowestBitSet(x);
return System.Math.Min(n, max);
}
- private int LowestBitSet(uint x)
+ protected virtual int LowestBitSet(int x)
{
if (x == 0)
- {
return 32;
- }
+ uint ux = (uint)x;
int n = 0;
- while ((x & 1) == 0)
+ while ((ux & 1U) == 0)
{
++n;
- x >>= 1;
+ ux >>= 1;
}
return n;
}
diff --git a/crypto/src/crypto/tls/TlsCipher.cs b/crypto/src/crypto/tls/TlsCipher.cs
index a58f4943f..7bd8573ac 100644
--- a/crypto/src/crypto/tls/TlsCipher.cs
+++ b/crypto/src/crypto/tls/TlsCipher.cs
@@ -5,10 +5,12 @@ namespace Org.BouncyCastle.Crypto.Tls
{
public interface TlsCipher
{
+ int GetPlaintextLimit(int ciphertextLimit);
+
/// <exception cref="IOException"></exception>
- byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len);
+ byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len);
/// <exception cref="IOException"></exception>
- byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len);
+ byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len);
}
}
diff --git a/crypto/src/crypto/tls/TlsCipherFactory.cs b/crypto/src/crypto/tls/TlsCipherFactory.cs
index e5cf96479..4e1fe0eb9 100644
--- a/crypto/src/crypto/tls/TlsCipherFactory.cs
+++ b/crypto/src/crypto/tls/TlsCipherFactory.cs
@@ -6,7 +6,6 @@ namespace Org.BouncyCastle.Crypto.Tls
public interface TlsCipherFactory
{
/// <exception cref="IOException"></exception>
- TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm,
- DigestAlgorithm digestAlgorithm);
+ TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm);
}
}
diff --git a/crypto/src/crypto/tls/TlsHandshakeHash.cs b/crypto/src/crypto/tls/TlsHandshakeHash.cs
new file mode 100644
index 000000000..7118d9769
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsHandshakeHash.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+ public interface TlsHandshakeHash
+ : IDigest
+ {
+ void Init(TlsContext context);
+
+ TlsHandshakeHash NotifyPrfDetermined();
+
+ void TrackHashAlgorithm(byte hashAlgorithm);
+
+ void SealHashAlgorithms();
+
+ TlsHandshakeHash StopTracking();
+
+ IDigest ForkPrfHash();
+
+ byte[] GetFinalHash(byte hashAlgorithm);
+ }
+}
diff --git a/crypto/src/crypto/tls/TlsMac.cs b/crypto/src/crypto/tls/TlsMac.cs
index e4313617e..a80319a17 100644
--- a/crypto/src/crypto/tls/TlsMac.cs
+++ b/crypto/src/crypto/tls/TlsMac.cs
@@ -9,134 +9,165 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
- /// <remarks>
- /// A generic TLS MAC implementation, which can be used with any kind of
- /// IDigest to act as an HMAC.
- /// </remarks>
+ /// <summary>
+ /// A generic TLS MAC implementation, acting as an HMAC based on some underlying Digest.
+ /// </summary>
public class TlsMac
{
- protected long seqNo;
- protected byte[] secret;
- protected HMac mac;
+ protected readonly TlsContext context;
+ protected readonly byte[] secret;
+ protected readonly IMac mac;
+ protected readonly int digestBlockSize;
+ protected readonly int digestOverhead;
+ protected readonly int macLength;
/**
- * Generate a new instance of an TlsMac.
- *
- * @param digest The digest to use.
- * @param key_block A byte-array where the key for this mac is located.
- * @param offset The number of bytes to skip, before the key starts in the buffer.
- * @param len The length of the key.
- */
- public TlsMac(
- IDigest digest,
- byte[] key_block,
- int offset,
- int len)
+ * Generate a new instance of an TlsMac.
+ *
+ * @param context the TLS client context
+ * @param digest The digest to use.
+ * @param key A byte-array where the key for this MAC is located.
+ * @param keyOff The number of bytes to skip, before the key starts in the buffer.
+ * @param keyLen The length of the key.
+ */
+ public TlsMac(TlsContext context, IDigest digest, byte[] key, int keyOff, int keyLen)
{
- this.seqNo = 0;
+ this.context = context;
- KeyParameter param = new KeyParameter(key_block, offset, len);
+ KeyParameter keyParameter = new KeyParameter(key, keyOff, keyLen);
- this.secret = Arrays.Clone(param.GetKey());
+ this.secret = Arrays.Clone(keyParameter.GetKey());
- this.mac = new HMac(digest);
- this.mac.Init(param);
- }
+ // TODO This should check the actual algorithm, not rely on the engine type
+ if (digest is LongDigest)
+ {
+ this.digestBlockSize = 128;
+ this.digestOverhead = 16;
+ }
+ else
+ {
+ this.digestBlockSize = 64;
+ this.digestOverhead = 8;
+ }
- /**
- * @return the MAC write secret
- */
- public virtual byte[] GetMacSecret()
- {
- return this.secret;
- }
+ if (TlsUtilities.IsSsl(context))
+ {
+ this.mac = new Ssl3Mac(digest);
- /**
- * @return the current write sequence number
- */
- public virtual long SequenceNumber
- {
- get { return this.seqNo; }
+ // TODO This should check the actual algorithm, not assume based on the digest size
+ if (digest.GetDigestSize() == 20)
+ {
+ /*
+ * NOTE: When SHA-1 is used with the SSL 3.0 MAC, the secret + input pad is not
+ * digest block-aligned.
+ */
+ this.digestOverhead = 4;
+ }
+ }
+ else
+ {
+ this.mac = new HMac(digest);
+
+ // NOTE: The input pad for HMAC is always a full digest block
+ }
+
+ this.mac.Init(keyParameter);
+
+ this.macLength = mac.GetMacSize();
+ if (context.SecurityParameters.truncatedHMac)
+ {
+ this.macLength = System.Math.Min(this.macLength, 10);
+ }
}
/**
- * Increment the current write sequence number
+ * @return the MAC write secret
*/
- public virtual void IncSequenceNumber()
+ public virtual byte[] MacSecret
{
- this.seqNo++;
+ get { return this.secret; }
}
/**
- * @return The Keysize of the mac.
- */
+ * @return The output length of this MAC.
+ */
public virtual int Size
{
- get { return mac.GetMacSize(); }
+ get { return macLength; }
}
/**
- * Calculate the mac for some given data.
- * <p/>
- * TlsMac will keep track of the sequence number internally.
- *
- * @param type The message type of the message.
- * @param message A byte-buffer containing the message.
- * @param offset The number of bytes to skip, before the message starts.
- * @param len The length of the message.
- * @return A new byte-buffer containing the mac value.
- */
- public virtual byte[] CalculateMac(byte type, byte[] message, int offset, int len)
+ * Calculate the MAC for some given data.
+ *
+ * @param type The message type of the message.
+ * @param message A byte-buffer containing the message.
+ * @param offset The number of bytes to skip, before the message starts.
+ * @param length The length of the message.
+ * @return A new byte-buffer containing the MAC value.
+ */
+ public virtual byte[] CalculateMac(long seqNo, byte type, byte[] message, int offset, int length)
{
- //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion;
- bool isTls = true;
+ ProtocolVersion serverVersion = context.ServerVersion;
+ bool isSsl = serverVersion.IsSsl;
- byte[] macHeader = new byte[isTls ? 13 : 11];
- TlsUtilities.WriteUint64(seqNo++, macHeader, 0);
+ byte[] macHeader = new byte[isSsl ? 11 : 13];
+ TlsUtilities.WriteUint64(seqNo, macHeader, 0);
TlsUtilities.WriteUint8(type, macHeader, 8);
- if (isTls)
+ if (!isSsl)
{
- TlsUtilities.WriteVersion(macHeader, 9);
+ TlsUtilities.WriteVersion(serverVersion, macHeader, 9);
}
- TlsUtilities.WriteUint16(len, macHeader, 11);
+ TlsUtilities.WriteUint16(length, macHeader, macHeader.Length - 2);
mac.BlockUpdate(macHeader, 0, macHeader.Length);
- mac.BlockUpdate(message, offset, len);
- return MacUtilities.DoFinal(mac);
+ mac.BlockUpdate(message, offset, length);
+
+ return Truncate(MacUtilities.DoFinal(mac));
}
- public virtual byte[] CalculateMacConstantTime(byte type, byte[] message, int offset, int len,
+ public virtual byte[] CalculateMacConstantTime(long seqNo, byte type, byte[] message, int offset, int length,
int fullLength, byte[] dummyData)
{
- // Actual MAC only calculated on 'len' bytes
- byte[] result = CalculateMac(type, message, offset, len);
+ /*
+ * Actual MAC only calculated on 'length' bytes...
+ */
+ byte[] result = CalculateMac(seqNo, type, message, offset, length);
+
+ /*
+ * ...but ensure a constant number of complete digest blocks are processed (as many as would
+ * be needed for 'fullLength' bytes of input).
+ */
+ int headerLength = TlsUtilities.IsSsl(context) ? 11 : 13;
- //bool isTls = context.ServerVersion.FullVersion >= ProtocolVersion.TLSv10.FullVersion;
- bool isTls = true;
+ // How many extra full blocks do we need to calculate?
+ int extra = GetDigestBlockCount(headerLength + fullLength) - GetDigestBlockCount(headerLength + length);
- // ...but ensure a constant number of complete digest blocks are processed (per 'fullLength')
- if (isTls)
+ while (--extra >= 0)
{
- // TODO Currently all TLS digests use a block size of 64, a suffix (length field) of 8, and padding (1+)
- int db = 64, ds = 8;
+ mac.BlockUpdate(dummyData, 0, digestBlockSize);
+ }
- int L1 = 13 + fullLength;
- int L2 = 13 + len;
+ // One more byte in case the implementation is "lazy" about processing blocks
+ mac.Update(dummyData[0]);
+ mac.Reset();
- // How many extra full blocks do we need to calculate?
- int extra = ((L1 + ds) / db) - ((L2 + ds) / db);
+ return result;
+ }
- while (--extra >= 0)
- {
- mac.BlockUpdate(dummyData, 0, db);
- }
+ protected virtual int GetDigestBlockCount(int inputLength)
+ {
+ // NOTE: This calculation assumes a minimum of 1 pad byte
+ return (inputLength + digestOverhead) / digestBlockSize;
+ }
- // One more byte in case the implementation is "lazy" about processing blocks
- mac.Update(dummyData[0]);
- mac.Reset();
+ protected virtual byte[] Truncate(byte[] bs)
+ {
+ if (bs.Length <= macLength)
+ {
+ return bs;
}
- return result;
+ return Arrays.CopyOf(bs, macLength);
}
}
}
diff --git a/crypto/src/crypto/tls/TlsNullCipher.cs b/crypto/src/crypto/tls/TlsNullCipher.cs
index 3e2bfa847..f30ace24f 100644
--- a/crypto/src/crypto/tls/TlsNullCipher.cs
+++ b/crypto/src/crypto/tls/TlsNullCipher.cs
@@ -1,28 +1,118 @@
using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
/// <summary>
- /// A NULL cipher suite, for use during handshake.
+ /// A NULL CipherSuite, with optional MAC.
/// </summary>
public class TlsNullCipher
- : TlsCipher
+ : TlsCipher
{
- public virtual byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len)
+ protected readonly TlsContext context;
+
+ protected readonly TlsMac writeMac;
+ protected readonly TlsMac readMac;
+
+ public TlsNullCipher(TlsContext context)
{
- return CopyData(plaintext, offset, len);
+ this.context = context;
+ this.writeMac = null;
+ this.readMac = null;
}
- public virtual byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len)
+ /// <exception cref="IOException"></exception>
+ public TlsNullCipher(TlsContext context, IDigest clientWriteDigest, IDigest serverWriteDigest)
{
- return CopyData(ciphertext, offset, len);
+ if ((clientWriteDigest == null) != (serverWriteDigest == null))
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ this.context = context;
+
+ TlsMac clientWriteMac = null, serverWriteMac = null;
+
+ if (clientWriteDigest != null)
+ {
+ int key_block_size = clientWriteDigest.GetDigestSize()
+ + serverWriteDigest.GetDigestSize();
+ byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size);
+
+ int offset = 0;
+
+ clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset,
+ clientWriteDigest.GetDigestSize());
+ offset += clientWriteDigest.GetDigestSize();
+
+ serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset,
+ serverWriteDigest.GetDigestSize());
+ offset += serverWriteDigest.GetDigestSize();
+
+ if (offset != key_block_size)
+ {
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
+ }
+
+ if (context.IsServer)
+ {
+ writeMac = serverWriteMac;
+ readMac = clientWriteMac;
+ }
+ else
+ {
+ writeMac = clientWriteMac;
+ readMac = serverWriteMac;
+ }
}
- protected virtual byte[] CopyData(byte[] text, int offset, int len)
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
{
- byte[] result = new byte[len];
- Array.Copy(text, offset, result, 0, len);
+ int result = ciphertextLimit;
+ if (writeMac != null)
+ {
+ result -= writeMac.Size;
+ }
return result;
}
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len)
+ {
+ if (writeMac == null)
+ {
+ return Arrays.CopyOfRange(plaintext, offset, offset + len);
+ }
+
+ byte[] mac = writeMac.CalculateMac(seqNo, type, plaintext, offset, len);
+ byte[] ciphertext = new byte[len + mac.Length];
+ Array.Copy(plaintext, offset, ciphertext, 0, len);
+ Array.Copy(mac, 0, ciphertext, len, mac.Length);
+ return ciphertext;
+ }
+
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len)
+ {
+ if (readMac == null)
+ {
+ return Arrays.CopyOfRange(ciphertext, offset, offset + len);
+ }
+
+ int macSize = readMac.Size;
+ if (len < macSize)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int macInputLen = len - macSize;
+
+ byte[] receivedMac = Arrays.CopyOfRange(ciphertext, offset + macInputLen, offset + len);
+ byte[] computedMac = readMac.CalculateMac(seqNo, type, ciphertext, offset, macInputLen);
+
+ if (!Arrays.ConstantTimeAreEqual(receivedMac, computedMac))
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+
+ return Arrays.CopyOfRange(ciphertext, offset, offset + macInputLen);
+ }
}
}
diff --git a/crypto/src/crypto/tls/TlsProtocolHandler.cs b/crypto/src/crypto/tls/TlsProtocolHandler.cs
index 918640db5..3dec5ade0 100644
--- a/crypto/src/crypto/tls/TlsProtocolHandler.cs
+++ b/crypto/src/crypto/tls/TlsProtocolHandler.cs
@@ -54,7 +54,7 @@ namespace Org.BouncyCastle.Crypto.Tls
/*
* The Record Stream we use
*/
- private RecordStream rs;
+ private RecordStream recordStream;
private SecureRandom random;
private TlsStream tlsStream = null;
@@ -116,7 +116,7 @@ namespace Org.BouncyCastle.Crypto.Tls
Stream outStr,
SecureRandom sr)
{
- this.rs = new RecordStream(this, inStr, outStr);
+ this.recordStream = new RecordStream(this, inStr, outStr);
this.random = sr;
}
@@ -202,8 +202,8 @@ namespace Org.BouncyCastle.Crypto.Tls
case HandshakeType.finished:
break;
default:
- rs.UpdateHandshakeData(beginning, 0, 4);
- rs.UpdateHandshakeData(buf, 0, len);
+ recordStream.UpdateHandshakeData(beginning, 0, 4);
+ recordStream.UpdateHandshakeData(buf, 0, len);
break;
}
@@ -270,7 +270,7 @@ namespace Org.BouncyCastle.Crypto.Tls
* Calculate our own checksum.
*/
byte[] expectedServerVerifyData = TlsUtilities.PRF(tlsClientContext,
- securityParameters.masterSecret, ExporterLabel.server_finished, rs.GetCurrentHash(), 12);
+ securityParameters.masterSecret, ExporterLabel.server_finished, recordStream.GetCurrentHash(), 12);
/*
* Compare both checksums.
@@ -445,6 +445,9 @@ namespace Org.BouncyCastle.Crypto.Tls
tlsClient.NotifySecureRenegotiation(secure_negotiation);
}
+ this.securityParameters.cipherSuite = selectedCipherSuite;
+ this.securityParameters.compressionAlgorithm = selectedCompressionMethod;
+
if (clientExtensions != null)
{
tlsClient.ProcessServerExtensions(serverExtensions);
@@ -457,6 +460,13 @@ namespace Org.BouncyCastle.Crypto.Tls
// TODO Just a place-holder until other TLS 1.2 changes arrive
this.securityParameters.prfAlgorithm = PrfAlgorithm.tls_prf_legacy;
+ /*
+ * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify
+ * verify_data_length has a verify_data_length equal to 12. This includes all
+ * existing cipher suites.
+ */
+ this.securityParameters.verifyDataLength = 12;
+
break;
default:
this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
@@ -527,7 +537,7 @@ namespace Org.BouncyCastle.Crypto.Tls
if (clientCreds != null && clientCreds is TlsSignerCredentials)
{
TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds;
- byte[] md5andsha1 = rs.GetCurrentHash();
+ byte[] md5andsha1 = recordStream.GetCurrentHash();
byte[] clientCertificateSignature = signerCreds.GenerateCertificateSignature(
md5andsha1);
SendCertificateVerify(clientCertificateSignature);
@@ -540,7 +550,7 @@ namespace Org.BouncyCastle.Crypto.Tls
*/
byte[] cmessage = new byte[1];
cmessage[0] = 1;
- rs.WriteMessage(ContentType.change_cipher_spec, cmessage, 0, cmessage.Length);
+ recordStream.WriteMessage(ContentType.change_cipher_spec, cmessage, 0, cmessage.Length);
connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND;
@@ -562,20 +572,20 @@ namespace Org.BouncyCastle.Crypto.Tls
/*
* Initialize our cipher suite
*/
- rs.ClientCipherSpecDecided(tlsClient.GetCompression(), tlsClient.GetCipher());
+ recordStream.ClientCipherSpecDecided(tlsClient.GetCompression(), tlsClient.GetCipher());
/*
* Send our finished message.
*/
byte[] clientVerifyData = TlsUtilities.PRF(tlsClientContext, securityParameters.masterSecret,
- ExporterLabel.client_finished, rs.GetCurrentHash(), 12);
+ ExporterLabel.client_finished, recordStream.GetCurrentHash(), 12);
MemoryStream bos = new MemoryStream();
TlsUtilities.WriteUint8((byte)HandshakeType.finished, bos);
TlsUtilities.WriteOpaque24(clientVerifyData, bos);
byte[] message = bos.ToArray();
- rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+ recordStream.WriteMessage(ContentType.handshake, message, 0, message.Length);
this.connection_state = CS_CLIENT_FINISHED_SEND;
break;
@@ -703,7 +713,7 @@ namespace Org.BouncyCastle.Crypto.Tls
*/
try
{
- rs.Close();
+ recordStream.Close();
}
catch (Exception)
{
@@ -747,7 +757,7 @@ namespace Org.BouncyCastle.Crypto.Tls
this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
}
- rs.ServerClientSpecReceived();
+ recordStream.ServerClientSpecReceived();
this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED;
}
@@ -767,7 +777,7 @@ namespace Org.BouncyCastle.Crypto.Tls
// Patch actual length back in
TlsUtilities.WriteUint24(message.Length - 4, message, 1);
- rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+ recordStream.WriteMessage(ContentType.handshake, message, 0, message.Length);
}
private void SendClientKeyExchange()
@@ -784,7 +794,7 @@ namespace Org.BouncyCastle.Crypto.Tls
// Patch actual length back in
TlsUtilities.WriteUint24(message.Length - 4, message, 1);
- rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+ recordStream.WriteMessage(ContentType.handshake, message, 0, message.Length);
}
private void SendCertificateVerify(byte[] data)
@@ -799,7 +809,7 @@ namespace Org.BouncyCastle.Crypto.Tls
TlsUtilities.WriteOpaque16(data, bos);
byte[] message = bos.ToArray();
- rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+ recordStream.WriteMessage(ContentType.handshake, message, 0, message.Length);
}
/// <summary>Connects to the remote system.</summary>
@@ -823,6 +833,7 @@ namespace Org.BouncyCastle.Crypto.Tls
this.tlsClient = tlsClient;
this.securityParameters = new SecurityParameters();
+ this.securityParameters.entity = ConnectionEnd.client;
this.tlsClientContext = new TlsClientContextImpl(random, securityParameters);
@@ -830,6 +841,7 @@ namespace Org.BouncyCastle.Crypto.Tls
tlsClientContext.NonceRandomGenerator);
this.tlsClient.Init(tlsClientContext);
+ this.recordStream.Init(tlsClientContext);
/*
* Send Client hello
@@ -965,7 +977,7 @@ namespace Org.BouncyCastle.Crypto.Tls
{
try
{
- rs.ReadData();
+ recordStream.ReadData();
}
catch (TlsFatalAlert e)
{
@@ -997,7 +1009,7 @@ namespace Org.BouncyCastle.Crypto.Tls
{
try
{
- rs.WriteMessage(type, buf, offset, len);
+ recordStream.WriteMessage(type, buf, offset, len);
}
catch (TlsFatalAlert e)
{
@@ -1127,7 +1139,7 @@ namespace Org.BouncyCastle.Crypto.Tls
this.failedWithError = true;
}
SendAlert(alertLevel, alertDescription);
- rs.Close();
+ recordStream.Close();
if (alertLevel == AlertLevel.fatal)
{
throw new IOException(TLS_ERROR_MESSAGE);
@@ -1143,7 +1155,7 @@ namespace Org.BouncyCastle.Crypto.Tls
{
byte[] error = new byte[] { alertLevel, alertDescription };
- rs.WriteMessage(ContentType.alert, error, 0, 2);
+ recordStream.WriteMessage(ContentType.alert, error, 0, 2);
}
/// <summary>Closes this connection</summary>
@@ -1180,7 +1192,7 @@ namespace Org.BouncyCastle.Crypto.Tls
internal void Flush()
{
- rs.Flush();
+ recordStream.Flush();
}
internal bool IsClosed
diff --git a/crypto/src/crypto/tls/TlsStreamCipher.cs b/crypto/src/crypto/tls/TlsStreamCipher.cs
index 24e2ce73f..d664b09dd 100644
--- a/crypto/src/crypto/tls/TlsStreamCipher.cs
+++ b/crypto/src/crypto/tls/TlsStreamCipher.cs
@@ -1,108 +1,173 @@
using System;
+using System.IO;
-using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Tls;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Crypto.Tls
{
- public class TlsStreamCipher : TlsCipher
+ public class TlsStreamCipher
+ : TlsCipher
{
- protected TlsContext context;
+ protected readonly TlsContext context;
- protected IStreamCipher encryptCipher;
- protected IStreamCipher decryptCipher;
+ protected readonly IStreamCipher encryptCipher;
+ protected readonly IStreamCipher decryptCipher;
- protected TlsMac writeMac;
- protected TlsMac readMac;
+ protected readonly TlsMac writeMac;
+ protected readonly TlsMac readMac;
- public TlsStreamCipher(TlsContext context, IStreamCipher encryptCipher,
- IStreamCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize)
+ protected readonly bool usesNonce;
+
+ /// <exception cref="IOException"></exception>
+ [Obsolete("Use version with additional 'usesNonce' argument")]
+ public TlsStreamCipher(TlsContext context, IStreamCipher clientWriteCipher,
+ IStreamCipher serverWriteCipher, IDigest clientWriteDigest, IDigest serverWriteDigest,
+ int cipherKeySize)
+ : this(context, clientWriteCipher, serverWriteCipher, clientWriteDigest, serverWriteDigest, cipherKeySize, false)
{
+ }
+
+ /// <exception cref="IOException"></exception>
+ public TlsStreamCipher(TlsContext context, IStreamCipher clientWriteCipher,
+ IStreamCipher serverWriteCipher, IDigest clientWriteDigest, IDigest serverWriteDigest,
+ int cipherKeySize, bool usesNonce)
+ {
+ bool isServer = context.IsServer;
+
this.context = context;
- this.encryptCipher = encryptCipher;
- this.decryptCipher = decryptCipher;
+ this.usesNonce = usesNonce;
- int prfSize = (2 * cipherKeySize) + writeDigest.GetDigestSize()
- + readDigest.GetDigestSize();
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
- SecurityParameters securityParameters = context.SecurityParameters;
+ int key_block_size = (2 * cipherKeySize) + clientWriteDigest.GetDigestSize()
+ + serverWriteDigest.GetDigestSize();
- byte[] keyBlock = TlsUtilities.PRF(context, securityParameters.masterSecret, ExporterLabel.key_expansion,
- TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom),
- prfSize);
+ byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size);
int offset = 0;
// Init MACs
- writeMac = CreateTlsMac(writeDigest, keyBlock, ref offset);
- readMac = CreateTlsMac(readDigest, keyBlock, ref offset);
+ TlsMac clientWriteMac = new TlsMac(context, clientWriteDigest, key_block, offset,
+ clientWriteDigest.GetDigestSize());
+ offset += clientWriteDigest.GetDigestSize();
+ TlsMac serverWriteMac = new TlsMac(context, serverWriteDigest, key_block, offset,
+ serverWriteDigest.GetDigestSize());
+ offset += serverWriteDigest.GetDigestSize();
// Build keys
- KeyParameter encryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
- KeyParameter decryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
+ KeyParameter clientWriteKey = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
+ KeyParameter serverWriteKey = new KeyParameter(key_block, offset, cipherKeySize);
+ offset += cipherKeySize;
- if (offset != prfSize)
+ if (offset != key_block_size)
+ {
throw new TlsFatalAlert(AlertDescription.internal_error);
+ }
- // Init Ciphers
- encryptCipher.Init(true, encryptKey);
- decryptCipher.Init(false, decryptKey);
- }
-
- public byte[] EncodePlaintext(byte type, byte[] plaintext, int offset, int len)
- {
- byte[] mac = writeMac.CalculateMac(type, plaintext, offset, len);
- int size = len + mac.Length;
+ ICipherParameters encryptParams, decryptParams;
+ if (isServer)
+ {
+ this.writeMac = serverWriteMac;
+ this.readMac = clientWriteMac;
+ this.encryptCipher = serverWriteCipher;
+ this.decryptCipher = clientWriteCipher;
+ encryptParams = serverWriteKey;
+ decryptParams = clientWriteKey;
+ }
+ else
+ {
+ this.writeMac = clientWriteMac;
+ this.readMac = serverWriteMac;
+ this.encryptCipher = clientWriteCipher;
+ this.decryptCipher = serverWriteCipher;
+ encryptParams = clientWriteKey;
+ decryptParams = serverWriteKey;
+ }
- byte[] outbuf = new byte[size];
+ if (usesNonce)
+ {
+ byte[] dummyNonce = new byte[8];
+ encryptParams = new ParametersWithIV(encryptParams, dummyNonce);
+ decryptParams = new ParametersWithIV(decryptParams, dummyNonce);
+ }
- encryptCipher.ProcessBytes(plaintext, offset, len, outbuf, 0);
- encryptCipher.ProcessBytes(mac, 0, mac.Length, outbuf, len);
+ this.encryptCipher.Init(true, encryptParams);
+ this.decryptCipher.Init(false, decryptParams);
+ }
- return outbuf;
+ public virtual int GetPlaintextLimit(int ciphertextLimit)
+ {
+ return ciphertextLimit - writeMac.Size;
}
- public byte[] DecodeCiphertext(byte type, byte[] ciphertext, int offset, int len)
+ public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len)
{
- byte[] deciphered = new byte[len];
- decryptCipher.ProcessBytes(ciphertext, offset, len, deciphered, 0);
+ /*
+ * draft-josefsson-salsa20-tls-04 2.1 Note that Salsa20 requires a 64-bit nonce. That
+ * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS
+ * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation
+ * of the 16-bit epoch with the 48-bit sequence number.
+ */
+ if (usesNonce)
+ {
+ UpdateIV(encryptCipher, true, seqNo);
+ }
- int plaintextSize = deciphered.Length - readMac.Size;
- byte[] plainText = CopyData(deciphered, 0, plaintextSize);
+ byte[] outBuf = new byte[len + writeMac.Size];
- byte[] receivedMac = CopyData(deciphered, plaintextSize, readMac.Size);
- byte[] computedMac = readMac.CalculateMac(type, plainText, 0, plainText.Length);
+ encryptCipher.ProcessBytes(plaintext, offset, len, outBuf, 0);
- if (!Arrays.ConstantTimeAreEqual(receivedMac, computedMac))
- {
- throw new TlsFatalAlert(AlertDescription.bad_record_mac);
- }
+ byte[] mac = writeMac.CalculateMac(seqNo, type, plaintext, offset, len);
+ encryptCipher.ProcessBytes(mac, 0, mac.Length, outBuf, len);
- return plainText;
+ return outBuf;
}
- protected virtual TlsMac CreateTlsMac(IDigest digest, byte[] buf, ref int off)
+ /// <exception cref="IOException"></exception>
+ public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len)
{
- int len = digest.GetDigestSize();
- TlsMac mac = new TlsMac(digest, buf, off, len);
- off += len;
- return mac;
+ /*
+ * draft-josefsson-salsa20-tls-04 2.1 Note that Salsa20 requires a 64-bit nonce. That
+ * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS
+ * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation
+ * of the 16-bit epoch with the 48-bit sequence number.
+ */
+ if (usesNonce)
+ {
+ UpdateIV(decryptCipher, false, seqNo);
+ }
+
+ int macSize = readMac.Size;
+ if (len < macSize)
+ throw new TlsFatalAlert(AlertDescription.decode_error);
+
+ int plaintextLength = len - macSize;
+
+ byte[] deciphered = new byte[len];
+ decryptCipher.ProcessBytes(ciphertext, offset, len, deciphered, 0);
+ CheckMac(seqNo, type, deciphered, plaintextLength, len, deciphered, 0, plaintextLength);
+ return Arrays.CopyOfRange(deciphered, 0, plaintextLength);
}
- protected virtual KeyParameter CreateKeyParameter(byte[] buf, ref int off, int len)
+ /// <exception cref="IOException"></exception>
+ protected virtual void CheckMac(long seqNo, byte type, byte[] recBuf, int recStart, int recEnd, byte[] calcBuf, int calcOff, int calcLen)
{
- KeyParameter key = new KeyParameter(buf, off, len);
- off += len;
- return key;
+ byte[] receivedMac = Arrays.CopyOfRange(recBuf, recStart, recEnd);
+ byte[] computedMac = readMac.CalculateMac(seqNo, type, calcBuf, calcOff, calcLen);
+
+ if (!Arrays.ConstantTimeAreEqual(receivedMac, computedMac))
+ throw new TlsFatalAlert(AlertDescription.bad_record_mac);
}
- protected virtual byte[] CopyData(byte[] text, int offset, int len)
+ protected virtual void UpdateIV(IStreamCipher cipher, bool forEncryption, long seqNo)
{
- byte[] result = new byte[len];
- Array.Copy(text, offset, result, 0, len);
- return result;
+ byte[] nonce = new byte[8];
+ TlsUtilities.WriteUint64(seqNo, nonce, 0);
+ cipher.Init(forEncryption, new ParametersWithIV(null, nonce));
}
}
}
diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index 462ec4074..bd5362e5b 100644
--- a/crypto/src/crypto/tls/TlsUtilities.cs
+++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -10,6 +10,7 @@ using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Date;
using Org.BouncyCastle.Utilities.IO;
@@ -759,6 +760,105 @@ namespace Org.BouncyCastle.Crypto.Tls
}
}
+ internal static byte[] CalculateKeyBlock(TlsContext context, int size)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ byte[] master_secret = securityParameters.MasterSecret;
+ byte[] seed = Concat(securityParameters.ServerRandom, securityParameters.ClientRandom);
+
+ if (IsSsl(context))
+ {
+ return CalculateKeyBlock_Ssl(master_secret, seed, size);
+ }
+
+ return PRF(context, master_secret, ExporterLabel.key_expansion, seed, size);
+ }
+
+ internal static byte[] CalculateKeyBlock_Ssl(byte[] master_secret, byte[] random, int size)
+ {
+ IDigest md5 = CreateHash(HashAlgorithm.md5);
+ IDigest sha1 = CreateHash(HashAlgorithm.sha1);
+ int md5Size = md5.GetDigestSize();
+ byte[] shatmp = new byte[sha1.GetDigestSize()];
+ byte[] tmp = new byte[size + md5Size];
+
+ int i = 0, pos = 0;
+ while (pos < size)
+ {
+ byte[] ssl3Const = SSL3_CONST[i];
+
+ sha1.BlockUpdate(ssl3Const, 0, ssl3Const.Length);
+ sha1.BlockUpdate(master_secret, 0, master_secret.Length);
+ sha1.BlockUpdate(random, 0, random.Length);
+ sha1.DoFinal(shatmp, 0);
+
+ md5.BlockUpdate(master_secret, 0, master_secret.Length);
+ md5.BlockUpdate(shatmp, 0, shatmp.Length);
+ md5.DoFinal(tmp, pos);
+
+ pos += md5Size;
+ ++i;
+ }
+
+ return Arrays.CopyOfRange(tmp, 0, size);
+ }
+
+ internal static byte[] CalculateMasterSecret(TlsContext context, byte[] pre_master_secret)
+ {
+ SecurityParameters securityParameters = context.SecurityParameters;
+ byte[] seed = Concat(securityParameters.ClientRandom, securityParameters.ServerRandom);
+
+ if (IsSsl(context))
+ {
+ return CalculateMasterSecret_Ssl(pre_master_secret, seed);
+ }
+
+ return PRF(context, pre_master_secret, ExporterLabel.master_secret, seed, 48);
+ }
+
+ internal static byte[] CalculateMasterSecret_Ssl(byte[] pre_master_secret, byte[] random)
+ {
+ IDigest md5 = CreateHash(HashAlgorithm.md5);
+ IDigest sha1 = CreateHash(HashAlgorithm.sha1);
+ int md5Size = md5.GetDigestSize();
+ byte[] shatmp = new byte[sha1.GetDigestSize()];
+
+ byte[] rval = new byte[md5Size * 3];
+ int pos = 0;
+
+ for (int i = 0; i < 3; ++i)
+ {
+ byte[] ssl3Const = SSL3_CONST[i];
+
+ sha1.BlockUpdate(ssl3Const, 0, ssl3Const.Length);
+ sha1.BlockUpdate(pre_master_secret, 0, pre_master_secret.Length);
+ sha1.BlockUpdate(random, 0, random.Length);
+ sha1.DoFinal(shatmp, 0);
+
+ md5.BlockUpdate(pre_master_secret, 0, pre_master_secret.Length);
+ md5.BlockUpdate(shatmp, 0, shatmp.Length);
+ md5.DoFinal(rval, pos);
+
+ pos += md5Size;
+ }
+
+ return rval;
+ }
+
+ internal static byte[] CalculateVerifyData(TlsContext context, string asciiLabel, byte[] handshakeHash)
+ {
+ if (IsSsl(context))
+ {
+ return handshakeHash;
+ }
+
+ SecurityParameters securityParameters = context.SecurityParameters;
+ byte[] master_secret = securityParameters.MasterSecret;
+ int verify_data_length = securityParameters.VerifyDataLength;
+
+ return PRF(context, master_secret, asciiLabel, handshakeHash, verify_data_length);
+ }
+
public static IDigest CreateHash(byte hashAlgorithm)
{
switch (hashAlgorithm)
@@ -859,6 +959,130 @@ namespace Org.BouncyCastle.Crypto.Tls
}
}
+ internal static short GetClientCertificateType(Certificate clientCertificate, Certificate serverCertificate)
+ {
+ if (clientCertificate.IsEmpty)
+ return -1;
+
+ X509CertificateStructure x509Cert = clientCertificate.GetCertificateAt(0);
+ SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+ try
+ {
+ AsymmetricKeyParameter publicKey = PublicKeyFactory.CreateKey(keyInfo);
+ if (publicKey.IsPrivate)
+ throw new TlsFatalAlert(AlertDescription.internal_error);
+
+ /*
+ * TODO RFC 5246 7.4.6. The certificates MUST be signed using an acceptable hash/
+ * signature algorithm pair, as described in Section 7.4.4. Note that this relaxes the
+ * constraints on certificate-signing algorithms found in prior versions of TLS.
+ */
+
+ /*
+ * RFC 5246 7.4.6. Client Certificate
+ */
+
+ /*
+ * RSA public key; the certificate MUST allow the key to be used for signing with the
+ * signature scheme and hash algorithm that will be employed in the certificate verify
+ * message.
+ */
+ if (publicKey is RsaKeyParameters)
+ {
+ ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+ return ClientCertificateType.rsa_sign;
+ }
+
+ /*
+ * DSA public key; the certificate MUST allow the key to be used for signing with the
+ * hash algorithm that will be employed in the certificate verify message.
+ */
+ if (publicKey is DsaPublicKeyParameters)
+ {
+ ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+ return ClientCertificateType.dss_sign;
+ }
+
+ /*
+ * ECDSA-capable public key; the certificate MUST allow the key to be used for signing
+ * with the hash algorithm that will be employed in the certificate verify message; the
+ * public key MUST use a curve and point format supported by the server.
+ */
+ if (publicKey is ECPublicKeyParameters)
+ {
+ ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+ // TODO Check the curve and point format
+ return ClientCertificateType.ecdsa_sign;
+ }
+
+ // TODO Add support for ClientCertificateType.*_fixed_*
+ }
+ catch (Exception)
+ {
+ }
+
+ throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+ }
+
+ internal static void TrackHashAlgorithms(TlsHandshakeHash handshakeHash, IList supportedSignatureAlgorithms)
+ {
+ if (supportedSignatureAlgorithms != null)
+ {
+ foreach (SignatureAndHashAlgorithm signatureAndHashAlgorithm in supportedSignatureAlgorithms)
+ {
+ byte hashAlgorithm = signatureAndHashAlgorithm.Hash;
+ handshakeHash.TrackHashAlgorithm(hashAlgorithm);
+ }
+ }
+ }
+
+ public static bool HasSigningCapability(byte clientCertificateType)
+ {
+ switch (clientCertificateType)
+ {
+ case ClientCertificateType.dss_sign:
+ case ClientCertificateType.ecdsa_sign:
+ case ClientCertificateType.rsa_sign:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ public static TlsSigner CreateTlsSigner(byte clientCertificateType)
+ {
+ switch (clientCertificateType)
+ {
+ case ClientCertificateType.dss_sign:
+ return new TlsDssSigner();
+ case ClientCertificateType.ecdsa_sign:
+ return new TlsECDsaSigner();
+ case ClientCertificateType.rsa_sign:
+ return new TlsRsaSigner();
+ default:
+ throw new ArgumentException("not a type with signing capability", "clientCertificateType");
+ }
+ }
+
+ internal static readonly byte[] SSL_CLIENT = {0x43, 0x4C, 0x4E, 0x54};
+ internal static readonly byte[] SSL_SERVER = {0x53, 0x52, 0x56, 0x52};
+
+ // SSL3 magic mix constants ("A", "BB", "CCC", ...)
+ internal static readonly byte[][] SSL3_CONST = GenSsl3Const();
+
+ private static byte[][] GenSsl3Const()
+ {
+ int n = 10;
+ byte[][] arr = new byte[n][];
+ for (int i = 0; i < n; i++)
+ {
+ byte[] b = new byte[i + 1];
+ Arrays.Fill(b, (byte)('A' + i));
+ arr[i] = b;
+ }
+ return arr;
+ }
+
private static IList VectorOfOne(object obj)
{
IList v = Platform.CreateArrayList(1);
diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs
index e629fcf65..87bc1ba65 100644
--- a/crypto/src/util/Arrays.cs
+++ b/crypto/src/util/Arrays.cs
@@ -1,6 +1,8 @@
using System;
using System.Text;
+using Org.BouncyCastle.Math;
+
namespace Org.BouncyCastle.Utilities
{
/// <summary> General array utilities.</summary>
@@ -407,11 +409,90 @@ namespace Org.BouncyCastle.Utilities
}
}
- public static byte[] Copy(byte[] data, int off, int len)
+ public static byte[] CopyOf(byte[] data, int newLength)
{
- byte[] result = new byte[len];
- Array.Copy(data, off, result, 0, len);
- return result;
+ byte[] tmp = new byte[newLength];
+ Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+ return tmp;
+ }
+
+ public static char[] CopyOf(char[] data, int newLength)
+ {
+ char[] tmp = new char[newLength];
+ Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+ return tmp;
+ }
+
+ public static int[] CopyOf(int[] data, int newLength)
+ {
+ int[] tmp = new int[newLength];
+ Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+ return tmp;
+ }
+
+ public static long[] CopyOf(long[] data, int newLength)
+ {
+ long[] tmp = new long[newLength];
+ Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+ return tmp;
+ }
+
+ public static BigInteger[] CopyOf(BigInteger[] data, int newLength)
+ {
+ BigInteger[] tmp = new BigInteger[newLength];
+ Array.Copy(data, 0, tmp, 0, System.Math.Min(newLength, data.Length));
+ return tmp;
+ }
+
+ /**
+ * Make a copy of a range of bytes from the passed in data array. The range can
+ * extend beyond the end of the input array, in which case the return array will
+ * be padded with zeroes.
+ *
+ * @param data the array from which the data is to be copied.
+ * @param from the start index at which the copying should take place.
+ * @param to the final index of the range (exclusive).
+ *
+ * @return a new byte array containing the range given.
+ */
+ public static byte[] CopyOfRange(byte[] data, int from, int to)
+ {
+ int newLength = GetLength(from, to);
+ byte[] tmp = new byte[newLength];
+ Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+ return tmp;
+ }
+
+ public static int[] CopyOfRange(int[] data, int from, int to)
+ {
+ int newLength = GetLength(from, to);
+ int[] tmp = new int[newLength];
+ Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+ return tmp;
+ }
+
+ public static long[] CopyOfRange(long[] data, int from, int to)
+ {
+ int newLength = GetLength(from, to);
+ long[] tmp = new long[newLength];
+ Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+ return tmp;
+ }
+
+ public static BigInteger[] CopyOfRange(BigInteger[] data, int from, int to)
+ {
+ int newLength = GetLength(from, to);
+ BigInteger[] tmp = new BigInteger[newLength];
+ Array.Copy(data, from, tmp, 0, System.Math.Min(newLength, data.Length - from));
+ return tmp;
+ }
+
+ private static int GetLength(int from, int to)
+ {
+ int newLength = to - from;
+ if (newLength < 0)
+ throw new ArgumentException(from + " > " + to);
+ return newLength;
}
public static byte[] Append(byte[] a, byte b)
|