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 }