Skip to content

JCE AAD API support #11

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 2 commits 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
27 changes: 22 additions & 5 deletions docs/specifications.html
Original file line number Diff line number Diff line change
Expand Up @@ -148,16 +148,26 @@ <h4>Symmetric (Block)</h4>
<tr><td><b>BufferedBlockCipher</b></td><td>BlockCipher</td><td>&nbsp;</td></tr>
<tr><td><b>CBCBlockCipher</b></td><td>BlockCipher</td><td>&nbsp;</td></tr>
<tr><td><b>CFBBlockCipher</b></td><td>BlockCipher, block size (in bits)</td><td>&nbsp;</td></tr>
<tr><td><b>CCMBlockCipher</b></td><td>BlockCipher</td><td>Packet mode - requires all data up front.</td></tr>
<tr><td><b>GCMBlockCipher</b></td><td>BlockCipher</td><td>Packet mode - NIST SP 800-38D.</td></tr>
<tr><td><b>EAXBlockCipher</b></td><td>BlockCipher</td><td>&nbsp;</td></tr>
<tr><td><b>OFBBlockCipher</b></td><td>BlockCipher, block size (in bits)</td><td>&nbsp;</td></tr>
<tr><td><b>SICBlockCipher</b></td><td>BlockCipher, block size (in bits)</td><td>Also known as CTR mode</td></tr>
<tr><td><b>OpenPGPCFBBlockCipher</b></td><td>BlockCipher</td><td>&nbsp;</td></tr>
<tr><td><b>GOFBBlockCipher</b></td><td>BlockCipher</td><td>GOST OFB mode</td></tr>

</table>

<p>
The base interface for AEAD (Authenticated Encryption Associated Data) modes is <b>AEADBlockCipher</b>
and has the following implemenations.
<p>
<table cellpadding=5 cellspacing=0 border=1 width=80%>
<tr><th>Name</th><th>Constructor</th><th>Notes</th></tr>
<tr><td><b>CCMBlockCipher</b></td><td>BlockCipher</td><td>Packet mode - requires all data up front.</td></tr>
<tr><td><b>EAXBlockCipher</b></td><td>BlockCipher</td><td>&nbsp;</td></tr>
<tr><td><b>GCMBlockCipher</b></td><td>BlockCipher</td><td>Packet mode - NIST SP 800-38D.</td></tr>
<tr><td><b>OCBBlockCipher</b></td><td>BlockCipher</td><td>&nbsp;</td></tr>
</table>
</p>

<p>
<b>BufferedBlockCipher</b> has a further sub-classes
<p>
Expand Down Expand Up @@ -499,15 +509,22 @@ <h4>Symmetric (Block)</h4>
<li>OpenPGPCFB
<li>CTS (equivalent to CBC/WithCTS)
<li>GOFB
<li>CCM
<li>EAX
<li>CCM (AEAD)
<li>EAX (AEAD)
<li>GCM (AEAD)
<li>OCB (AEAD)
</ul>

<p>
Where <i>(n)</i> is a multiple of 8 that gives the blocksize in bits,
eg, OFB8. Note that OFB and CFB mode can be used with plain text that
is not an exact multiple of the block size if NoPadding has been specified.
<p>
All <i>AEAD</i> (Authenticated Encryption Associated Data) modes support
Additional Authentication Data (AAD) using the <code>Cipher.updateAAD()</code>
methods added in Java SE 7.
<p>


Padding Schemes:
<ul>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.bouncycastle.jcajce.provider.symmetric.util;

import java.nio.ByteBuffer;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
Expand All @@ -15,6 +16,7 @@
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.RC2ParameterSpec;
Expand Down Expand Up @@ -46,6 +48,7 @@
import org.bouncycastle.crypto.paddings.TBCPadding;
import org.bouncycastle.crypto.paddings.X923Padding;
import org.bouncycastle.crypto.paddings.ZeroBytePadding;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.crypto.params.ParametersWithRandom;
Expand All @@ -70,7 +73,8 @@ public class BaseBlockCipher
RC5ParameterSpec.class,
IvParameterSpec.class,
PBEParameterSpec.class,
GOST28147ParameterSpec.class
GOST28147ParameterSpec.class,
GCMParameterSpec.class
};

private BlockCipher baseEngine;
Expand Down Expand Up @@ -528,12 +532,29 @@ else if (baseEngine.getAlgorithmName().equals("RC5-64"))
ivParam = (ParametersWithIV)param;
}
}
else if (params instanceof GCMParameterSpec)
{
if (!isAEADModeName(modeName))
{
throw new InvalidAlgorithmParameterException("GCMParameterSpec can only be used with AEAD modes.");
}

GCMParameterSpec p = (GCMParameterSpec) params;

if (key instanceof RepeatedSecretKeySpec)
{
param = new AEADParameters(null, p.getTLen(), p.getIV());
} else
{
param = new AEADParameters(new KeyParameter(key.getEncoded()), p.getTLen(), p.getIV());
}
}
else
{
throw new InvalidAlgorithmParameterException("unknown parameter type.");
}

if ((ivLength != 0) && !(param instanceof ParametersWithIV))
if ((ivLength != 0) && !(param instanceof ParametersWithIV) && !(param instanceof AEADParameters))
{
SecureRandom ivRandom = random;

Expand Down Expand Up @@ -634,6 +655,18 @@ protected void engineInit(
}
}

protected void engineUpdateAAD(byte[] input, int offset, int length)
{
cipher.updateAAD(input, offset, length);
}

protected void engineUpdateAAD(ByteBuffer bytebuffer)
{
int offset = bytebuffer.arrayOffset() + bytebuffer.position();
int length = bytebuffer.limit() - bytebuffer.position();
engineUpdateAAD(bytebuffer.array(), offset, length);
}

protected byte[] engineUpdate(
byte[] input,
int inputOffset,
Expand Down Expand Up @@ -783,6 +816,8 @@ public void init(boolean forEncryption, CipherParameters params)

public int getUpdateOutputSize(int len);

public void updateAAD(byte[] input, int offset, int length);

public int processByte(byte in, byte[] out, int outOff)
throws DataLengthException;

Expand Down Expand Up @@ -844,6 +879,11 @@ public int getUpdateOutputSize(int len)
return cipher.getUpdateOutputSize(len);
}

public void updateAAD(byte[] input, int offset, int length)
{
throw new UnsupportedOperationException("AAD is not supported in the current mode.");
}

public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
{
return cipher.processByte(in, out, outOff);
Expand Down Expand Up @@ -901,6 +941,11 @@ public int getUpdateOutputSize(int len)
return cipher.getUpdateOutputSize(len);
}

public void updateAAD(byte[] input, int offset, int length)
{
cipher.processAADBytes(input, offset, length);
}

public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
{
return cipher.processByte(in, out, outOff);
Expand Down
155 changes: 155 additions & 0 deletions prov/src/test/java/org/bouncycastle/jce/provider/test/AEADTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package org.bouncycastle.jce.provider.test;

import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.RepeatedSecretKeySpec;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.SimpleTest;

public class AEADTest extends SimpleTest
{

// EAX test vector from EAXTest
private byte[] K2 = Hex.decode("91945D3F4DCBEE0BF45EF52255F095A4");
private byte[] N2 = Hex.decode("BECAF043B0A23D843194BA972C66DEBD");
private byte[] A2 = Hex.decode("FA3BFD4806EB53FA");
private byte[] P2 = Hex.decode("F7FB");
private byte[] C2 = Hex.decode("19DD5C4C9331049D0BDAB0277408F67967E5");
// C2 with only 64bit MAC (default for EAX)
private byte[] C2_short = Hex.decode("19DD5C4C9331049D0BDA");

@Override
public String getName()
{
return "AEAD";
}

@Override
public void performTest() throws Exception
{
checkCipherWithAD(K2, N2, A2, P2, C2_short);
testGCMParameterSpec(K2, N2, A2, P2, C2);
testGCMParameterSpecWithRepeatKey(K2, N2, A2, P2, C2);
}

private void checkCipherWithAD(byte[] K,
byte[] N,
byte[] A,
byte[] P,
byte[] C) throws InvalidKeyException,
NoSuchAlgorithmException, NoSuchPaddingException,
IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException, NoSuchProviderException
{
Cipher eax = Cipher.getInstance("AES/EAX/NoPadding", "BC");
SecretKeySpec key = new SecretKeySpec(K, "AES");
IvParameterSpec iv = new IvParameterSpec(N);
eax.init(Cipher.ENCRYPT_MODE, key, iv);

eax.updateAAD(A);
byte[] c = eax.doFinal(P);

if (!areEqual(C, c))
{
fail("JCE encrypt with additional data failed.");
}

eax.init(Cipher.DECRYPT_MODE, key, iv);
eax.updateAAD(A);
byte[] p = eax.doFinal(C);

if (!areEqual(P, p))
{
fail("JCE decrypt with additional data failed.");
}
}

private void testGCMParameterSpec(byte[] K,
byte[] N,
byte[] A,
byte[] P,
byte[] C) throws InvalidKeyException,
NoSuchAlgorithmException, NoSuchPaddingException,
IllegalBlockSizeException, BadPaddingException,
InvalidAlgorithmParameterException, NoSuchProviderException
{
Cipher eax = Cipher.getInstance("AES/EAX/NoPadding", "BC");
SecretKeySpec key = new SecretKeySpec(K, "AES");

// GCMParameterSpec mapped to AEADParameters and overrides default MAC
// size
GCMParameterSpec spec = new GCMParameterSpec(128, N);
eax.init(Cipher.ENCRYPT_MODE, key, spec);

eax.updateAAD(A);
byte[] c = eax.doFinal(P);

if (!areEqual(C, c))
{
fail("JCE encrypt with additional data and GCMParameterSpec failed.");
}

eax.init(Cipher.DECRYPT_MODE, key, spec);
eax.updateAAD(A);
byte[] p = eax.doFinal(C);

if (!areEqual(P, p))
{
fail("JCE decrypt with additional data and GCMParameterSpec failed.");
}
}

private void testGCMParameterSpecWithRepeatKey(byte[] K,
byte[] N,
byte[] A,
byte[] P,
byte[] C)
throws InvalidKeyException, NoSuchAlgorithmException,
NoSuchPaddingException, IllegalBlockSizeException,
BadPaddingException, InvalidAlgorithmParameterException, NoSuchProviderException
{
Cipher eax = Cipher.getInstance("AES/EAX/NoPadding", "BC");
SecretKeySpec key = new SecretKeySpec(K, "AES");
GCMParameterSpec spec = new GCMParameterSpec(128, N);
eax.init(Cipher.ENCRYPT_MODE, key, spec);

eax.updateAAD(A);
byte[] c = eax.doFinal(P);

if (!areEqual(C, c))
{
fail("JCE encrypt with additional data and RepeatedSecretKeySpec failed.");
}

// Check GCMParameterSpec handling knows about RepeatedSecretKeySpec
eax.init(Cipher.DECRYPT_MODE, new RepeatedSecretKeySpec("AES"), spec);
eax.updateAAD(A);
byte[] p = eax.doFinal(C);

if (!areEqual(P, p))
{
fail("JCE decrypt with additional data and RepeatedSecretKeySpec failed.");
}
}

public static void main(String[] args) throws Exception
{
Security.addProvider(new BouncyCastleProvider());

runTest(new AEADTest());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class RegressionTest
new FIPSDESTest(),
new DESedeTest(),
new AESTest(),
new AEADTest(),
new CamelliaTest(),
new SEEDTest(),
new AESSICTest(),
Expand Down