Skip to content

Commit 7f073f7

Browse files
committed
Merge branch 'io-overlap-dbbc-child' into 'main'
Io overlap dbbc child See merge request root/bc-java!87
2 parents 0d7dd97 + c4523c8 commit 7f073f7

File tree

6 files changed

+157
-2
lines changed

6 files changed

+157
-2
lines changed

core/src/main/java/org/bouncycastle/crypto/modes/ChaCha20Poly1305.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,12 @@ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) t
307307
{
308308
throw new IllegalArgumentException("'outOff' cannot be negative");
309309
}
310+
if (in == out && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len)))
311+
{
312+
in = new byte[len];
313+
System.arraycopy(out, inOff, in, 0, len);
314+
inOff = 0;
315+
}
310316

311317
checkData();
312318

@@ -611,4 +617,10 @@ private void reset(boolean clearMac, boolean resetCipher)
611617
processAADBytes(initialAAD, 0, initialAAD.length);
612618
}
613619
}
620+
621+
protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen)
622+
{
623+
// please ensure a valid check for inLen > 0 and outLen > 0 outside this function
624+
return inOff <= outOff + outLen && outOff <= inOff + inLen;
625+
}
614626
}

core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,12 @@ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
224224
{
225225
throw new DataLengthException("Input buffer too short");
226226
}
227+
if (in == out && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len)))
228+
{
229+
in = new byte[len];
230+
System.arraycopy(out, inOff, in, 0, len);
231+
inOff = 0;
232+
}
227233

228234
int resultLen = 0;
229235

@@ -384,4 +390,10 @@ private boolean verifyMac(byte[] mac, int off)
384390

385391
return nonEqual == 0;
386392
}
393+
394+
protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen)
395+
{
396+
// please ensure a valid check for inLen > 0 and outLen > 0 outside this function
397+
return inOff <= outOff + outLen && outOff <= inOff + inLen;
398+
}
387399
}

core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,12 @@ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)
383383
{
384384
throw new DataLengthException("Input buffer too short");
385385
}
386-
386+
if (in == out && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len)))
387+
{
388+
in = new byte[len];
389+
System.arraycopy(out, inOff, in, 0, len);
390+
inOff = 0;
391+
}
387392
int resultLen = 0;
388393

389394
if (forEncryption)
@@ -749,4 +754,10 @@ private void checkStatus()
749754
throw new IllegalStateException("GCM cipher needs to be initialised");
750755
}
751756
}
757+
758+
protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen)
759+
{
760+
// please ensure a valid check for inLen > 0 and outLen > 0 outside this function
761+
return inOff <= outOff + outLen && outOff <= inOff + inLen;
762+
}
752763
}

core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,12 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out
333333
throw new DataLengthException("Input buffer too short");
334334
}
335335
int resultLen = 0;
336-
336+
if (input == output && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len)))
337+
{
338+
input = new byte[len];
339+
System.arraycopy(output, inOff, input, 0, len);
340+
inOff = 0;
341+
}
337342
for (int i = 0; i < len; ++i)
338343
{
339344
mainBlock[mainBlockPos] = input[inOff + i];
@@ -592,4 +597,10 @@ protected static void xor(byte[] block, byte[] val)
592597
{
593598
Bytes.xorTo(16, val, block);
594599
}
600+
601+
protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen)
602+
{
603+
// please ensure a valid check for inLen > 0 and outLen > 0 outside this function
604+
return inOff <= outOff + outLen && outOff <= inOff + inLen;
605+
}
595606
}

core/src/test/java/org/bouncycastle/crypto/test/ChaCha20Poly1305Test.java

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
import java.security.SecureRandom;
44

55
import org.bouncycastle.crypto.InvalidCipherTextException;
6+
import org.bouncycastle.crypto.engines.AESEngine;
67
import org.bouncycastle.crypto.macs.SipHash;
8+
import org.bouncycastle.crypto.modes.CTSBlockCipher;
79
import org.bouncycastle.crypto.modes.ChaCha20Poly1305;
810
import org.bouncycastle.crypto.params.AEADParameters;
911
import org.bouncycastle.crypto.params.KeyParameter;
12+
import org.bouncycastle.util.Arrays;
1013
import org.bouncycastle.util.Strings;
1114
import org.bouncycastle.util.Times;
1215
import org.bouncycastle.util.encoders.Hex;
@@ -48,6 +51,7 @@ public String getName()
4851

4952
public void performTest() throws Exception
5053
{
54+
testOverlapping();
5155
for (int i = 0; i < TEST_VECTORS.length; ++i)
5256
{
5357
runTestCase(TEST_VECTORS[i]);
@@ -439,6 +443,55 @@ private void testExceptions() throws InvalidCipherTextException
439443
}
440444
}
441445

446+
private void testOverlapping()
447+
throws Exception
448+
{
449+
SecureRandom random = new SecureRandom();
450+
int kLength = 32;
451+
byte[] K = new byte[kLength];
452+
random.nextBytes(K);
453+
454+
int aLength = random.nextInt() >>> 24;
455+
byte[] A = new byte[aLength];
456+
random.nextBytes(A);
457+
458+
int nonceLength = 12;
459+
byte[] nonce = new byte[nonceLength];
460+
random.nextBytes(nonce);
461+
462+
AEADParameters parameters = new AEADParameters(new KeyParameter(K), 16 * 8, nonce, A);
463+
464+
ChaCha20Poly1305 bc = initCipher(true, parameters);
465+
466+
final int blockSize = 64;
467+
int offset = 1 + random.nextInt(blockSize - 1) + blockSize;
468+
byte[] data = new byte[blockSize * 4 + offset];
469+
byte[] expected = new byte[bc.getOutputSize(blockSize * 3)];
470+
random.nextBytes(data);
471+
472+
473+
int len = bc.processBytes(data, 0, blockSize * 3, expected, 0);
474+
bc.doFinal(expected, len);
475+
bc = initCipher(true, parameters);
476+
len = bc.processBytes(data, 0, blockSize * 3, data, offset);
477+
bc.doFinal(data, offset + len);
478+
479+
if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length)))
480+
{
481+
fail("failed to overlapping of encryption");
482+
}
483+
484+
bc = initCipher(false, parameters);
485+
bc.processBytes(data, 0, expected.length, expected, 0);
486+
bc = initCipher(true, parameters);
487+
bc.processBytes(data, 0, expected.length, data, offset);
488+
489+
if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length)))
490+
{
491+
fail("failed to overlapping of encryption");
492+
}
493+
}
494+
442495
public static void main(String[] args)
443496
{
444497
runTest(new ChaCha20Poly1305Test());

prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.bouncycastle.util.encoders.Hex;
5151
import org.bouncycastle.util.test.SimpleTest;
5252
import org.bouncycastle.util.test.TestFailedException;
53+
import org.junit.Assert;
5354

5455
/**
5556
* basic test class for a block cipher, basically this just exercises the provider, and makes sure we
@@ -850,6 +851,61 @@ else if (algorithm.startsWith("RC5"))
850851
fail("" + algorithm + " failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes)));
851852
}
852853

854+
//
855+
// Try the doFinal - same input/output same index, index
856+
//
857+
byte[] data = Arrays.concatenate(input, new byte[2 * in.getBlockSize()]);
858+
int len = 0;
859+
try
860+
{
861+
if (algorithm.indexOf("GCM") > 0)
862+
{
863+
out = Cipher.getInstance(algorithm, "BC");
864+
out.init(Cipher.ENCRYPT_MODE, key, rand);
865+
}
866+
867+
len = out.doFinal(data, 0, input.length, data, 0);
868+
}
869+
catch (Exception e)
870+
{
871+
Assert.fail(e.toString());
872+
}
873+
874+
if (!Arrays.areEqual(data, 0, len, output, 0, output.length))
875+
{
876+
Assert.fail("" + algorithm + " failed doFinal - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(data)));
877+
}
878+
879+
//
880+
// Try the doFinal - same input/output shifted offset
881+
//
882+
data = Arrays.concatenate(input, new byte[2 * in.getBlockSize()]);
883+
len = 0;
884+
try
885+
{
886+
887+
if (algorithm.indexOf("GCM") > 0)
888+
{
889+
out = Cipher.getInstance(algorithm, "BC");
890+
out.init(Cipher.ENCRYPT_MODE, key, rand);
891+
}
892+
893+
len = out.doFinal(data, 0, input.length, data, 1);
894+
895+
// System.out.println(Hex.toHexString(output));
896+
// System.out.println(Hex.toHexString(Arrays.copyOfRange(data, 1, 1 + len)));
897+
// System.out.println(len + " " + output.length);
898+
}
899+
catch (Exception e)
900+
{
901+
Assert.fail(e.toString());
902+
}
903+
904+
if (!Arrays.areEqual(data, 1, 1 + len, output, 0, output.length))
905+
{
906+
Assert.fail("" + algorithm + " failed doFinal - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(data)));
907+
}
908+
853909
//
854910
// decryption pass
855911
//

0 commit comments

Comments
 (0)