1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 package org.apache.commons.crypto.cipher; 19 20 import java.nio.ByteBuffer; 21 import java.security.GeneralSecurityException; 22 import java.security.InvalidAlgorithmParameterException; 23 import java.security.InvalidKeyException; 24 import java.security.Key; 25 import java.security.spec.AlgorithmParameterSpec; 26 import java.util.Objects; 27 import java.util.Properties; 28 29 import javax.crypto.BadPaddingException; 30 import javax.crypto.Cipher; 31 import javax.crypto.IllegalBlockSizeException; 32 import javax.crypto.ShortBufferException; 33 34 /** 35 * Implements the CryptoCipher using JNI into OpenSSL. 36 * <p> 37 * this class is not public/protected so does not appear in the main Javadoc Please ensure that property use is documented in the enum 38 * CryptoRandomFactory.RandomProvider 39 * </p> 40 */ 41 final class OpenSslCipher implements CryptoCipher { 42 43 private final OpenSsl openSslEngine; 44 private boolean initialized; 45 46 private final String transformation; 47 48 /** 49 * Constructs a {@link CryptoCipher} using JNI into OpenSSL 50 * 51 * @param props properties for OpenSSL openSslEngine (unused) 52 * @param transformation transformation for OpenSSL openSslEngine (algorithm/mode/padding) 53 * @throws GeneralSecurityException if OpenSSL openSslEngine initialize failed 54 */ 55 public OpenSslCipher(final Properties props, final String transformation) // NOPMD 56 throws GeneralSecurityException { 57 this.transformation = transformation; 58 59 final Throwable loadingFailureReason = OpenSsl.getLoadingFailureReason(); 60 if (loadingFailureReason != null) { 61 throw new IllegalStateException(loadingFailureReason); 62 } 63 64 openSslEngine = OpenSsl.getInstance(transformation); 65 } 66 67 /** 68 * Closes the OpenSSL openSslEngine. Clean the OpenSsl native context. 69 */ 70 @Override 71 public void close() { 72 openSslEngine.clean(); 73 } 74 75 /** 76 * Encrypts or decrypts data in a single-part operation, or finishes a 77 * multiple-part operation. 78 * 79 * @param input the input byte array 80 * @param inputOffset the offset in input where the input starts 81 * @param inputLen the input length 82 * @param output the byte array for the result 83 * @param outputOffset the offset in output where the result is stored 84 * @return the number of bytes stored in output 85 * @throws ShortBufferException if the given output byte array is too small 86 * to hold the result 87 * @throws BadPaddingException if this openSslEngine is in decryption mode, and 88 * (un)padding has been requested, but the decrypted data is not 89 * bounded by the appropriate padding bytes 90 * @throws IllegalBlockSizeException if this openSslEngine is a block openSslEngine, no 91 * padding has been requested (only in encryption mode), and the 92 * total input length of the data processed by this openSslEngine is not a 93 * multiple of block size; or if this encryption algorithm is unable 94 * to process the input data provided. 95 */ 96 @Override 97 public int doFinal(final byte[] input, final int inputOffset, final int inputLen, 98 final byte[] output, final int outputOffset) throws ShortBufferException, 99 IllegalBlockSizeException, BadPaddingException { 100 return openSslEngine.doFinal(input, inputOffset, inputLen, output,outputOffset); 101 } 102 103 /** 104 * Encrypts or decrypts data in a single-part operation, or finishes a 105 * multiple-part operation. The data is encrypted or decrypted, depending on 106 * how this openSslEngine was initialized. 107 * 108 * @param inBuffer the input ByteBuffer 109 * @param outBuffer the output ByteBuffer 110 * @return int number of bytes stored in {@code output} 111 * @throws BadPaddingException if this openSslEngine is in decryption mode, and 112 * (un)padding has been requested, but the decrypted data is not 113 * bounded by the appropriate padding bytes 114 * @throws IllegalBlockSizeException if this openSslEngine is a block openSslEngine, no 115 * padding has been requested (only in encryption mode), and the 116 * total input length of the data processed by this openSslEngine is not a 117 * multiple of block size; or if this encryption algorithm is unable 118 * to process the input data provided. 119 * @throws ShortBufferException if the given output buffer is too small to 120 * hold the result 121 */ 122 @Override 123 public int doFinal(final ByteBuffer inBuffer, final ByteBuffer outBuffer) 124 throws ShortBufferException, IllegalBlockSizeException, 125 BadPaddingException { 126 return openSslEngine.doFinal(inBuffer, outBuffer); 127 } 128 129 /** 130 * Returns the algorithm name of this {@code CryptoCipher} object. 131 * 132 * <p> 133 * This is the same name that was specified in one of the 134 * {@code CryptoCipherFactory#getInstance} calls that created this 135 * {@code CryptoCipher} object.. 136 * </p> 137 * 138 * @return the algorithm name of this {@code CryptoCipher} object. 139 */ 140 @Override 141 public String getAlgorithm() { 142 return transformation; 143 } 144 145 /** 146 * Returns the block size (in bytes). 147 * 148 * @return the block size (in bytes), or 0 if the underlying algorithm is 149 * not a block openSslEngine 150 */ 151 @Override 152 public int getBlockSize() { 153 return CryptoCipherFactory.AES_BLOCK_SIZE; 154 } 155 156 /** 157 * Initializes the openSslEngine with mode, key and iv. 158 * 159 * @param mode {@link Cipher#ENCRYPT_MODE} or {@link Cipher#DECRYPT_MODE} 160 * @param key crypto key for the openSslEngine 161 * @param params the algorithm parameters 162 * @throws InvalidKeyException If key length is invalid 163 * @throws InvalidAlgorithmParameterException if IV length is wrong 164 */ 165 @Override 166 public void init(final int mode, final Key key, final AlgorithmParameterSpec params) 167 throws InvalidKeyException, InvalidAlgorithmParameterException { 168 Objects.requireNonNull(key, "key"); 169 Objects.requireNonNull(params, "params"); 170 171 final int cipherMode = mode == Cipher.ENCRYPT_MODE ? OpenSsl.ENCRYPT_MODE: OpenSsl.DECRYPT_MODE; 172 openSslEngine.init(cipherMode, key.getEncoded(), params); 173 initialized = true; 174 } 175 176 /** 177 * Continues a multiple-part encryption/decryption operation. The data is 178 * encrypted or decrypted, depending on how this openSslEngine was initialized. 179 * 180 * @param input the input byte array 181 * @param inputOffset the offset in input where the input starts 182 * @param inputLen the input length 183 * @param output the byte array for the result 184 * @param outputOffset the offset in output where the result is stored 185 * @return the number of bytes stored in output 186 * @throws ShortBufferException if there is insufficient space in the output 187 * byte array 188 */ 189 @Override 190 public int update(final byte[] input, final int inputOffset, final int inputLen, 191 final byte[] output, final int outputOffset) throws ShortBufferException { 192 return openSslEngine 193 .update(input, inputOffset, inputLen, output, outputOffset); 194 } 195 196 197 /** 198 * Continues a multiple-part encryption/decryption operation. The data is 199 * encrypted or decrypted, depending on how this openSslEngine was initialized. 200 * 201 * @param inBuffer the input ByteBuffer 202 * @param outBuffer the output ByteBuffer 203 * @return int number of bytes stored in {@code output} 204 * @throws ShortBufferException if there is insufficient space in the output 205 * buffer 206 */ 207 @Override 208 public int update(final ByteBuffer inBuffer, final ByteBuffer outBuffer) 209 throws ShortBufferException { 210 return openSslEngine.update(inBuffer, outBuffer); 211 } 212 213 /** 214 * Continues a multi-part update of the Additional Authentication 215 * Data (AAD). 216 * <p> 217 * Calls to this method provide AAD to the opensslEngine when operating in 218 * modes such as AEAD (GCM). If this opensslEngine is operating in 219 * either GCM mode, all AAD must be supplied before beginning 220 * operations on the ciphertext (via the {@code update} and 221 * {@code doFinal} methods). 222 * </p> 223 * 224 * @param aad the buffer containing the Additional Authentication Data 225 * 226 * @throws IllegalArgumentException if the {@code aad} 227 * byte array is null 228 * @throws IllegalStateException if this opensslEngine is in a wrong state 229 * (e.g., has not been initialized), does not accept AAD, or if 230 * operating in either GCM mode and one of the {@code update} 231 * methods has already been called for the active 232 * encryption/decryption operation 233 * @throws UnsupportedOperationException if the implementation {@code opensslEngine} 234 * doesn't support this operation. 235 */ 236 @Override 237 public void updateAAD(final byte[] aad) throws IllegalArgumentException, 238 IllegalStateException, UnsupportedOperationException { 239 if (aad == null) { 240 throw new IllegalArgumentException("aad buffer is null"); 241 } 242 if (!initialized) { 243 throw new IllegalStateException("Cipher not initialized"); 244 } 245 if (aad.length == 0) { 246 return; 247 } 248 249 openSslEngine.updateAAD(aad); 250 } 251 252 253 /** 254 * Continues a multi-part update of the Additional Authentication 255 * Data (AAD). 256 * <p> 257 * Calls to this method provide AAD to the opensslEngine when operating in 258 * modes such as AEAD (GCM). If this opensslEngine is operating in 259 * either GCM mode, all AAD must be supplied before beginning 260 * operations on the ciphertext (via the {@code update} and 261 * {@code doFinal} methods). 262 * </p> 263 * 264 * @param aad the buffer containing the Additional Authentication Data 265 * 266 * @throws IllegalArgumentException if the {@code aad} 267 * byte array is null 268 * @throws IllegalStateException if this opensslEngine is in a wrong state 269 * (e.g., has not been initialized), does not accept AAD, or if 270 * operating in either GCM mode and one of the {@code update} 271 * methods has already been called for the active 272 * encryption/decryption operation 273 * @throws UnsupportedOperationException if the implementation {@code opensslEngine} 274 * doesn't support this operation. 275 */ 276 @Override 277 public void updateAAD(final ByteBuffer aad) throws IllegalArgumentException, 278 IllegalStateException, UnsupportedOperationException { 279 if (aad == null) { 280 throw new IllegalArgumentException("aad buffer is null"); 281 } 282 if (!initialized) { 283 throw new IllegalStateException("Cipher not initialized"); 284 } 285 286 final int aadLen = aad.limit() - aad.position(); 287 if (aadLen == 0) { 288 return; 289 } 290 final byte[] aadBytes = new byte[aadLen]; 291 aad.get(aadBytes); 292 openSslEngine.updateAAD(aadBytes); 293 } 294 }