summary refs log tree commit diff
diff options
context:
space:
mode:
authorOren Novotny <oren@novotny.org>2017-10-18 17:05:36 -0400
committerOren Novotny <oren@novotny.org>2017-10-18 17:05:36 -0400
commit5eb146543035b4fe7fdc7e809dd9ea408eb08107 (patch)
tree6f76cec93362a053c5c2a3205dd20a1be5742a24
parentUpdate sign client settings (diff)
parentPerf. opts. in GCMBlockCipher (diff)
downloadBouncyCastle.NET-ed25519-5eb146543035b4fe7fdc7e809dd9ea408eb08107.tar.xz
Merge branch 'master' into netstandard
-rw-r--r--crypto/src/crypto/modes/GCMBlockCipher.cs134
-rw-r--r--crypto/src/crypto/modes/gcm/GcmUtilities.cs34
-rw-r--r--crypto/src/crypto/tls/TlsClientProtocol.cs3
-rw-r--r--crypto/src/crypto/tls/TlsServerProtocol.cs7
-rw-r--r--crypto/test/src/crypto/test/RsaTest.cs100
5 files changed, 228 insertions, 50 deletions
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 {