Skip to content

Reduce data copying in CCM mode #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
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
@@ -1,7 +1,5 @@
package org.bouncycastle.crypto.modes;

import java.io.ByteArrayOutputStream;

import org.bouncycastle.crypto.BlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.DataLengthException;
Expand All @@ -11,6 +9,7 @@
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.io.AccessibleByteArrayOutputStream;

/**
* Implements the Counter with Cipher Block Chaining mode (CCM) detailed in
Expand All @@ -29,8 +28,8 @@ public class CCMBlockCipher
private int macSize;
private CipherParameters keyParam;
private byte[] macBlock;
private ByteArrayOutputStream associatedText = new ByteArrayOutputStream();
private ByteArrayOutputStream data = new ByteArrayOutputStream();
private AccessibleByteArrayOutputStream associatedText = new AccessibleByteArrayOutputStream();
private AccessibleByteArrayOutputStream data = new AccessibleByteArrayOutputStream();

/**
* Basic constructor.
Expand Down Expand Up @@ -138,14 +137,12 @@ public int processBytes(byte[] in, int inOff, int inLen, byte[] out, int outOff)
public int doFinal(byte[] out, int outOff)
throws IllegalStateException, InvalidCipherTextException
{
byte[] text = data.toByteArray();
byte[] enc = processPacket(text, 0, text.length);

System.arraycopy(enc, 0, out, outOff, enc.length);
int len = getOutputSize(0);
processPacket(data.getBuffer(), 0, data.size(), out, outOff);

reset();

return enc.length;
return len;
}

public void reset()
Expand Down Expand Up @@ -187,7 +184,7 @@ public int getOutputSize(int len)
return totalData < macSize ? 0 : totalData - macSize;
}

public byte[] processPacket(byte[] in, int inOff, int inLen)
public void processPacket(byte[] in, int inOff, int inLen, byte[] output, int outOff)
throws IllegalStateException, InvalidCipherTextException
{
// TODO: handle null keyParam (e.g. via RepeatedKeySpec)
Expand Down Expand Up @@ -215,14 +212,12 @@ public byte[] processPacket(byte[] in, int inOff, int inLen)
BlockCipher ctrCipher = new SICBlockCipher(cipher);
ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv));

int index = inOff;
int outOff = 0;
byte[] output;
int inIndex = inOff;
int outIndex = outOff;

if (forEncryption)
{
int outputLen = inLen + macSize;
output = new byte[outputLen];
if (output.length < (outputLen + outOff))
{
throw new DataLengthException("Output buffer too short.");
Expand All @@ -232,24 +227,22 @@ public byte[] processPacket(byte[] in, int inOff, int inLen)

ctrCipher.processBlock(macBlock, 0, macBlock, 0); // S0

while (index < inLen - blockSize) // S1...
while (inIndex < (inOff + inLen - blockSize)) // S1...
{
ctrCipher.processBlock(in, index, output, outOff);
outOff += blockSize;
index += blockSize;
ctrCipher.processBlock(in, inIndex, output, outIndex);
outIndex += blockSize;
inIndex += blockSize;
}

byte[] block = new byte[blockSize];

System.arraycopy(in, index, block, 0, inLen - index);
System.arraycopy(in, inIndex, block, 0, inLen + inOff - inIndex);

ctrCipher.processBlock(block, 0, block, 0);

System.arraycopy(block, 0, output, outOff, inLen - index);
System.arraycopy(block, 0, output, outIndex, inLen + inOff - inIndex);

outOff += inLen - index;

System.arraycopy(macBlock, 0, output, outOff, output.length - outOff);
System.arraycopy(macBlock, 0, output, outOff + inLen, macSize);
}
else
{
Expand All @@ -258,7 +251,6 @@ public byte[] processPacket(byte[] in, int inOff, int inLen)
throw new InvalidCipherTextException("data too short");
}
int outputLen = inLen - macSize;
output = new byte[outputLen];
if (output.length < (outputLen + outOff))
{
throw new DataLengthException("Output buffer too short.");
Expand All @@ -273,32 +265,30 @@ public byte[] processPacket(byte[] in, int inOff, int inLen)
macBlock[i] = 0;
}

while (outOff < output.length - blockSize)
while (inIndex < (inOff + outputLen - blockSize))
{
ctrCipher.processBlock(in, index, output, outOff);
outOff += blockSize;
index += blockSize;
ctrCipher.processBlock(in, inIndex, output, outIndex);
outIndex += blockSize;
inIndex += blockSize;
}

byte[] block = new byte[blockSize];

System.arraycopy(in, index, block, 0, output.length - outOff);
System.arraycopy(in, inIndex, block, 0, outputLen - (inIndex - inOff));

ctrCipher.processBlock(block, 0, block, 0);

System.arraycopy(block, 0, output, outOff, output.length - outOff);
System.arraycopy(block, 0, output, outIndex, outputLen - (inIndex - inOff));

byte[] calculatedMacBlock = new byte[blockSize];

calculateMac(output, 0, output.length, calculatedMacBlock);
calculateMac(output, outOff, outputLen, calculatedMacBlock);

if (!Arrays.constantTimeAreEqual(macBlock, calculatedMacBlock))
{
throw new InvalidCipherTextException("mac check in CCM failed");
}
}

return output;
}

private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
Expand Down Expand Up @@ -367,8 +357,7 @@ private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
}
if (associatedText.size() > 0)
{
byte[] tmp = associatedText.toByteArray();
cMac.update(tmp, 0, tmp.length);
cMac.update(associatedText.getBuffer(), 0, associatedText.size());
}

extra = (extra + textLength) % 16;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.bouncycastle.util.io;

import java.io.ByteArrayOutputStream;

/**
* {@link ByteArrayOutputStream} that allows access to the internal byte buffer.
*/
public class AccessibleByteArrayOutputStream extends ByteArrayOutputStream
{

public AccessibleByteArrayOutputStream()
{
}

public AccessibleByteArrayOutputStream(int size)
{
super(size);
}

public byte[] getBuffer()
{
return this.buf;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public void performTest()
{
ccm.init(false, new AEADParameters(new KeyParameter(K1), 32, N2, A2));

ccm.processPacket(C2, 0, C2.length);
ccm.processPacket(C2, 0, C2.length, new byte[100], 0);

fail("invalid cipher text not picked up");
}
Expand Down