1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.crypto.jna;
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.NoSuchAlgorithmException;
26 import java.security.spec.AlgorithmParameterSpec;
27 import java.util.Objects;
28 import java.util.Properties;
29
30 import javax.crypto.BadPaddingException;
31 import javax.crypto.Cipher;
32 import javax.crypto.IllegalBlockSizeException;
33 import javax.crypto.ShortBufferException;
34 import javax.crypto.spec.IvParameterSpec;
35
36 import org.apache.commons.crypto.cipher.CryptoCipher;
37 import org.apache.commons.crypto.cipher.CryptoCipherFactory;
38 import org.apache.commons.crypto.utils.Transformation;
39
40 import com.sun.jna.NativeLong;
41 import com.sun.jna.ptr.PointerByReference;
42
43
44
45
46 final class OpenSslJnaCipher implements CryptoCipher {
47
48
49
50
51 private enum AlgorithmMode {
52 AES_CTR, AES_CBC;
53
54
55
56
57
58
59
60
61
62 static AlgorithmMode get(final String algorithm, final String mode) throws NoSuchAlgorithmException {
63 try {
64 return AlgorithmMode.valueOf(algorithm + "_" + mode);
65 } catch (final Exception e) {
66 throw new NoSuchAlgorithmException("Algorithm not supported: " + algorithm + " and mode: " + mode);
67 }
68 }
69 }
70 private PointerByReference algo;
71 private final PointerByReference context;
72 private final AlgorithmMode algorithmMode;
73 private final int padding;
74 private final String transformation;
75
76 private final int IV_LENGTH = 16;
77
78
79
80
81
82
83
84
85 public OpenSslJnaCipher(final Properties props, final String transformation)
86 throws GeneralSecurityException {
87 if (!OpenSslJna.isEnabled()) {
88 throw new GeneralSecurityException("Could not enable JNA access", OpenSslJna.initialisationError());
89 }
90 this.transformation = transformation;
91 final Transformation transform = Transformation.parse(transformation);
92 algorithmMode = AlgorithmMode.get(transform.getAlgorithm(), transform.getMode());
93
94 if (algorithmMode != AlgorithmMode.AES_CBC && algorithmMode != AlgorithmMode.AES_CTR) {
95 throw new GeneralSecurityException("Unknown algorithm " + transform.getAlgorithm() + "_" + transform.getMode());
96 }
97
98 padding = transform.getPadding().ordinal();
99 context = OpenSslNativeJna.EVP_CIPHER_CTX_new();
100
101 }
102
103
104
105
106 @Override
107 public void close() {
108 if (context != null) {
109 OpenSslNativeJna.EVP_CIPHER_CTX_cleanup(context);
110
111
112
113
114 }
115 }
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141 @Override
142 public int doFinal(final byte[] input, final int inputOffset, final int inputLen, final byte[] output,
143 final int outputOffset) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
144 final ByteBuffer outputBuf = ByteBuffer.wrap(output, outputOffset, output.length - outputOffset);
145 final ByteBuffer inputBuf = ByteBuffer.wrap(input, inputOffset, inputLen);
146 return doFinal(inputBuf, outputBuf);
147 }
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171 @Override
172 public int doFinal(final ByteBuffer inBuffer, final ByteBuffer outBuffer)
173 throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
174 final int uptLen = update(inBuffer, outBuffer);
175 final int[] outlen = new int[1];
176 throwOnError(OpenSslNativeJna.EVP_CipherFinal_ex(context, outBuffer, outlen));
177 final int len = uptLen + outlen[0];
178 outBuffer.position(outBuffer.position() + outlen[0]);
179 return len;
180 }
181
182 @Override
183 protected void finalize() throws Throwable {
184 OpenSslNativeJna.EVP_CIPHER_CTX_free(context);
185 super.finalize();
186 }
187
188 @Override
189 public String getAlgorithm() {
190 return transformation;
191 }
192
193 @Override
194 public int getBlockSize() {
195 return CryptoCipherFactory.AES_BLOCK_SIZE;
196 }
197
198
199
200
201
202
203
204
205
206
207 @Override
208 public void init(final int mode, final Key key, final AlgorithmParameterSpec params)
209 throws InvalidKeyException, InvalidAlgorithmParameterException {
210 Objects.requireNonNull(key, "key");
211 Objects.requireNonNull(params, "params");
212 final int cipherMode = mode == Cipher.ENCRYPT_MODE ? OpenSslNativeJna.OOSL_JNA_ENCRYPT_MODE : OpenSslNativeJna.OOSL_JNA_DECRYPT_MODE;
213 if (!(params instanceof IvParameterSpec)) {
214
215
216 throw new InvalidAlgorithmParameterException("Illegal parameters");
217 }
218 final byte[] iv = ((IvParameterSpec) params).getIV();
219
220 if ((algorithmMode == AlgorithmMode.AES_CBC || algorithmMode == AlgorithmMode.AES_CTR) && iv.length != IV_LENGTH) {
221 throw new InvalidAlgorithmParameterException("Wrong IV length: must be 16 bytes long");
222 }
223 final int keyEncodedLength = key.getEncoded().length;
224
225 if (algorithmMode == AlgorithmMode.AES_CBC) {
226 switch (keyEncodedLength) {
227 case 16:
228 algo = OpenSslNativeJna.EVP_aes_128_cbc();
229 break;
230 case 24:
231 algo = OpenSslNativeJna.EVP_aes_192_cbc();
232 break;
233 case 32:
234 algo = OpenSslNativeJna.EVP_aes_256_cbc();
235 break;
236 default:
237 throw new InvalidKeyException("keysize unsupported (" + keyEncodedLength + ")");
238 }
239
240 } else {
241 switch (keyEncodedLength) {
242 case 16:
243 algo = OpenSslNativeJna.EVP_aes_128_ctr();
244 break;
245 case 24:
246 algo = OpenSslNativeJna.EVP_aes_192_ctr();
247 break;
248 case 32:
249 algo = OpenSslNativeJna.EVP_aes_256_ctr();
250 break;
251 default:
252 throw new InvalidKeyException("keysize unsupported (" + keyEncodedLength + ")");
253 }
254 }
255
256 throwOnError(OpenSslNativeJna.EVP_CipherInit_ex(context, algo, null, key.getEncoded(), iv, cipherMode));
257 throwOnError(OpenSslNativeJna.EVP_CIPHER_CTX_set_padding(context, padding));
258 }
259
260
261
262
263 private void throwOnError(final int retVal) {
264 if (retVal != 1) {
265 final NativeLong err = OpenSslNativeJna.ERR_peek_error();
266 final String errdesc = OpenSslNativeJna.ERR_error_string(err, null);
267
268 if (context != null) {
269 OpenSslNativeJna.EVP_CIPHER_CTX_cleanup(context);
270 }
271 throw new IllegalStateException(
272 "return code " + retVal + " from OpenSSL. Err code is " + err + ": " + errdesc);
273 }
274 }
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289 @Override
290 public int update(final byte[] input, final int inputOffset, final int inputLen, final byte[] output,
291 final int outputOffset) throws ShortBufferException {
292 final ByteBuffer outputBuf = ByteBuffer.wrap(output, outputOffset, output.length - outputOffset);
293 final ByteBuffer inputBuf = ByteBuffer.wrap(input, inputOffset, inputLen);
294 return update(inputBuf, outputBuf);
295 }
296
297
298
299
300
301
302
303
304
305
306
307 @Override
308 public int update(final ByteBuffer inBuffer, final ByteBuffer outBuffer) throws ShortBufferException {
309 final int[] outlen = new int[1];
310 throwOnError(OpenSslNativeJna.EVP_CipherUpdate(context, outBuffer, outlen, inBuffer, inBuffer.remaining()));
311 final int len = outlen[0];
312 inBuffer.position(inBuffer.limit());
313 outBuffer.position(outBuffer.position() + len);
314 return len;
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340 @Override
341 public void updateAAD(final byte[] aad)
342 throws IllegalArgumentException, IllegalStateException, UnsupportedOperationException {
343
344 throw new UnsupportedOperationException("This is unsupported in Jna Cipher");
345 }
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370 @Override
371 public void updateAAD(final ByteBuffer aad)
372 throws IllegalArgumentException, IllegalStateException, UnsupportedOperationException {
373
374 throw new UnsupportedOperationException("This is unsupported in Jna Cipher");
375 }
376 }