Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java
66646 views
1
/*
2
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
package sun.security.pkcs11;
26
27
import java.io.ByteArrayOutputStream;
28
import java.nio.ByteBuffer;
29
import java.util.Arrays;
30
import java.util.Locale;
31
32
import java.security.*;
33
import java.security.spec.*;
34
35
import javax.crypto.*;
36
import javax.crypto.spec.*;
37
38
import sun.nio.ch.DirectBuffer;
39
import sun.security.jca.JCAUtil;
40
import sun.security.pkcs11.wrapper.*;
41
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
42
import static sun.security.pkcs11.wrapper.PKCS11Exception.*;
43
44
/**
45
* P11 AEAD Cipher implementation class. This class currently supports
46
* AES cipher in GCM mode and CHACHA20-POLY1305 cipher.
47
*
48
* Note that AEAD modes do not use padding, so this class does not have
49
* its own padding impl. In addition, some vendors such as NSS may not support
50
* multi-part encryption/decryption for AEAD cipher algorithms, thus the
51
* current impl uses PKCS#11 C_Encrypt/C_Decrypt calls and buffers data until
52
* doFinal is called.
53
*
54
* @since 13
55
*/
56
final class P11AEADCipher extends CipherSpi {
57
58
// supported AEAD algorithms/transformations
59
private enum Transformation {
60
AES_GCM("AES", "GCM", "NOPADDING", 16, 16),
61
CHACHA20_POLY1305("CHACHA20", "NONE", "NOPADDING", 12, 16);
62
63
final String keyAlgo;
64
final String mode;
65
final String padding;
66
final int defIvLen; // in bytes
67
final int defTagLen; // in bytes
68
69
Transformation(String keyAlgo, String mode, String padding,
70
int defIvLen, int defTagLen) {
71
this.keyAlgo = keyAlgo;
72
this.mode = mode;
73
this.padding = padding;
74
this.defIvLen = defIvLen;
75
this.defTagLen = defTagLen;
76
}
77
}
78
79
// token instance
80
private final Token token;
81
82
// mechanism id
83
private final long mechanism;
84
85
// type of this AEAD cipher, one of Transformation enum above
86
private final Transformation type;
87
88
// acceptable key size in bytes, -1 if more than 1 key sizes are accepted
89
private final int fixedKeySize;
90
91
// associated session, if any
92
private Session session = null;
93
94
// key, if init() was called
95
private P11Key p11Key = null;
96
97
// flag indicating whether an operation is initialized
98
private boolean initialized = false;
99
100
// flag indicating encrypt or decrypt mode
101
private boolean encrypt = true;
102
103
// parameters
104
private byte[] iv = null;
105
private int tagLen = -1;
106
private SecureRandom random = JCAUtil.getSecureRandom();
107
108
// dataBuffer is cleared upon doFinal calls
109
private ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream();
110
// aadBuffer is cleared upon successful init calls
111
private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
112
private boolean updateCalled = false;
113
114
private boolean requireReinit = false;
115
private P11Key lastEncKey = null;
116
private byte[] lastEncIv = null;
117
118
P11AEADCipher(Token token, String algorithm, long mechanism)
119
throws PKCS11Exception, NoSuchAlgorithmException {
120
super();
121
this.token = token;
122
this.mechanism = mechanism;
123
124
String[] algoParts = algorithm.split("/");
125
if (algoParts[0].startsWith("AES")) {
126
// for AES_GCM, need 3 parts
127
if (algoParts.length != 3) {
128
throw new AssertionError("Invalid Transformation format: " +
129
algorithm);
130
}
131
int index = algoParts[0].indexOf('_');
132
if (index != -1) {
133
// should be well-formed since we specify what we support
134
fixedKeySize = Integer.parseInt(algoParts[0].substring(index+1)) >> 3;
135
} else {
136
fixedKeySize = -1;
137
}
138
this.type = Transformation.AES_GCM;
139
engineSetMode(algoParts[1]);
140
try {
141
engineSetPadding(algoParts[2]);
142
} catch (NoSuchPaddingException e) {
143
throw new NoSuchAlgorithmException();
144
}
145
} else if (algoParts[0].equals("ChaCha20-Poly1305")) {
146
fixedKeySize = 32;
147
this.type = Transformation.CHACHA20_POLY1305;
148
if (algoParts.length > 3) {
149
throw new AssertionError(
150
"Invalid Transformation format: " + algorithm);
151
} else {
152
if (algoParts.length > 1) {
153
engineSetMode(algoParts[1]);
154
}
155
try {
156
if (algoParts.length > 2) {
157
engineSetPadding(algoParts[2]);
158
}
159
} catch (NoSuchPaddingException e) {
160
throw new NoSuchAlgorithmException();
161
}
162
}
163
} else {
164
throw new AssertionError("Unsupported transformation " + algorithm);
165
}
166
}
167
168
@Override
169
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
170
if (!mode.toUpperCase(Locale.ENGLISH).equals(type.mode)) {
171
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
172
}
173
}
174
175
// see JCE spec
176
@Override
177
protected void engineSetPadding(String padding)
178
throws NoSuchPaddingException {
179
if (!padding.toUpperCase(Locale.ENGLISH).equals(type.padding)) {
180
throw new NoSuchPaddingException("Unsupported padding " + padding);
181
}
182
}
183
184
// see JCE spec
185
@Override
186
protected int engineGetBlockSize() {
187
return switch (type) {
188
case AES_GCM -> 16;
189
case CHACHA20_POLY1305 -> 0;
190
default -> throw new AssertionError("Unsupported type " + type);
191
};
192
}
193
194
// see JCE spec
195
@Override
196
protected int engineGetOutputSize(int inputLen) {
197
return doFinalLength(inputLen);
198
}
199
200
// see JCE spec
201
@Override
202
protected byte[] engineGetIV() {
203
return (iv == null) ? null : iv.clone();
204
}
205
206
// see JCE spec
207
protected AlgorithmParameters engineGetParameters() {
208
String apAlgo;
209
AlgorithmParameterSpec spec = null;
210
switch (type) {
211
case AES_GCM:
212
apAlgo = "GCM";
213
if (encrypt && iv == null && tagLen == -1) {
214
iv = new byte[type.defIvLen];
215
tagLen = type.defTagLen;
216
random.nextBytes(iv);
217
}
218
if (iv != null) {
219
spec = new GCMParameterSpec(tagLen << 3, iv);
220
}
221
break;
222
case CHACHA20_POLY1305:
223
if (encrypt && iv == null) {
224
iv = new byte[type.defIvLen];
225
random.nextBytes(iv);
226
}
227
apAlgo = "ChaCha20-Poly1305";
228
if (iv != null) {
229
spec = new IvParameterSpec(iv);
230
}
231
break;
232
default:
233
throw new AssertionError("Unsupported type " + type);
234
}
235
if (spec != null) {
236
try {
237
AlgorithmParameters params =
238
AlgorithmParameters.getInstance(apAlgo);
239
params.init(spec);
240
return params;
241
} catch (GeneralSecurityException e) {
242
// NoSuchAlgorithmException, NoSuchProviderException
243
// InvalidParameterSpecException
244
throw new ProviderException("Could not encode parameters", e);
245
}
246
}
247
return null;
248
}
249
250
// see JCE spec
251
protected void engineInit(int opmode, Key key, SecureRandom sr)
252
throws InvalidKeyException {
253
if (opmode == Cipher.DECRYPT_MODE) {
254
throw new InvalidKeyException("Parameters required for decryption");
255
}
256
updateCalled = false;
257
try {
258
implInit(opmode, key, null, -1, sr);
259
} catch (InvalidAlgorithmParameterException e) {
260
throw new InvalidKeyException("init() failed", e);
261
}
262
}
263
264
// see JCE spec
265
protected void engineInit(int opmode, Key key,
266
AlgorithmParameterSpec params, SecureRandom sr)
267
throws InvalidKeyException, InvalidAlgorithmParameterException {
268
if (opmode == Cipher.DECRYPT_MODE && params == null) {
269
throw new InvalidAlgorithmParameterException
270
("Parameters required for decryption");
271
}
272
updateCalled = false;
273
byte[] ivValue = null;
274
int tagLen = -1;
275
switch (type) {
276
case AES_GCM:
277
if (params != null) {
278
if (!(params instanceof GCMParameterSpec)) {
279
throw new InvalidAlgorithmParameterException
280
("Only GCMParameterSpec is supported");
281
}
282
ivValue = ((GCMParameterSpec) params).getIV();
283
tagLen = ((GCMParameterSpec) params).getTLen() >> 3;
284
}
285
break;
286
case CHACHA20_POLY1305:
287
if (params != null) {
288
if (!(params instanceof IvParameterSpec)) {
289
throw new InvalidAlgorithmParameterException
290
("Only IvParameterSpec is supported");
291
}
292
ivValue = ((IvParameterSpec) params).getIV();
293
tagLen = type.defTagLen;
294
}
295
break;
296
default:
297
throw new AssertionError("Unsupported type " + type);
298
};
299
implInit(opmode, key, ivValue, tagLen, sr);
300
}
301
302
// see JCE spec
303
protected void engineInit(int opmode, Key key, AlgorithmParameters params,
304
SecureRandom sr)
305
throws InvalidKeyException, InvalidAlgorithmParameterException {
306
if (opmode == Cipher.DECRYPT_MODE && params == null) {
307
throw new InvalidAlgorithmParameterException
308
("Parameters required for decryption");
309
}
310
updateCalled = false;
311
try {
312
AlgorithmParameterSpec paramSpec = null;
313
if (params != null) {
314
switch (type) {
315
case AES_GCM:
316
paramSpec =
317
params.getParameterSpec(GCMParameterSpec.class);
318
break;
319
case CHACHA20_POLY1305:
320
paramSpec =
321
params.getParameterSpec(IvParameterSpec.class);
322
break;
323
default:
324
throw new AssertionError("Unsupported type " + type);
325
}
326
}
327
engineInit(opmode, key, paramSpec, sr);
328
} catch (InvalidParameterSpecException ex) {
329
throw new InvalidAlgorithmParameterException(ex);
330
}
331
}
332
333
// actual init() implementation
334
private void implInit(int opmode, Key key, byte[] iv, int tagLen,
335
SecureRandom sr)
336
throws InvalidKeyException, InvalidAlgorithmParameterException {
337
reset(true);
338
if (fixedKeySize != -1 &&
339
((key instanceof P11Key) ? ((P11Key) key).length() >> 3 :
340
key.getEncoded().length) != fixedKeySize) {
341
throw new InvalidKeyException("Key size is invalid");
342
}
343
P11Key newKey = P11SecretKeyFactory.convertKey(token, key,
344
type.keyAlgo);
345
switch (opmode) {
346
case Cipher.ENCRYPT_MODE:
347
encrypt = true;
348
requireReinit = Arrays.equals(iv, lastEncIv) &&
349
(newKey == lastEncKey);
350
if (requireReinit) {
351
throw new InvalidAlgorithmParameterException(
352
"Cannot reuse the same key and iv pair");
353
}
354
break;
355
case Cipher.DECRYPT_MODE:
356
encrypt = false;
357
requireReinit = false;
358
break;
359
case Cipher.WRAP_MODE:
360
case Cipher.UNWRAP_MODE:
361
throw new UnsupportedOperationException
362
("Unsupported mode: " + opmode);
363
default:
364
// should never happen; checked by Cipher.init()
365
throw new AssertionError("Unknown mode: " + opmode);
366
}
367
368
// decryption without parameters is checked in all engineInit() calls
369
if (sr != null) {
370
this.random = sr;
371
}
372
373
if (iv == null && tagLen == -1) {
374
// generate default values
375
switch (type) {
376
case AES_GCM:
377
iv = new byte[type.defIvLen];
378
this.random.nextBytes(iv);
379
tagLen = type.defTagLen;
380
break;
381
case CHACHA20_POLY1305:
382
iv = new byte[type.defIvLen];
383
this.random.nextBytes(iv);
384
tagLen = type.defTagLen;
385
break;
386
default:
387
throw new AssertionError("Unsupported type " + type);
388
}
389
}
390
this.iv = iv;
391
this.tagLen = tagLen;
392
this.p11Key = newKey;
393
try {
394
initialize();
395
} catch (PKCS11Exception e) {
396
if (e.getErrorCode() == CKR_MECHANISM_PARAM_INVALID) {
397
throw new InvalidAlgorithmParameterException("Bad params", e);
398
}
399
throw new InvalidKeyException("Could not initialize cipher", e);
400
}
401
}
402
403
private void cancelOperation() {
404
// cancel operation by finishing it; avoid killSession as some
405
// hardware vendors may require re-login
406
int bufLen = doFinalLength(0);
407
byte[] buffer = new byte[bufLen];
408
byte[] in = dataBuffer.toByteArray();
409
int inLen = in.length;
410
try {
411
if (encrypt) {
412
token.p11.C_Encrypt(session.id(), 0, in, 0, inLen,
413
0, buffer, 0, bufLen);
414
} else {
415
token.p11.C_Decrypt(session.id(), 0, in, 0, inLen,
416
0, buffer, 0, bufLen);
417
}
418
} catch (PKCS11Exception e) {
419
if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {
420
// Cancel Operation may be invoked after an error on a PKCS#11
421
// call. If the operation inside the token was already cancelled,
422
// do not fail here. This is part of a defensive mechanism for
423
// PKCS#11 libraries that do not strictly follow the standard.
424
return;
425
}
426
if (encrypt) {
427
throw new ProviderException("Cancel failed", e);
428
}
429
// ignore failure for decryption
430
}
431
}
432
433
private void ensureInitialized() throws PKCS11Exception {
434
if (initialized && aadBuffer.size() > 0) {
435
// need to cancel first to avoid CKR_OPERATION_ACTIVE
436
reset(true);
437
}
438
if (!initialized) {
439
initialize();
440
}
441
}
442
443
private void initialize() throws PKCS11Exception {
444
if (p11Key == null) {
445
throw new ProviderException(
446
"Operation cannot be performed without"
447
+ " calling engineInit first");
448
}
449
if (requireReinit) {
450
throw new IllegalStateException
451
("Must use either different key or iv");
452
}
453
454
token.ensureValid();
455
456
byte[] aad = (aadBuffer.size() > 0? aadBuffer.toByteArray() : null);
457
458
long p11KeyID = p11Key.getKeyID();
459
try {
460
CK_MECHANISM mechWithParams;
461
switch (type) {
462
case AES_GCM:
463
mechWithParams = new CK_MECHANISM(mechanism,
464
new CK_GCM_PARAMS(tagLen << 3, iv, aad));
465
break;
466
case CHACHA20_POLY1305:
467
mechWithParams = new CK_MECHANISM(mechanism,
468
new CK_SALSA20_CHACHA20_POLY1305_PARAMS(iv, aad));
469
break;
470
default:
471
throw new AssertionError("Unsupported type: " + type);
472
}
473
if (session == null) {
474
session = token.getOpSession();
475
}
476
if (encrypt) {
477
token.p11.C_EncryptInit(session.id(), mechWithParams,
478
p11KeyID);
479
} else {
480
token.p11.C_DecryptInit(session.id(), mechWithParams,
481
p11KeyID);
482
}
483
} catch (PKCS11Exception e) {
484
p11Key.releaseKeyID();
485
session = token.releaseSession(session);
486
throw e;
487
} finally {
488
dataBuffer.reset();
489
aadBuffer.reset();
490
}
491
initialized = true;
492
}
493
494
// if doFinal(inLen) is called, how big does the output buffer have to be?
495
private int doFinalLength(int inLen) {
496
if (inLen < 0) {
497
throw new ProviderException("Invalid negative input length");
498
}
499
500
int result = inLen + dataBuffer.size();
501
if (encrypt) {
502
result += tagLen;
503
} else {
504
// In earlier NSS versions, AES_GCM would report
505
// CKR_BUFFER_TOO_SMALL error if minus tagLen
506
if (type == Transformation.CHACHA20_POLY1305) {
507
result -= tagLen;
508
}
509
}
510
return (result > 0? result : 0);
511
}
512
513
// reset the states to the pre-initialized values
514
private void reset(boolean doCancel) {
515
if (!initialized) {
516
return;
517
}
518
initialized = false;
519
520
try {
521
if (session == null) {
522
return;
523
}
524
525
if (doCancel && token.explicitCancel) {
526
cancelOperation();
527
}
528
} finally {
529
p11Key.releaseKeyID();
530
session = token.releaseSession(session);
531
dataBuffer.reset();
532
}
533
}
534
535
// see JCE spec
536
protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
537
updateCalled = true;
538
int n = implUpdate(in, inOfs, inLen);
539
return new byte[0];
540
}
541
542
// see JCE spec
543
protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
544
int outOfs) throws ShortBufferException {
545
updateCalled = true;
546
implUpdate(in, inOfs, inLen);
547
return 0;
548
}
549
550
// see JCE spec
551
@Override
552
protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
553
throws ShortBufferException {
554
updateCalled = true;
555
implUpdate(inBuffer);
556
return 0;
557
}
558
559
// see JCE spec
560
@Override
561
protected synchronized void engineUpdateAAD(byte[] src, int srcOfs, int srcLen)
562
throws IllegalStateException {
563
if ((src == null) || (srcOfs < 0) || (srcOfs + srcLen > src.length)) {
564
throw new IllegalArgumentException("Invalid AAD");
565
}
566
if (requireReinit) {
567
throw new IllegalStateException
568
("Must use either different key or iv for encryption");
569
}
570
if (p11Key == null) {
571
throw new IllegalStateException("Need to initialize Cipher first");
572
}
573
if (updateCalled) {
574
throw new IllegalStateException
575
("Update has been called; no more AAD data");
576
}
577
aadBuffer.write(src, srcOfs, srcLen);
578
}
579
580
// see JCE spec
581
@Override
582
protected void engineUpdateAAD(ByteBuffer src)
583
throws IllegalStateException {
584
if (src == null) {
585
throw new IllegalArgumentException("Invalid AAD");
586
}
587
byte[] srcBytes = new byte[src.remaining()];
588
src.get(srcBytes);
589
engineUpdateAAD(srcBytes, 0, srcBytes.length);
590
}
591
592
// see JCE spec
593
protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
594
throws IllegalBlockSizeException, BadPaddingException {
595
int minOutLen = doFinalLength(inLen);
596
try {
597
byte[] out = new byte[minOutLen];
598
int n = engineDoFinal(in, inOfs, inLen, out, 0);
599
return P11Util.convert(out, 0, n);
600
} catch (ShortBufferException e) {
601
// convert since the output length is calculated by doFinalLength()
602
throw new ProviderException(e);
603
} finally {
604
updateCalled = false;
605
}
606
}
607
// see JCE spec
608
protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
609
int outOfs) throws ShortBufferException, IllegalBlockSizeException,
610
BadPaddingException {
611
try {
612
return implDoFinal(in, inOfs, inLen, out, outOfs, out.length - outOfs);
613
} finally {
614
updateCalled = false;
615
}
616
}
617
618
// see JCE spec
619
@Override
620
protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
621
throws ShortBufferException, IllegalBlockSizeException,
622
BadPaddingException {
623
try {
624
return implDoFinal(inBuffer, outBuffer);
625
} finally {
626
updateCalled = false;
627
}
628
}
629
630
private int implUpdate(byte[] in, int inOfs, int inLen) {
631
if (inLen > 0) {
632
updateCalled = true;
633
try {
634
ensureInitialized();
635
} catch (PKCS11Exception e) {
636
//e.printStackTrace();
637
reset(false);
638
throw new ProviderException("update() failed", e);
639
}
640
dataBuffer.write(in, inOfs, inLen);
641
}
642
// always 0 as NSS only supports single-part encryption/decryption
643
return 0;
644
}
645
646
private int implUpdate(ByteBuffer inBuf) {
647
int inLen = inBuf.remaining();
648
if (inLen > 0) {
649
try {
650
ensureInitialized();
651
} catch (PKCS11Exception e) {
652
reset(false);
653
throw new ProviderException("update() failed", e);
654
}
655
byte[] data = new byte[inLen];
656
inBuf.get(data);
657
dataBuffer.write(data, 0, data.length);
658
}
659
// always 0 as NSS only supports single-part encryption/decryption
660
return 0;
661
}
662
663
private int implDoFinal(byte[] in, int inOfs, int inLen,
664
byte[] out, int outOfs, int outLen)
665
throws ShortBufferException, IllegalBlockSizeException,
666
BadPaddingException {
667
int requiredOutLen = doFinalLength(inLen);
668
if (outLen < requiredOutLen) {
669
throw new ShortBufferException();
670
}
671
672
boolean doCancel = true;
673
try {
674
ensureInitialized();
675
if (dataBuffer.size() > 0) {
676
if (in != null && inOfs > 0 && inLen > 0 &&
677
inOfs < (in.length - inLen)) {
678
dataBuffer.write(in, inOfs, inLen);
679
}
680
in = dataBuffer.toByteArray();
681
inOfs = 0;
682
inLen = in.length;
683
}
684
int k = 0;
685
if (encrypt) {
686
k = token.p11.C_Encrypt(session.id(), 0, in, inOfs, inLen,
687
0, out, outOfs, outLen);
688
doCancel = false;
689
} else {
690
// Special handling to match SunJCE provider behavior
691
if (inLen == 0) {
692
return 0;
693
}
694
k = token.p11.C_Decrypt(session.id(), 0, in, inOfs, inLen,
695
0, out, outOfs, outLen);
696
doCancel = false;
697
}
698
return k;
699
} catch (PKCS11Exception e) {
700
// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only
701
// keep the operation active on CKR_BUFFER_TOO_SMALL errors or
702
// successful calls to determine the output length. However,
703
// these cases are not expected here because the output length
704
// is checked in the OpenJDK side before making the PKCS#11 call.
705
// Thus, doCancel can safely be 'false'.
706
doCancel = false;
707
handleException(e);
708
throw new ProviderException("doFinal() failed", e);
709
} finally {
710
if (encrypt) {
711
lastEncKey = this.p11Key;
712
lastEncIv = this.iv;
713
requireReinit = true;
714
}
715
reset(doCancel);
716
}
717
}
718
719
private int implDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
720
throws ShortBufferException, IllegalBlockSizeException,
721
BadPaddingException {
722
int outLen = outBuffer.remaining();
723
int inLen = inBuffer.remaining();
724
725
int requiredOutLen = doFinalLength(inLen);
726
if (outLen < requiredOutLen) {
727
throw new ShortBufferException();
728
}
729
730
boolean doCancel = true;
731
try {
732
ensureInitialized();
733
734
long inAddr = 0;
735
byte[] in = null;
736
int inOfs = 0;
737
if (dataBuffer.size() > 0) {
738
if (inLen > 0) {
739
byte[] temp = new byte[inLen];
740
inBuffer.get(temp);
741
dataBuffer.write(temp, 0, temp.length);
742
}
743
in = dataBuffer.toByteArray();
744
inOfs = 0;
745
inLen = in.length;
746
} else {
747
if (inBuffer instanceof DirectBuffer) {
748
inAddr = ((DirectBuffer) inBuffer).address();
749
inOfs = inBuffer.position();
750
} else {
751
if (inBuffer.hasArray()) {
752
in = inBuffer.array();
753
inOfs = inBuffer.position() + inBuffer.arrayOffset();
754
} else {
755
in = new byte[inLen];
756
inBuffer.get(in);
757
}
758
}
759
}
760
long outAddr = 0;
761
byte[] outArray = null;
762
int outOfs = 0;
763
if (outBuffer instanceof DirectBuffer) {
764
outAddr = ((DirectBuffer) outBuffer).address();
765
outOfs = outBuffer.position();
766
} else {
767
if (outBuffer.hasArray()) {
768
outArray = outBuffer.array();
769
outOfs = outBuffer.position() + outBuffer.arrayOffset();
770
} else {
771
outArray = new byte[outLen];
772
}
773
}
774
775
int k = 0;
776
if (encrypt) {
777
k = token.p11.C_Encrypt(session.id(), inAddr, in, inOfs, inLen,
778
outAddr, outArray, outOfs, outLen);
779
doCancel = false;
780
} else {
781
// Special handling to match SunJCE provider behavior
782
if (inLen == 0) {
783
return 0;
784
}
785
k = token.p11.C_Decrypt(session.id(), inAddr, in, inOfs, inLen,
786
outAddr, outArray, outOfs, outLen);
787
doCancel = false;
788
}
789
inBuffer.position(inBuffer.limit());
790
outBuffer.position(outBuffer.position() + k);
791
return k;
792
} catch (PKCS11Exception e) {
793
// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only
794
// keep the operation active on CKR_BUFFER_TOO_SMALL errors or
795
// successful calls to determine the output length. However,
796
// these cases are not expected here because the output length
797
// is checked in the OpenJDK side before making the PKCS#11 call.
798
// Thus, doCancel can safely be 'false'.
799
doCancel = false;
800
handleException(e);
801
throw new ProviderException("doFinal() failed", e);
802
} finally {
803
if (encrypt) {
804
lastEncKey = this.p11Key;
805
lastEncIv = this.iv;
806
requireReinit = true;
807
}
808
reset(doCancel);
809
}
810
}
811
812
private void handleException(PKCS11Exception e)
813
throws ShortBufferException, IllegalBlockSizeException,
814
BadPaddingException {
815
long errorCode = e.getErrorCode();
816
if (errorCode == CKR_BUFFER_TOO_SMALL) {
817
throw (ShortBufferException)
818
(new ShortBufferException().initCause(e));
819
} else if (errorCode == CKR_DATA_LEN_RANGE ||
820
errorCode == CKR_ENCRYPTED_DATA_LEN_RANGE) {
821
throw (IllegalBlockSizeException)
822
(new IllegalBlockSizeException(e.toString()).initCause(e));
823
} else if (errorCode == CKR_ENCRYPTED_DATA_INVALID ||
824
// Solaris-specific
825
errorCode == CKR_GENERAL_ERROR) {
826
throw (AEADBadTagException)
827
(new AEADBadTagException(e.toString()).initCause(e));
828
}
829
}
830
831
// see JCE spec
832
protected byte[] engineWrap(Key key) throws IllegalBlockSizeException,
833
InvalidKeyException {
834
// XXX key wrapping
835
throw new UnsupportedOperationException("engineWrap()");
836
}
837
838
// see JCE spec
839
protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
840
int wrappedKeyType)
841
throws InvalidKeyException, NoSuchAlgorithmException {
842
// XXX key unwrapping
843
throw new UnsupportedOperationException("engineUnwrap()");
844
}
845
846
// see JCE spec
847
@Override
848
protected int engineGetKeySize(Key key) throws InvalidKeyException {
849
int n = P11SecretKeyFactory.convertKey
850
(token, key, type.keyAlgo).length();
851
return n;
852
}
853
}
854
855
856