diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/Aes128GcmNoPadding.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/Aes128GcmNoPadding.java index 17d6ded26..ff1168cff 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/Aes128GcmNoPadding.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/Aes128GcmNoPadding.java @@ -25,15 +25,15 @@ import javax.crypto.NoSuchPaddingException; public class Aes128GcmNoPadding extends AesGcmNoPadding { public static final String NAMESPACE = "urn:xmpp:ciphers:aes-128-gcm-nopadding:0"; - public Aes128GcmNoPadding() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, + public Aes128GcmNoPadding(int MODE) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, InvalidAlgorithmParameterException { - super(128); + super(128, MODE); } - public Aes128GcmNoPadding(byte[] keyAndIv) throws NoSuchProviderException, InvalidAlgorithmParameterException, + public Aes128GcmNoPadding(byte[] keyAndIv, int MODE) throws NoSuchProviderException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException { super(AesGcmNoPadding.copyOfRange(keyAndIv, 0, keyAndIv.length / 2), //Key - AesGcmNoPadding.copyOfRange(keyAndIv, keyAndIv.length / 2, keyAndIv.length / 2)); //IV + AesGcmNoPadding.copyOfRange(keyAndIv, keyAndIv.length / 2, keyAndIv.length), MODE); //IV } @Override diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/Aes256GcmNoPadding.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/Aes256GcmNoPadding.java index 17b223795..e65677b10 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/Aes256GcmNoPadding.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/Aes256GcmNoPadding.java @@ -25,15 +25,15 @@ import javax.crypto.NoSuchPaddingException; public class Aes256GcmNoPadding extends AesGcmNoPadding { public static final String NAMESPACE = "urn:xmpp:ciphers:aes-256-gcm-nopadding:0"; - public Aes256GcmNoPadding() throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, + public Aes256GcmNoPadding(int MODE) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, InvalidAlgorithmParameterException { - super(256); + super(256, MODE); } - public Aes256GcmNoPadding(byte[] keyAndIv) throws NoSuchProviderException, InvalidAlgorithmParameterException, + public Aes256GcmNoPadding(byte[] keyAndIv, int MODE) throws NoSuchProviderException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException { super(AesGcmNoPadding.copyOfRange(keyAndIv, 0, keyAndIv.length / 2), //Key - AesGcmNoPadding.copyOfRange(keyAndIv, keyAndIv.length / 2, keyAndIv.length)); //IV + AesGcmNoPadding.copyOfRange(keyAndIv, keyAndIv.length / 2, keyAndIv.length), MODE); //IV } @Override diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/AesGcmNoPadding.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/AesGcmNoPadding.java index ef3b06358..5f4daa02b 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/AesGcmNoPadding.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/ciphers/AesGcmNoPadding.java @@ -37,7 +37,7 @@ public abstract class AesGcmNoPadding { protected final Cipher cipher; protected final byte[] key, iv, keyAndIv; - protected AesGcmNoPadding(int bits) throws NoSuchAlgorithmException, NoSuchProviderException, + protected AesGcmNoPadding(int bits, int MODE) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException { this.length = bits; int bytes = bits / 8; @@ -57,23 +57,34 @@ public abstract class AesGcmNoPadding { cipher = Cipher.getInstance(cipherMode, "BC"); SecretKey keySpec = new SecretKeySpec(key, keyType); IvParameterSpec ivSpec = new IvParameterSpec(iv); - cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); + cipher.init(MODE, keySpec, ivSpec); } - public static AesGcmNoPadding create(String cipherName) + public static AesGcmNoPadding createEncryptionKey(String cipherName) throws NoSuchProviderException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException { switch (cipherName) { case Aes128GcmNoPadding.NAMESPACE: - return new Aes128GcmNoPadding(); + return new Aes128GcmNoPadding(Cipher.ENCRYPT_MODE); case Aes256GcmNoPadding.NAMESPACE: - return new Aes256GcmNoPadding(); + return new Aes256GcmNoPadding(Cipher.ENCRYPT_MODE); default: throw new NoSuchAlgorithmException("Invalid cipher."); } } - public AesGcmNoPadding(byte[] key, byte[] iv) throws NoSuchPaddingException, NoSuchAlgorithmException, + /** + * Create a new AES key. + * @param key key + * @param iv iv + * @param MODE cipher mode (Cipher.ENCRYPT_MODE / Cipher.DECRYPT_MODE) + * @throws NoSuchPaddingException + * @throws NoSuchAlgorithmException + * @throws NoSuchProviderException + * @throws InvalidAlgorithmParameterException + * @throws InvalidKeyException + */ + public AesGcmNoPadding(byte[] key, byte[] iv, int MODE) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException { this.length = key.length * 8; this.key = key; @@ -86,18 +97,18 @@ public abstract class AesGcmNoPadding { cipher = Cipher.getInstance(cipherMode, "BC"); SecretKeySpec keySpec = new SecretKeySpec(key, keyType); IvParameterSpec ivSpec = new IvParameterSpec(iv); - cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); + cipher.init(MODE, keySpec, ivSpec); } - public static AesGcmNoPadding parse(String namespace, byte[] serialized) + public static AesGcmNoPadding createDecryptionKey(String namespace, byte[] serialized) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, NoSuchProviderException, InvalidKeyException, NoSuchPaddingException { switch (namespace) { case Aes128GcmNoPadding.NAMESPACE: - return new Aes128GcmNoPadding(serialized); + return new Aes128GcmNoPadding(serialized, Cipher.DECRYPT_MODE); case Aes256GcmNoPadding.NAMESPACE: - return new Aes256GcmNoPadding(serialized); + return new Aes256GcmNoPadding(serialized, Cipher.DECRYPT_MODE); default: throw new NoSuchAlgorithmException("Invalid cipher."); } } diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/jet/component/JetSecurity.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/jet/component/JetSecurity.java index d074e21d8..2e167d0d9 100644 --- a/smack-experimental/src/main/java/org/jivesoftware/smackx/jet/component/JetSecurity.java +++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/jet/component/JetSecurity.java @@ -71,7 +71,7 @@ public class JetSecurity extends JingleSecurity { SmackException.NoResponseException { this.methodNamespace = method.getNamespace(); - this.aesKey = AesGcmNoPadding.create(cipherName); + this.aesKey = AesGcmNoPadding.createEncryptionKey(cipherName); this.child = method.encryptJingleTransfer(recipient, aesKey.getKeyAndIv()); this.name = name; this.cipherName = cipherName; @@ -83,7 +83,7 @@ public class JetSecurity extends JingleSecurity { InvalidAlgorithmParameterException, NoSuchProviderException, InvalidKeyException, NoSuchPaddingException { byte[] keyAndIv = method.decryptJingleTransfer(sender, child); LOGGER.log(Level.INFO, "Transported JET Key has length: " + keyAndIv.length); - aesKey = AesGcmNoPadding.parse(cipherName, keyAndIv); + aesKey = AesGcmNoPadding.createDecryptionKey(cipherName, keyAndIv); } @Override diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/ciphers/AesGcmNoPaddingTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/ciphers/AesGcmNoPaddingTest.java new file mode 100644 index 000000000..aef86a359 --- /dev/null +++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/ciphers/AesGcmNoPaddingTest.java @@ -0,0 +1,76 @@ +/** + * + * Copyright 2017 Paul Schaub + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smackx.ciphers; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertTrue; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.util.Arrays; +import java.util.Random; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +import org.jivesoftware.smack.test.util.SmackTestSuite; + +import org.junit.Test; + +public class AesGcmNoPaddingTest extends SmackTestSuite { + + @Test + public void Aes128Test() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidKeyException, InvalidAlgorithmParameterException { + AesGcmNoPadding aes128 = AesGcmNoPadding.createEncryptionKey(Aes128GcmNoPadding.NAMESPACE); + assertNotNull(aes128); + assertEquals(16, aes128.getKey().length); + assertEquals(16, aes128.getIv().length); + assertEquals(32, aes128.getKeyAndIv().length); + assertNotNull(aes128.getCipher()); + assertEquals(128, aes128.getLength()); + } + + @Test + public void Aes256Test() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidKeyException, InvalidAlgorithmParameterException { + AesGcmNoPadding aes256 = AesGcmNoPadding.createEncryptionKey(Aes256GcmNoPadding.NAMESPACE); + assertNotNull(aes256); + assertEquals(32, aes256.getKey().length); + assertEquals(32, aes256.getIv().length); + assertEquals(64, aes256.getKeyAndIv().length); + assertNotNull(aes256.getCipher()); + assertEquals(256, aes256.getLength()); + } + + @Test + public void encryptionTest() throws NoSuchAlgorithmException, NoSuchPaddingException, NoSuchProviderException, InvalidKeyException, InvalidAlgorithmParameterException, BadPaddingException, IllegalBlockSizeException { + AesGcmNoPadding aes1 = AesGcmNoPadding.createEncryptionKey(Aes128GcmNoPadding.NAMESPACE); + AesGcmNoPadding aes2 = AesGcmNoPadding.createDecryptionKey(Aes128GcmNoPadding.NAMESPACE, aes1.getKeyAndIv()); + + byte[] data = new byte[4096]; + new Random().nextBytes(data); + + byte[] enc = aes1.getCipher().doFinal(data); + assertFalse(Arrays.equals(data, enc)); + + byte[] dec = aes2.getCipher().doFinal(enc); + assertTrue(Arrays.equals(dec, data)); + } +}