Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ public class RegressionTest
new SM2EngineTest(),
new SM2KeyExchangeTest(),
new SM2SignerTest(),
new ShortWrapCipherTextTest(),
new SM4Test(),
new DSTU7624Test(),
new DSTU7564Test(),
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
1 change: 1 addition & 0 deletions docs/releasenotes.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ <h3>2.1.2 Defects Fixed</h3>
<ul>
<li>The Ed25519 KeyFactory in the JDK 11+ and JDK 15+ multi-release overlays (META-INF/versions/11 and /15) had drifted from the base implementation on the OpenSSH key-spec path: it used the no-passphrase OpenSSHPrivateKeyUtil.parsePrivateKeyBlob overload, so a passphrase-encrypted openssh-key-v1 Ed25519 private key that KeyFactory.getInstance("Ed25519", "BC").generatePrivate(new OpenSSHPrivateKeySpec(blob, passphrase)) decoded correctly on JDK 8 failed on JDK 11 and later; it also let a raw RuntimeException escape on a malformed blob and threw IllegalStateException (JDK 11) instead of InvalidKeySpecException for a non-Ed25519 key. The overlays now match the base implementation (passphrase support, parse errors wrapped as InvalidKeySpecException). Relatedly, the OpenSSH wrong-key-type and decode-failure paths across the RSA, DSA, EC and Ed25519 KeyFactorySpi implementations now consistently raise InvalidKeySpecException (previously a mix of IllegalArgumentException / IllegalStateException) and wrap a malformed OpenSSH public key, and an incorrect "public key is not RSA private key" message on the RSA private path was corrected. The multi-release test tasks now exercise the OpenSSH key specs against the multi-release jar.</li>
<li>The Ant-built utility jars (bcutil-jdk15to18, bcutil-jdk14) duplicated org.bouncycastle.asn1.iana.IANAObjectIdentifiers, which since 1.85 (github #2176) lives only in core and is therefore already shipped in bcprov. The shared ant/bc+-build.xml build-util target still copied org/bouncycastle/asn1/iana/** into bcutil, so a project depending on both bcprov and bcutil (for example via bcpkix) failed an Android/R8 build with "Duplicate class org.bouncycastle.asn1.iana.IANAObjectIdentifiers found in modules bcprov-jdk15to18-1.85.jar and bcutil-jdk15to18-1.85.jar". The iana package is no longer bundled into bcutil (it remains in bcprov); the Gradle jdk18on jars were already correct (github #2356).</li>
<li>SM2Engine.decrypt and the GOST28147, DSTU7624, DESede and RC2 key-wrap engines sized their output as new byte[inLen - overhead] without first checking the ciphertext was at least that overhead, so a short attacker-supplied ciphertext or wrapped key threw NegativeArraySizeException / ArrayIndexOutOfBoundsException instead of the declared InvalidCipherTextException. The engines now reject an under-length input with InvalidCipherTextException, matching the guard the IES, RFC 3394 and RFC 5649 engines already have.</li>
</ul>

<a id="r1rv85"><h3>2.2.1 Version</h3></a>
Expand Down