001/** 002 * 003 * Copyright 2017 Paul Schaub, 2019-2023 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.omemo.internal; 018 019import java.nio.charset.StandardCharsets; 020import java.security.InvalidAlgorithmParameterException; 021import java.security.InvalidKeyException; 022import java.security.NoSuchAlgorithmException; 023 024import javax.crypto.BadPaddingException; 025import javax.crypto.Cipher; 026import javax.crypto.IllegalBlockSizeException; 027import javax.crypto.NoSuchPaddingException; 028import javax.crypto.SecretKey; 029import javax.crypto.spec.IvParameterSpec; 030import javax.crypto.spec.SecretKeySpec; 031 032import org.jivesoftware.smack.util.RandomUtil; 033 034import org.jivesoftware.smackx.omemo.util.OmemoConstants; 035import org.jivesoftware.smackx.omemo.util.OmemoMessageBuilder; 036 037public class OmemoAesCipher { 038 039 static { 040 byte[] iv = OmemoMessageBuilder.generateIv(); 041 byte[] key = new byte[16]; 042 RandomUtil.fillWithSecureRandom(key); 043 044 try { 045 encryptAesGcmNoPadding("This is just a test", key, iv); 046 } catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException 047 | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { 048 String message = "Unable to perform " + OmemoConstants.Crypto.CIPHERMODE 049 + " operation requires by OMEMO. Ensure that a suitable crypto provider for is available." 050 + " For example Bouncycastle on Android (BouncyCastleProvider)"; 051 throw new AssertionError(message, e); 052 } 053 } 054 055 private enum CipherOpmode { 056 encrypt(Cipher.ENCRYPT_MODE), 057 decrypt(Cipher.DECRYPT_MODE), 058 ; 059 060 public final int opmodeInt; 061 062 CipherOpmode(int opmodeInt) { 063 this.opmodeInt = opmodeInt; 064 } 065 } 066 067 private static byte[] performCipherOperation(CipherOpmode opmode, byte[] input, byte[] key, 068 byte[] initializationVector) 069 throws IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, 070 NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { 071 SecretKey secretKey = new SecretKeySpec(key, OmemoConstants.Crypto.KEYTYPE); 072 IvParameterSpec ivSpec = new IvParameterSpec(initializationVector); 073 074 Cipher cipher = Cipher.getInstance(OmemoConstants.Crypto.CIPHERMODE); 075 cipher.init(opmode.opmodeInt, secretKey, ivSpec); 076 077 byte[] ciphertext = cipher.doFinal(input); 078 079 return ciphertext; 080 } 081 082 public static byte[] decryptAesGcmNoPadding(byte[] ciphertext, byte[] key, byte[] initializationVector) 083 throws InvalidKeyException, IllegalBlockSizeException, BadPaddingException, 084 NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException { 085 return performCipherOperation(CipherOpmode.decrypt, ciphertext, key, initializationVector); 086 } 087 088 public static byte[] encryptAesGcmNoPadding(byte[] plaintext, byte[] key, byte[] initializationVector) 089 throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, 090 InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { 091 return performCipherOperation(CipherOpmode.encrypt, plaintext, key, initializationVector); 092 } 093 094 public static byte[] encryptAesGcmNoPadding(String plaintext, byte[] key, byte[] initializationVector) 095 throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, 096 InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException { 097 byte[] plaintextBytes = plaintext.getBytes(StandardCharsets.UTF_8); 098 return encryptAesGcmNoPadding(plaintextBytes, key, initializationVector); 099 } 100}