package org.bouncycastle.jce.provider.test; import java.io.IOException; import java.security.AlgorithmParameters; 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.asn1.cms.GCMParameters; import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.Arrays; 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"); private byte[] KGCM = Hex.decode("00000000000000000000000000000000"); private byte[] NGCM = Hex.decode("000000000000000000000000"); private byte[] CGCM = Hex.decode("58e2fccefa7e3061367f1d57a4e7455a"); public String getName() { return "AEAD"; } public void performTest() throws Exception { try { this.getClass().getClassLoader().loadClass("javax.crypto.spec.GCMParameterSpec"); checkCipherWithAD(K2, N2, A2, P2, C2_short); testGCMParameterSpec(K2, N2, A2, P2, C2); testGCMParameterSpecWithRepeatKey(K2, N2, A2, P2, C2); testGCMGeneric(KGCM, NGCM, new byte[0], new byte[0], CGCM); } catch (ClassNotFoundException e) { System.err.println("AEADTest disabled due to JDK"); } } 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, IOException { 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."); } AlgorithmParameters algParams = eax.getParameters(); byte[] encParams = algParams.getEncoded(); GCMParameters gcmParameters = GCMParameters.getInstance(encParams); if (!Arrays.areEqual(spec.getIV(), gcmParameters.getNonce()) || spec.getTLen() != gcmParameters.getIcvLen()) { fail("parameters mismatch"); } } private void testGCMParameterSpecWithRepeatKey(byte[] K, byte[] N, byte[] A, byte[] P, byte[] C) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchProviderException, IOException { 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."); } AlgorithmParameters algParams = eax.getParameters(); byte[] encParams = algParams.getEncoded(); GCMParameters gcmParameters = GCMParameters.getInstance(encParams); if (!Arrays.areEqual(spec.getIV(), gcmParameters.getNonce()) || spec.getTLen() != gcmParameters.getIcvLen()) { fail("parameters mismatch"); } } private void testGCMGeneric(byte[] K, byte[] N, byte[] A, byte[] P, byte[] C) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchProviderException, IOException { Cipher eax = Cipher.getInstance("AES/GCM/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 = Cipher.getInstance("GCM", "BC"); 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."); } AlgorithmParameters algParams = eax.getParameters(); byte[] encParams = algParams.getEncoded(); GCMParameters gcmParameters = GCMParameters.getInstance(encParams); if (!Arrays.areEqual(spec.getIV(), gcmParameters.getNonce()) || spec.getTLen() != gcmParameters.getIcvLen()) { fail("parameters mismatch"); } } public static void main(String[] args) throws Exception { Security.addProvider(new BouncyCastleProvider()); runTest(new AEADTest()); } }