/** * * Copyright 2017 Paul Schaub, 2019-2021 Florian Schmaus * * 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.omemo.internal; import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.jivesoftware.smack.util.RandomUtil; import org.jivesoftware.smackx.omemo.util.OmemoConstants; import org.jivesoftware.smackx.omemo.util.OmemoMessageBuilder; public class OmemoAesCipher { static { byte[] iv = OmemoMessageBuilder.generateIv(); byte[] key = new byte[16]; RandomUtil.fillWithSecureRandom(key); try { encryptAesGcmNoPadding("This is just a test", key, iv); } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { String message = "Unable to perform " + OmemoConstants.Crypto.CIPHERMODE + " operation requires by OMEMO. Ensure that a suitable crypto provider for is available." + " For example Bouncycastle on Android (BouncyCastleProvider)"; throw new AssertionError(message); } } private enum CipherOpmode { encrypt(Cipher.ENCRYPT_MODE), decrypt(Cipher.DECRYPT_MODE), ; public final int opmodeInt; CipherOpmode(int opmodeInt) { this.opmodeInt = opmodeInt; } } private static byte[] performCipherOperation(CipherOpmode opmode, byte[] input, byte[] key, byte[] initializationVector) throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { SecretKey secretKey = new SecretKeySpec(key, OmemoConstants.Crypto.KEYTYPE); IvParameterSpec ivSpec = new IvParameterSpec(initializationVector); Cipher cipher = Cipher.getInstance(OmemoConstants.Crypto.CIPHERMODE); cipher.init(opmode.opmodeInt, secretKey, ivSpec); byte[] ciphertext = cipher.doFinal(input); return ciphertext; } public static byte[] decryptAesGcmNoPadding(byte[] ciphertext, byte[] key, byte[] initializationVector) throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException { return performCipherOperation(CipherOpmode.decrypt, ciphertext, key, initializationVector); } public static byte[] encryptAesGcmNoPadding(byte[] plaintext, byte[] key, byte[] initializationVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { return performCipherOperation(CipherOpmode.encrypt, plaintext, key, initializationVector); } public static byte[] encryptAesGcmNoPadding(String plaintext, byte[] key, byte[] initializationVector) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8); return encryptAesGcmNoPadding(plaintextBytes, key, initializationVector); } }