blob: 4fca6f880a81f4d7b040bec7ecf09e35e2f3c1f0 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.pkcs11;
27
28import java.nio.ByteBuffer;
29
30import java.security.*;
31import java.security.spec.*;
32
33import javax.crypto.*;
34import javax.crypto.spec.*;
35
36import sun.nio.ch.DirectBuffer;
37
38import sun.security.pkcs11.wrapper.*;
39import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
40
41/**
42 * Cipher implementation class. This class currently supports
43 * DES, DESede, AES, ARCFOUR, and Blowfish.
44 *
45 * This class is designed to support ECB and CBC with NoPadding and
46 * PKCS5Padding for both. However, currently only CBC/NoPadding (and
47 * ECB/NoPadding for stream ciphers) is functional.
48 *
49 * Note that PKCS#11 current only supports ECB and CBC. There are no
50 * provisions for other modes such as CFB, OFB, PCBC, or CTR mode.
51 * However, CTR could be implemented relatively easily (and efficiently)
52 * on top of ECB mode in this class, if need be.
53 *
54 * @author Andreas Sterbenz
55 * @since 1.5
56 */
57final class P11Cipher extends CipherSpi {
58
59 // mode constant for ECB mode
60 private final static int MODE_ECB = 3;
61 // mode constant for CBC mode
62 private final static int MODE_CBC = 4;
63
64 // padding constant for NoPadding
65 private final static int PAD_NONE = 5;
66 // padding constant for PKCS5Padding
67 private final static int PAD_PKCS5 = 6;
68
69 // token instance
70 private final Token token;
71
72 // algorithm name
73 private final String algorithm;
74
75 // name of the key algorithm, e.g. DES instead of algorithm DES/CBC/...
76 private final String keyAlgorithm;
77
78 // mechanism id
79 private final long mechanism;
80
81 // associated session, if any
82 private Session session;
83
84 // key, if init() was called
85 private P11Key p11Key;
86
87 // flag indicating whether an operation is initialized
88 private boolean initialized;
89
90 // falg indicating encrypt or decrypt mode
91 private boolean encrypt;
92
93 // mode, one of MODE_* above (MODE_ECB for stream ciphers)
94 private int blockMode;
95
96 // block size, 0 for stream ciphers
97 private final int blockSize;
98
99 // padding type, on of PAD_* above (PAD_NONE for stream ciphers)
100 private int paddingType;
101
102 // original IV, if in MODE_CBC
103 private byte[] iv;
104
105 // total number of bytes processed
106 private int bytesProcessed;
107
108 P11Cipher(Token token, String algorithm, long mechanism)
109 throws PKCS11Exception {
110 super();
111 this.token = token;
112 this.algorithm = algorithm;
113 this.mechanism = mechanism;
114 keyAlgorithm = algorithm.split("/")[0];
115 if (keyAlgorithm.equals("AES")) {
116 blockSize = 16;
117 blockMode = MODE_CBC;
118 // XXX change default to PKCS5Padding
119 paddingType = PAD_NONE;
120 } else if (keyAlgorithm.equals("RC4") || keyAlgorithm.equals("ARCFOUR")) {
121 blockSize = 0;
122 blockMode = MODE_ECB;
123 paddingType = PAD_NONE;
124 } else { // DES, DESede, Blowfish
125 blockSize = 8;
126 blockMode = MODE_CBC;
127 // XXX change default to PKCS5Padding
128 paddingType = PAD_NONE;
129 }
130 session = token.getOpSession();
131 }
132
133 protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
134 mode = mode.toUpperCase();
135 if (mode.equals("ECB")) {
136 this.blockMode = MODE_ECB;
137 } else if (mode.equals("CBC")) {
138 if (blockSize == 0) {
139 throw new NoSuchAlgorithmException
140 ("CBC mode not supported with stream ciphers");
141 }
142 this.blockMode = MODE_CBC;
143 } else {
144 throw new NoSuchAlgorithmException("Unsupported mode " + mode);
145 }
146 }
147
148 // see JCE spec
149 protected void engineSetPadding(String padding)
150 throws NoSuchPaddingException {
151 if (padding.equalsIgnoreCase("NoPadding")) {
152 paddingType = PAD_NONE;
153 } else if (padding.equalsIgnoreCase("PKCS5Padding")) {
154 if (blockSize == 0) {
155 throw new NoSuchPaddingException
156 ("PKCS#5 padding not supported with stream ciphers");
157 }
158 paddingType = PAD_PKCS5;
159 // XXX PKCS#5 not yet implemented
160 throw new NoSuchPaddingException("pkcs5");
161 } else {
162 throw new NoSuchPaddingException("Unsupported padding " + padding);
163 }
164 }
165
166 // see JCE spec
167 protected int engineGetBlockSize() {
168 return blockSize;
169 }
170
171 // see JCE spec
172 protected int engineGetOutputSize(int inputLen) {
173 return doFinalLength(inputLen);
174 }
175
176 // see JCE spec
177 protected byte[] engineGetIV() {
178 return (iv == null) ? null : (byte[])iv.clone();
179 }
180
181 // see JCE spec
182 protected AlgorithmParameters engineGetParameters() {
183 if (iv == null) {
184 return null;
185 }
186 IvParameterSpec ivSpec = new IvParameterSpec(iv);
187 try {
188 AlgorithmParameters params = AlgorithmParameters.getInstance
189 (keyAlgorithm, P11Util.getSunJceProvider());
190 params.init(ivSpec);
191 return params;
192 } catch (GeneralSecurityException e) {
193 // NoSuchAlgorithmException, NoSuchProviderException
194 // InvalidParameterSpecException
195 throw new ProviderException("Could not encode parameters", e);
196 }
197 }
198
199 // see JCE spec
200 protected void engineInit(int opmode, Key key, SecureRandom random)
201 throws InvalidKeyException {
202 try {
203 implInit(opmode, key, null, random);
204 } catch (InvalidAlgorithmParameterException e) {
205 throw new InvalidKeyException("init() failed", e);
206 }
207 }
208
209 // see JCE spec
210 protected void engineInit(int opmode, Key key,
211 AlgorithmParameterSpec params, SecureRandom random)
212 throws InvalidKeyException, InvalidAlgorithmParameterException {
213 byte[] iv;
214 if (params != null) {
215 if (params instanceof IvParameterSpec == false) {
216 throw new InvalidAlgorithmParameterException
217 ("Only IvParameterSpec supported");
218 }
219 IvParameterSpec ivSpec = (IvParameterSpec)params;
220 iv = ivSpec.getIV();
221 } else {
222 iv = null;
223 }
224 implInit(opmode, key, iv, random);
225 }
226
227 // see JCE spec
228 protected void engineInit(int opmode, Key key, AlgorithmParameters params,
229 SecureRandom random)
230 throws InvalidKeyException, InvalidAlgorithmParameterException {
231 byte[] iv;
232 if (params != null) {
233 try {
234 IvParameterSpec ivSpec = (IvParameterSpec)
235 params.getParameterSpec(IvParameterSpec.class);
236 iv = ivSpec.getIV();
237 } catch (InvalidParameterSpecException e) {
238 throw new InvalidAlgorithmParameterException
239 ("Could not decode IV", e);
240 }
241 } else {
242 iv = null;
243 }
244 implInit(opmode, key, iv, random);
245 }
246
247 // actual init() implementation
248 private void implInit(int opmode, Key key, byte[] iv,
249 SecureRandom random)
250 throws InvalidKeyException, InvalidAlgorithmParameterException {
251 cancelOperation();
252 switch (opmode) {
253 case Cipher.ENCRYPT_MODE:
254 encrypt = true;
255 break;
256 case Cipher.DECRYPT_MODE:
257 encrypt = false;
258 break;
259 default:
260 throw new InvalidAlgorithmParameterException
261 ("Unsupported mode: " + opmode);
262 }
263 if (blockMode == MODE_ECB) { // ECB or stream cipher
264 if (iv != null) {
265 if (blockSize == 0) {
266 throw new InvalidAlgorithmParameterException
267 ("IV not used with stream ciphers");
268 } else {
269 throw new InvalidAlgorithmParameterException
270 ("IV not used in ECB mode");
271 }
272 }
273 } else { // MODE_CBC
274 if (iv == null) {
275 if (encrypt == false) {
276 throw new InvalidAlgorithmParameterException
277 ("IV must be specified for decryption in CBC mode");
278 }
279 // generate random IV
280 if (random == null) {
281 random = new SecureRandom();
282 }
283 iv = new byte[blockSize];
284 random.nextBytes(iv);
285 } else {
286 if (iv.length != blockSize) {
287 throw new InvalidAlgorithmParameterException
288 ("IV length must match block size");
289 }
290 }
291 }
292 this.iv = iv;
293 p11Key = P11SecretKeyFactory.convertKey(token, key, keyAlgorithm);
294 try {
295 initialize();
296 } catch (PKCS11Exception e) {
297 throw new InvalidKeyException("Could not initialize cipher", e);
298 }
299 }
300
301 private void cancelOperation() {
302 if (initialized == false) {
303 return;
304 }
305 initialized = false;
306 if ((session == null) || (token.explicitCancel == false)) {
307 return;
308 }
309 // cancel operation by finishing it
310 int bufLen = doFinalLength(0);
311 byte[] buffer = new byte[bufLen];
312 try {
313 if (encrypt) {
314 token.p11.C_EncryptFinal(session.id(), 0, buffer, 0, bufLen);
315 } else {
316 token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen);
317 }
318 } catch (PKCS11Exception e) {
319 throw new ProviderException("Cancel failed", e);
320 }
321 }
322
323 private void ensureInitialized() throws PKCS11Exception {
324 if (initialized == false) {
325 initialize();
326 }
327 }
328
329 private void initialize() throws PKCS11Exception {
330 if (session == null) {
331 session = token.getOpSession();
332 }
333 if (encrypt) {
334 token.p11.C_EncryptInit
335 (session.id(), new CK_MECHANISM(mechanism, iv), p11Key.keyID);
336 } else {
337 token.p11.C_DecryptInit
338 (session.id(), new CK_MECHANISM(mechanism, iv), p11Key.keyID);
339 }
340 bytesProcessed = 0;
341 initialized = true;
342 }
343
344 // XXX the calculations below assume the PKCS#11 implementation is smart.
345 // conceivably, not all implementations are and we may need to estimate
346 // more conservatively
347
348 private int bytesBuffered(int totalLen) {
349 if (paddingType == PAD_NONE) {
350 // with NoPadding, buffer only the current unfinished block
351 return totalLen & (blockSize - 1);
352 } else { // PKCS5
353 // with PKCS5Padding in decrypt mode, the buffer must never
354 // be empty. Buffer a full block instead of nothing.
355 int buffered = totalLen & (blockSize - 1);
356 if ((buffered == 0) && (encrypt == false)) {
357 buffered = blockSize;
358 }
359 return buffered;
360 }
361 }
362
363 // if update(inLen) is called, how big does the output buffer have to be?
364 private int updateLength(int inLen) {
365 if (inLen <= 0) {
366 return 0;
367 }
368 if (blockSize == 0) {
369 return inLen;
370 } else {
371 // bytes that need to be buffered now
372 int buffered = bytesBuffered(bytesProcessed);
373 // bytes that need to be buffered after this update
374 int newBuffered = bytesBuffered(bytesProcessed + inLen);
375 return inLen + buffered - newBuffered;
376 }
377 }
378
379 // if doFinal(inLen) is called, how big does the output buffer have to be?
380 private int doFinalLength(int inLen) {
381 if (paddingType == PAD_NONE) {
382 return updateLength(inLen);
383 }
384 if (inLen < 0) {
385 return 0;
386 }
387 int buffered = bytesBuffered(bytesProcessed);
388 int newProcessed = bytesProcessed + inLen;
389 int paddedProcessed = (newProcessed + blockSize) & ~(blockSize - 1);
390 return paddedProcessed - bytesProcessed + buffered;
391 }
392
393 // see JCE spec
394 protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
395 try {
396 byte[] out = new byte[updateLength(inLen)];
397 int n = engineUpdate(in, inOfs, inLen, out, 0);
398 return P11Util.convert(out, 0, n);
399 } catch (ShortBufferException e) {
400 throw new ProviderException(e);
401 }
402 }
403
404 // see JCE spec
405 protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
406 int outOfs) throws ShortBufferException {
407 int outLen = out.length - outOfs;
408 return implUpdate(in, inOfs, inLen, out, outOfs, outLen);
409 }
410
411 // see JCE spec
412 protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
413 throws ShortBufferException {
414 return implUpdate(inBuffer, outBuffer);
415 }
416
417 // see JCE spec
418 protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
419 throws IllegalBlockSizeException, BadPaddingException {
420 try {
421 byte[] out = new byte[doFinalLength(inLen)];
422 int n = engineDoFinal(in, inOfs, inLen, out, 0);
423 return P11Util.convert(out, 0, n);
424 } catch (ShortBufferException e) {
425 throw new ProviderException(e);
426 }
427 }
428
429 // see JCE spec
430 protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
431 int outOfs) throws ShortBufferException, IllegalBlockSizeException {
432 // BadPaddingException {
433 int n = 0;
434 if ((inLen != 0) && (in != null)) {
435 n = engineUpdate(in, inOfs, inLen, out, outOfs);
436 outOfs += n;
437 }
438 n += implDoFinal(out, outOfs, out.length - outOfs);
439 return n;
440 }
441
442 // see JCE spec
443 protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
444 throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
445 int n = engineUpdate(inBuffer, outBuffer);
446 n += implDoFinal(outBuffer);
447 return n;
448 }
449
450 private int implUpdate(byte[] in, int inOfs, int inLen,
451 byte[] out, int outOfs, int outLen) throws ShortBufferException {
452 if (outLen < updateLength(inLen)) {
453 throw new ShortBufferException();
454 }
455 try {
456 ensureInitialized();
457 int k;
458 if (encrypt) {
459 k = token.p11.C_EncryptUpdate
460 (session.id(), 0, in, inOfs, inLen, 0, out, outOfs, outLen);
461 } else {
462 k = token.p11.C_DecryptUpdate
463 (session.id(), 0, in, inOfs, inLen, 0, out, outOfs, outLen);
464 }
465 bytesProcessed += inLen;
466 return k;
467 } catch (PKCS11Exception e) {
468 // XXX throw correct exception
469 throw new ProviderException("update() failed", e);
470 }
471 }
472
473 private int implUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
474 throws ShortBufferException {
475 int inLen = inBuffer.remaining();
476 if (inLen <= 0) {
477 return 0;
478 }
479
480 int outLen = outBuffer.remaining();
481 if (outLen < updateLength(inLen)) {
482 throw new ShortBufferException();
483 }
484 boolean inPosChanged = false;
485 try {
486 ensureInitialized();
487
488 long inAddr = 0;
489 int inOfs = inBuffer.position();
490 byte[] inArray = null;
491 if (inBuffer instanceof DirectBuffer) {
492 inAddr = ((DirectBuffer)inBuffer).address();
493 } else {
494 if (inBuffer.hasArray()) {
495 inArray = inBuffer.array();
496 inOfs += inBuffer.arrayOffset();
497 } else {
498 inArray = new byte[inLen];
499 inBuffer.get(inArray);
500 inOfs = 0;
501 inPosChanged = true;
502 }
503 }
504
505 long outAddr = 0;
506 int outOfs = outBuffer.position();
507 byte[] outArray = null;
508 if (outBuffer instanceof DirectBuffer) {
509 outAddr = ((DirectBuffer)outBuffer).address();
510 } else {
511 if (outBuffer.hasArray()) {
512 outArray = outBuffer.array();
513 outOfs += outBuffer.arrayOffset();
514 } else {
515 outArray = new byte[outLen];
516 outOfs = 0;
517 }
518 }
519
520 int k;
521 if (encrypt) {
522 k = token.p11.C_EncryptUpdate
523 (session.id(), inAddr, inArray, inOfs, inLen,
524 outAddr, outArray, outOfs, outLen);
525 } else {
526 k = token.p11.C_DecryptUpdate
527 (session.id(), inAddr, inArray, inOfs, inLen,
528 outAddr, outArray, outOfs, outLen);
529 }
530 bytesProcessed += inLen;
531 if (!inPosChanged) {
532 inBuffer.position(inBuffer.position() + inLen);
533 }
534 if (!(outBuffer instanceof DirectBuffer) &&
535 !outBuffer.hasArray()) {
536 outBuffer.put(outArray, outOfs, k);
537 } else {
538 outBuffer.position(outBuffer.position() + k);
539 }
540 return k;
541 } catch (PKCS11Exception e) {
542 // Un-read the bytes back to input buffer
543 if (inPosChanged) {
544 inBuffer.position(inBuffer.position() - inLen);
545 }
546 // XXX throw correct exception
547 throw new ProviderException("update() failed", e);
548 }
549 }
550
551 private int implDoFinal(byte[] out, int outOfs, int outLen)
552 throws ShortBufferException, IllegalBlockSizeException {
553 if (outLen < doFinalLength(0)) {
554 throw new ShortBufferException();
555 }
556 try {
557 ensureInitialized();
558 if (encrypt) {
559 return token.p11.C_EncryptFinal
560 (session.id(), 0, out, outOfs, outLen);
561 } else {
562 return token.p11.C_DecryptFinal
563 (session.id(), 0, out, outOfs, outLen);
564 }
565 } catch (PKCS11Exception e) {
566 handleException(e);
567 throw new ProviderException("doFinal() failed", e);
568 } finally {
569 initialized = false;
570 bytesProcessed = 0;
571 session = token.releaseSession(session);
572 }
573 }
574
575 private int implDoFinal(ByteBuffer outBuffer)
576 throws ShortBufferException, IllegalBlockSizeException {
577 int outLen = outBuffer.remaining();
578 if (outLen < doFinalLength(0)) {
579 throw new ShortBufferException();
580 }
581
582 try {
583 ensureInitialized();
584
585 long outAddr = 0;
586 int outOfs = outBuffer.position();
587 byte[] outArray = null;
588 if (outBuffer instanceof DirectBuffer) {
589 outAddr = ((DirectBuffer)outBuffer).address();
590 } else {
591 if (outBuffer.hasArray()) {
592 outArray = outBuffer.array();
593 outOfs += outBuffer.arrayOffset();
594 } else {
595 outArray = new byte[outLen];
596 outOfs = 0;
597 }
598 }
599
600 int k;
601 if (encrypt) {
602 k = token.p11.C_EncryptFinal
603 (session.id(), outAddr, outArray, outOfs, outLen);
604 } else {
605 k = token.p11.C_DecryptFinal
606 (session.id(), outAddr, outArray, outOfs, outLen);
607 }
608 if (!(outBuffer instanceof DirectBuffer) &&
609 !outBuffer.hasArray()) {
610 outBuffer.put(outArray, outOfs, k);
611 } else {
612 outBuffer.position(outBuffer.position() + k);
613 }
614 return k;
615 } catch (PKCS11Exception e) {
616 handleException(e);
617 throw new ProviderException("doFinal() failed", e);
618 } finally {
619 initialized = false;
620 bytesProcessed = 0;
621 session = token.releaseSession(session);
622 }
623 }
624
625 private void handleException(PKCS11Exception e)
626 throws IllegalBlockSizeException {
627 long errorCode = e.getErrorCode();
628 // XXX better check
629 if (errorCode == CKR_DATA_LEN_RANGE) {
630 throw (IllegalBlockSizeException)new
631 IllegalBlockSizeException(e.toString()).initCause(e);
632 }
633
634 }
635
636 // see JCE spec
637 protected byte[] engineWrap(Key key) throws IllegalBlockSizeException,
638 InvalidKeyException {
639 // XXX key wrapping
640 throw new UnsupportedOperationException("engineWrap()");
641 }
642
643 // see JCE spec
644 protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
645 int wrappedKeyType)
646 throws InvalidKeyException, NoSuchAlgorithmException {
647 // XXX key unwrapping
648 throw new UnsupportedOperationException("engineUnwrap()");
649 }
650
651 // see JCE spec
652 protected int engineGetKeySize(Key key) throws InvalidKeyException {
653 int n = P11SecretKeyFactory.convertKey
654 (token, key, keyAlgorithm).keyLength();
655 return n;
656 }
657
658 protected void finalize() throws Throwable {
659 try {
660 if ((session != null) && token.isValid()) {
661 cancelOperation();
662 session = token.releaseSession(session);
663 }
664 } finally {
665 super.finalize();
666 }
667 }
668
669}