diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs
index a6cd00401..b5919aead 100644
--- a/crypto/src/crypto/modes/GCMBlockCipher.cs
+++ b/crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -306,7 +306,16 @@ namespace Org.BouncyCastle.Crypto.Modes
bufBlock[bufOff] = input;
if (++bufOff == bufBlock.Length)
{
- OutputBlock(output, outOff);
+ ProcessBlock(bufBlock, 0, output, outOff);
+ if (forEncryption)
+ {
+ bufOff = 0;
+ }
+ else
+ {
+ Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
+ bufOff = macSize;
+ }
return BlockSize;
}
return 0;
@@ -321,41 +330,58 @@ namespace Org.BouncyCastle.Crypto.Modes
{
CheckStatus();
- if (input.Length < (inOff + len))
- throw new DataLengthException("Input buffer too short");
+ Check.DataLength(input, inOff, len, "input buffer too short");
int resultLen = 0;
- for (int i = 0; i < len; ++i)
+ if (forEncryption)
{
- bufBlock[bufOff] = input[inOff + i];
- if (++bufOff == bufBlock.Length)
+ if (bufOff != 0)
{
- OutputBlock(output, outOff + resultLen);
- resultLen += BlockSize;
+ while (len > 0)
+ {
+ --len;
+ bufBlock[bufOff] = input[inOff++];
+ if (++bufOff == BlockSize)
+ {
+ ProcessBlock(bufBlock, 0, output, outOff);
+ bufOff = 0;
+ resultLen += BlockSize;
+ break;
+ }
+ }
}
- }
- return resultLen;
- }
+ while (len >= BlockSize)
+ {
+ ProcessBlock(input, inOff, output, outOff + resultLen);
+ inOff += BlockSize;
+ len -= BlockSize;
+ resultLen += BlockSize;
+ }
- private void OutputBlock(byte[] output, int offset)
- {
- Check.OutputLength(output, offset, BlockSize, "Output buffer too short");
- if (totalLength == 0)
- {
- InitCipher();
- }
- gCTRBlock(bufBlock, output, offset);
- if (forEncryption)
- {
- bufOff = 0;
+ if (len > 0)
+ {
+ Array.Copy(input, inOff, bufBlock, 0, len);
+ bufOff = len;
+ }
}
else
{
- Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
- bufOff = macSize;
+ for (int i = 0; i < len; ++i)
+ {
+ bufBlock[bufOff] = input[inOff + i];
+ if (++bufOff == bufBlock.Length)
+ {
+ ProcessBlock(bufBlock, 0, output, outOff + resultLen);
+ Array.Copy(bufBlock, BlockSize, bufBlock, 0, macSize);
+ bufOff = macSize;
+ resultLen += BlockSize;
+ }
+ }
}
+
+ return resultLen;
}
public int DoFinal(byte[] output, int outOff)
@@ -385,7 +411,7 @@ namespace Org.BouncyCastle.Crypto.Modes
if (extra > 0)
{
- gCTRPartial(bufBlock, 0, extra, output, outOff);
+ ProcessPartial(bufBlock, 0, extra, output, outOff);
}
atLength += (uint)atBlockPos;
@@ -515,27 +541,50 @@ namespace Org.BouncyCastle.Crypto.Modes
}
}
- private void gCTRBlock(byte[] block, byte[] output, int outOff)
+ private void ProcessBlock(byte[] buf, int bufOff, byte[] output, int outOff)
{
- byte[] tmp = GetNextCounterBlock();
+ Check.OutputLength(output, outOff, BlockSize, "Output buffer too short");
- GcmUtilities.Xor(tmp, block);
- Array.Copy(tmp, 0, output, outOff, BlockSize);
+ if (totalLength == 0)
+ {
+ InitCipher();
+ }
+
+ byte[] ctrBlock = new byte[BlockSize];
+ GetNextCtrBlock(ctrBlock);
- gHASHBlock(S, forEncryption ? tmp : block);
+ if (forEncryption)
+ {
+ GcmUtilities.Xor(ctrBlock, buf, bufOff);
+ gHASHBlock(S, ctrBlock);
+ Array.Copy(ctrBlock, 0, output, outOff, BlockSize);
+ }
+ else
+ {
+ gHASHBlock(S, buf, bufOff);
+ GcmUtilities.Xor(ctrBlock, 0, buf, bufOff, output, outOff);
+ }
totalLength += BlockSize;
}
- private void gCTRPartial(byte[] buf, int off, int len, byte[] output, int outOff)
+ private void ProcessPartial(byte[] buf, int off, int len, byte[] output, int outOff)
{
- byte[] tmp = GetNextCounterBlock();
+ byte[] ctrBlock = new byte[BlockSize];
+ GetNextCtrBlock(ctrBlock);
- GcmUtilities.Xor(tmp, buf, off, len);
- Array.Copy(tmp, 0, output, outOff, len);
-
- gHASHPartial(S, forEncryption ? tmp : buf, 0, len);
+ if (forEncryption)
+ {
+ GcmUtilities.Xor(buf, off, ctrBlock, 0, len);
+ gHASHPartial(S, buf, off, len);
+ }
+ else
+ {
+ gHASHPartial(S, buf, off, len);
+ GcmUtilities.Xor(buf, off, ctrBlock, 0, len);
+ }
+ Array.Copy(buf, off, output, outOff, len);
totalLength += (uint)len;
}
@@ -554,13 +603,19 @@ namespace Org.BouncyCastle.Crypto.Modes
multiplier.MultiplyH(Y);
}
+ private void gHASHBlock(byte[] Y, byte[] b, int off)
+ {
+ GcmUtilities.Xor(Y, b, off);
+ multiplier.MultiplyH(Y);
+ }
+
private void gHASHPartial(byte[] Y, byte[] b, int off, int len)
{
GcmUtilities.Xor(Y, b, off, len);
multiplier.MultiplyH(Y);
}
- private byte[] GetNextCounterBlock()
+ private void GetNextCtrBlock(byte[] block)
{
if (blocksRemaining == 0)
throw new InvalidOperationException("Attempt to process too many blocks");
@@ -573,10 +628,7 @@ namespace Org.BouncyCastle.Crypto.Modes
c += counter[13]; counter[13] = (byte)c; c >>= 8;
c += counter[12]; counter[12] = (byte)c;
- byte[] tmp = new byte[BlockSize];
- // TODO Sure would be nice if ciphers could operate on int[]
- cipher.ProcessBlock(counter, 0, tmp, 0);
- return tmp;
+ cipher.ProcessBlock(counter, 0, block, 0);
}
private void CheckStatus()
diff --git a/crypto/src/crypto/modes/gcm/GcmUtilities.cs b/crypto/src/crypto/modes/gcm/GcmUtilities.cs
index d8ab2ca73..22e0067a5 100644
--- a/crypto/src/crypto/modes/gcm/GcmUtilities.cs
+++ b/crypto/src/crypto/modes/gcm/GcmUtilities.cs
@@ -267,6 +267,32 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm
while (i < 16);
}
+ internal static void Xor(byte[] x, byte[] y, int yOff)
+ {
+ int i = 0;
+ do
+ {
+ x[i] ^= y[yOff + i]; ++i;
+ x[i] ^= y[yOff + i]; ++i;
+ x[i] ^= y[yOff + i]; ++i;
+ x[i] ^= y[yOff + i]; ++i;
+ }
+ while (i < 16);
+ }
+
+ internal static void Xor(byte[] x, int xOff, byte[] y, int yOff, byte[] z, int zOff)
+ {
+ int i = 0;
+ do
+ {
+ z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
+ z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
+ z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
+ z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); ++i;
+ }
+ while (i < 16);
+ }
+
internal static void Xor(byte[] x, byte[] y, int yOff, int yLen)
{
while (--yLen >= 0)
@@ -275,6 +301,14 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm
}
}
+ internal static void Xor(byte[] x, int xOff, byte[] y, int yOff, int len)
+ {
+ while (--len >= 0)
+ {
+ x[xOff + len] ^= y[yOff + len];
+ }
+ }
+
internal static void Xor(byte[] x, byte[] y, byte[] z)
{
int i = 0;
diff --git a/crypto/src/crypto/tls/TlsClientProtocol.cs b/crypto/src/crypto/tls/TlsClientProtocol.cs
index 0ea84c05c..8de76c2f8 100644
--- a/crypto/src/crypto/tls/TlsClientProtocol.cs
+++ b/crypto/src/crypto/tls/TlsClientProtocol.cs
@@ -145,6 +145,7 @@ namespace Org.BouncyCastle.Crypto.Tls
ProcessFinishedMessage(buf);
this.mConnectionState = CS_SERVER_FINISHED;
+ SendChangeCipherSpecMessage();
SendFinishedMessage();
this.mConnectionState = CS_CLIENT_FINISHED;
@@ -266,8 +267,6 @@ namespace Org.BouncyCastle.Crypto.Tls
{
this.mSecurityParameters.masterSecret = Arrays.Clone(this.mSessionParameters.MasterSecret);
this.mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
-
- SendChangeCipherSpecMessage();
}
else
{
diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs
index c2bfbcb74..f5285d80b 100644
--- a/crypto/src/crypto/tls/TlsServerProtocol.cs
+++ b/crypto/src/crypto/tls/TlsServerProtocol.cs
@@ -359,10 +359,10 @@ namespace Org.BouncyCastle.Crypto.Tls
if (this.mExpectSessionTicket)
{
SendNewSessionTicketMessage(mTlsServer.GetNewSessionTicket());
- SendChangeCipherSpecMessage();
}
this.mConnectionState = CS_SERVER_SESSION_TICKET;
+ SendChangeCipherSpecMessage();
SendFinishedMessage();
this.mConnectionState = CS_SERVER_FINISHED;
@@ -647,11 +647,6 @@ namespace Org.BouncyCastle.Crypto.Tls
}
mRecordStream.SetPendingConnectionState(Peer.GetCompression(), Peer.GetCipher());
-
- if (!mExpectSessionTicket)
- {
- SendChangeCipherSpecMessage();
- }
}
protected virtual void SendCertificateRequestMessage(CertificateRequest certificateRequest)
diff --git a/crypto/test/src/crypto/test/RsaTest.cs b/crypto/test/src/crypto/test/RsaTest.cs
index 6ecaea59b..db9851c06 100644
--- a/crypto/test/src/crypto/test/RsaTest.cs
+++ b/crypto/test/src/crypto/test/RsaTest.cs
@@ -20,7 +20,84 @@ namespace Org.BouncyCastle.Crypto.Tests
public class RsaTest
: SimpleTest
{
- static BigInteger mod = new BigInteger("b259d2d6e627a768c94be36164c2d9fc79d97aab9253140e5bf17751197731d6f7540d2509e7b9ffee0a70a6e26d56e92d2edd7f85aba85600b69089f35f6bdbf3c298e05842535d9f064e6b0391cb7d306e0a2d20c4dfb4e7b49a9640bdea26c10ad69c3f05007ce2513cee44cfe01998e62b6c3637d3fc0391079b26ee36d5", 16);
+ /*
+ * Based on https://github.com/crocs-muni/roca/blob/master/java/BrokenKey.java
+ * Credits: ported to Java by Martin Paljak
+ */
+ internal class BrokenKey_CVE_2017_15361
+ {
+ private static readonly int[] prims = new int[]{ 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,
+ 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167 };
+ private static readonly BigInteger[] primes = new BigInteger[prims.Length];
+
+ static BrokenKey_CVE_2017_15361()
+ {
+ for (int i = 0; i < prims.Length; i++)
+ {
+ primes[i] = BigInteger.ValueOf(prims[i]);
+ }
+ }
+
+ private static readonly BigInteger[] markers = new BigInteger[]
+ {
+ new BigInteger("6"),
+ new BigInteger("30"),
+ new BigInteger("126"),
+ new BigInteger("1026"),
+ new BigInteger("5658"),
+ new BigInteger("107286"),
+ new BigInteger("199410"),
+ new BigInteger("8388606"),
+ new BigInteger("536870910"),
+ new BigInteger("2147483646"),
+ new BigInteger("67109890"),
+ new BigInteger("2199023255550"),
+ new BigInteger("8796093022206"),
+ new BigInteger("140737488355326"),
+ new BigInteger("5310023542746834"),
+ new BigInteger("576460752303423486"),
+ new BigInteger("1455791217086302986"),
+ new BigInteger("147573952589676412926"),
+ new BigInteger("20052041432995567486"),
+ new BigInteger("6041388139249378920330"),
+ new BigInteger("207530445072488465666"),
+ new BigInteger("9671406556917033397649406"),
+ new BigInteger("618970019642690137449562110"),
+ new BigInteger("79228162521181866724264247298"),
+ new BigInteger("2535301200456458802993406410750"),
+ new BigInteger("1760368345969468176824550810518"),
+ new BigInteger("50079290986288516948354744811034"),
+ new BigInteger("473022961816146413042658758988474"),
+ new BigInteger("10384593717069655257060992658440190"),
+ new BigInteger("144390480366845522447407333004847678774"),
+ new BigInteger("2722258935367507707706996859454145691646"),
+ new BigInteger("174224571863520493293247799005065324265470"),
+ new BigInteger("696898287454081973172991196020261297061886"),
+ new BigInteger("713623846352979940529142984724747568191373310"),
+ new BigInteger("1800793591454480341970779146165214289059119882"),
+ new BigInteger("126304807362733370595828809000324029340048915994"),
+ new BigInteger("11692013098647223345629478661730264157247460343806"),
+ new BigInteger("187072209578355573530071658587684226515959365500926")
+ };
+
+ public static bool IsAffected(RsaKeyParameters publicKey)
+ {
+ BigInteger modulus = publicKey.Modulus;
+
+ for (int i = 0; i < primes.Length; i++)
+ {
+ int remainder = modulus.Remainder(primes[i]).IntValue;
+ if (!markers[i].TestBit(remainder))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+
+ static BigInteger mod = new BigInteger("b259d2d6e627a768c94be36164c2d9fc79d97aab9253140e5bf17751197731d6f7540d2509e7b9ffee0a70a6e26d56e92d2edd7f85aba85600b69089f35f6bdbf3c298e05842535d9f064e6b0391cb7d306e0a2d20c4dfb4e7b49a9640bdea26c10ad69c3f05007ce2513cee44cfe01998e62b6c3637d3fc0391079b26ee36d5", 16);
static BigInteger pubExp = new BigInteger("11", 16);
static BigInteger privExp = new BigInteger("92e08f83cc9920746989ca5034dcb384a094fb9c5a6288fcc4304424ab8f56388f72652d8fafc65a4b9020896f2cde297080f2a540e7b7ce5af0b3446e1258d1dd7f245cf54124b4c6e17da21b90a0ebd22605e6f45c9f136d7a13eaac1c0f7487de8bd6d924972408ebb58af71e76fd7b012a8d0e165f3ae2e5077a8648e619", 16);
static BigInteger p = new BigInteger("f75e80839b9b9379f1cf1128f321639757dba514642c206bbbd99f9a4846208b3e93fbbe5e0527cc59b1d4b929d9555853004c7c8b30ee6a213c3d1bb7415d03", 16);
@@ -355,6 +432,26 @@ namespace Org.BouncyCastle.Crypto.Tests
}
}
+ private void doTest_CVE_2017_15361()
+ {
+ SecureRandom random = new SecureRandom();
+ RsaKeyPairGenerator pGen = new RsaKeyPairGenerator();
+ BigInteger e = BigInteger.ValueOf(0x11);
+
+ for (int strength = 512; strength <= 2048; strength += 32)
+ {
+ pGen.Init(new RsaKeyGenerationParameters(
+ e, random, strength, 100));
+
+ RsaKeyParameters pubKey = (RsaKeyParameters)pGen.GenerateKeyPair().Public;
+
+ if (BrokenKey_CVE_2017_15361.IsAffected(pubKey))
+ {
+ Fail("failed CVE-2017-15361 vulnerability test for generated RSA key");
+ }
+ }
+ }
+
public override void PerformTest()
{
RsaKeyParameters pubParameters = new RsaKeyParameters(false, mod, pubExp);
@@ -634,6 +731,7 @@ namespace Org.BouncyCastle.Crypto.Tests
doTestMissingDataPkcs1Block(pubParameters, privParameters);
doTestTruncatedPkcs1Block(pubParameters, privParameters);
doTestWrongPaddingPkcs1Block(pubParameters, privParameters);
+ doTest_CVE_2017_15361();
try
{
|