diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java index 1eb3444bbc..851361fa17 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/DESedeWrapEngine.java @@ -224,6 +224,11 @@ public byte[] unwrap(byte[] in, int inOff, int inLen) throw new InvalidCipherTextException("Ciphertext not multiple of " + blockSize); } + if (inLen < 2 * blockSize) + { + throw new InvalidCipherTextException("unwrap data too short"); + } + /* // Check if the length of the cipher text is reasonable given the key // type. It must be 40 bytes for a 168 bit key and either 32, 40, or diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/DSTU7624WrapEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/DSTU7624WrapEngine.java index c86ac50962..16c8aceec8 100755 --- a/core/src/main/java/org/bouncycastle/crypto/engines/DSTU7624WrapEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/DSTU7624WrapEngine.java @@ -156,6 +156,11 @@ public byte[] unwrap(byte[] in, int inOff, int inLen) throw new DataLengthException("unwrap data must be a multiple of " + engine.getBlockSize() + " bytes"); } + if (inLen < engine.getBlockSize()) + { + throw new InvalidCipherTextException("unwrap data too short"); + } + int n = 2 * inLen / engine.getBlockSize(); int V = (n - 1) * 6; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GOST28147WrapEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GOST28147WrapEngine.java index 65c0021ab7..62c3a864f5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GOST28147WrapEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GOST28147WrapEngine.java @@ -54,6 +54,12 @@ public byte[] wrap(byte[] input, int inOff, int inLen) public byte[] unwrap(byte[] input, int inOff, int inLen) throws InvalidCipherTextException { + // wrapped key is 32 octets of encrypted key material followed by the MAC + if (inLen < 32 + mac.getMacSize()) + { + throw new InvalidCipherTextException("unwrap data too short"); + } + byte[] decKey = new byte[inLen - mac.getMacSize()]; cipher.processBlock(input, inOff, decKey, 0); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java index 98208b4a8e..a7fcf9fd45 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RC2WrapEngine.java @@ -245,6 +245,11 @@ public byte[] unwrap(byte[] in, int inOff, int inLen) + engine.getBlockSize()); } + if (inLen < 2 * engine.getBlockSize()) + { + throw new InvalidCipherTextException("unwrap data too short"); + } + /* * // Check if the length of the cipher text is reasonable given the key // * type. It must be 40 bytes for a 168 bit key and either 32, 40, or // diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SM2Engine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SM2Engine.java index 8ac88aede1..e5da9fd6e7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SM2Engine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SM2Engine.java @@ -176,6 +176,11 @@ private byte[] encrypt(byte[] in, int inOff, int inLen) private byte[] decrypt(byte[] in, int inOff, int inLen) throws InvalidCipherTextException { + if (inLen < (curveLength * 2 + 1) + digest.getDigestSize()) + { + throw new InvalidCipherTextException("ciphertext too short"); + } + byte[] c1 = new byte[curveLength * 2 + 1]; System.arraycopy(in, inOff, c1, 0, c1.length); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java index 30bf398cd1..a988033de8 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java @@ -153,6 +153,7 @@ public class RegressionTest new SM2EngineTest(), new SM2KeyExchangeTest(), new SM2SignerTest(), + new ShortWrapCipherTextTest(), new SM4Test(), new DSTU7624Test(), new DSTU7564Test(), diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ShortWrapCipherTextTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ShortWrapCipherTextTest.java new file mode 100644 index 0000000000..fdbe062155 --- /dev/null +++ b/core/src/test/java/org/bouncycastle/crypto/test/ShortWrapCipherTextTest.java @@ -0,0 +1,155 @@ +package org.bouncycastle.crypto.test; + +import java.math.BigInteger; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.engines.DESedeWrapEngine; +import org.bouncycastle.crypto.engines.DSTU7624WrapEngine; +import org.bouncycastle.crypto.engines.GOST28147WrapEngine; +import org.bouncycastle.crypto.engines.RC2WrapEngine; +import org.bouncycastle.crypto.engines.SM2Engine; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithUKM; +import org.bouncycastle.crypto.params.RC2Parameters; +import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; +import org.bouncycastle.util.test.TestRandomBigInteger; + +/** + * A ciphertext / wrapped-key shorter than the fixed overhead the engine strips + * must be rejected with an {@link InvalidCipherTextException}, not an escaping + * {@code NegativeArraySizeException} / {@code ArrayIndexOutOfBoundsException} + * from the {@code new byte[inLen - overhead]} allocation. + */ +public class ShortWrapCipherTextTest + extends SimpleTest +{ + public String getName() + { + return "ShortWrapCipherText"; + } + + private void sm2() + throws Exception + { + BigInteger p = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3", 16); + BigInteger a = new BigInteger("787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498", 16); + BigInteger b = new BigInteger("63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A", 16); + BigInteger n = new BigInteger("8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7", 16); + BigInteger gx = new BigInteger("421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D", 16); + BigInteger gy = new BigInteger("0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2", 16); + + ECCurve curve = new ECCurve.Fp(p, a, b, n, ECConstants.ONE); + ECPoint g = curve.createPoint(gx, gy); + ECDomainParameters domainParams = new ECDomainParameters(curve, g, n); + + ECKeyPairGenerator kpg = new ECKeyPairGenerator(); + kpg.init(new ECKeyGenerationParameters(domainParams, new TestRandomBigInteger("1649AB77A00637BD5E2EFE283FBF353534AA7F7CB89463F208DDBC2920BB0DA0", 16))); + AsymmetricCipherKeyPair kp = kpg.generateKeyPair(); + + SM2Engine engine = new SM2Engine(); + engine.init(false, (ECPrivateKeyParameters)kp.getPrivate()); + + // C1 (65) + C3 (32) is 97 octets; anything shorter cannot hold C1 + digest. + try + { + engine.processBlock(new byte[50], 0, 50); + fail("SM2Engine: no exception on short ciphertext"); + } + catch (InvalidCipherTextException e) + { + isEquals("ciphertext too short", e.getMessage()); + } + } + + private void gost28147Wrap() + throws Exception + { + GOST28147WrapEngine engine = new GOST28147WrapEngine(); + engine.init(false, new ParametersWithUKM(new KeyParameter(new byte[32]), new byte[8])); + + try + { + engine.unwrap(new byte[16], 0, 16); + fail("GOST28147WrapEngine: no exception on short input"); + } + catch (InvalidCipherTextException e) + { + isEquals("unwrap data too short", e.getMessage()); + } + } + + private void dstu7624Wrap() + throws Exception + { + DSTU7624WrapEngine engine = new DSTU7624WrapEngine(128); + engine.init(false, new KeyParameter(new byte[16])); + + try + { + engine.unwrap(new byte[0], 0, 0); + fail("DSTU7624WrapEngine: no exception on empty input"); + } + catch (InvalidCipherTextException e) + { + isEquals("unwrap data too short", e.getMessage()); + } + } + + private void desedeWrap() + throws Exception + { + DESedeWrapEngine engine = new DESedeWrapEngine(); + engine.init(false, new KeyParameter(Hex.decode("0123456789abcdeffedcba987654321089abcdef01234567"))); + + try + { + engine.unwrap(new byte[8], 0, 8); + fail("DESedeWrapEngine: no exception on short input"); + } + catch (InvalidCipherTextException e) + { + isEquals("unwrap data too short", e.getMessage()); + } + } + + private void rc2Wrap() + throws Exception + { + RC2WrapEngine engine = new RC2WrapEngine(); + engine.init(false, new RC2Parameters(new byte[16])); + + try + { + engine.unwrap(new byte[8], 0, 8); + fail("RC2WrapEngine: no exception on short input"); + } + catch (InvalidCipherTextException e) + { + isEquals("unwrap data too short", e.getMessage()); + } + } + + public void performTest() + throws Exception + { + sm2(); + gost28147Wrap(); + dstu7624Wrap(); + desedeWrap(); + rc2Wrap(); + } + + public static void main(String[] args) + { + runTest(new ShortWrapCipherTextTest()); + } +} diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 19399289fd..59233d38da 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -25,6 +25,7 @@

2.1.2 Defects Fixed

2.2.1 Version