1 /*
   2  * Copyright (c) 2003, 2016, 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.nio.ByteBuffer;
  28 import java.util.Arrays;
  29 import java.util.Locale;
  30 
  31 import java.security.*;
  32 import java.security.spec.*;
  33 
  34 import javax.crypto.*;
  35 import javax.crypto.spec.*;
  36 
  37 import sun.nio.ch.DirectBuffer;
  38 import sun.security.jca.JCAUtil;
  39 import sun.security.pkcs11.wrapper.*;
  40 import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
  41 
  42 /**
  43  * Cipher implementation class. This class currently supports
  44  * DES, DESede, AES, ARCFOUR, and Blowfish.
  45  *
  46  * This class is designed to support ECB, CBC, CTR with NoPadding
  47  * and ECB, CBC with PKCS5Padding. It will use its own padding impl
  48  * if the native mechanism does not support padding.
  49  *
  50  * Note that PKCS#11 currently only supports ECB, CBC, and CTR.
  51  * There are no provisions for other modes such as CFB, OFB, and PCBC.
  52  *
  53  * @author  Andreas Sterbenz
  54  * @since   1.5
  55  */
  56 final class P11Cipher extends CipherSpi {
  57 
  58     // mode constant for ECB mode
  59     private final static int MODE_ECB = 3;
  60     // mode constant for CBC mode
  61     private final static int MODE_CBC = 4;
  62     // mode constant for CTR mode
  63     private final static int MODE_CTR = 5;
  64 
  65     // padding constant for NoPadding
  66     private final static int PAD_NONE = 5;
  67     // padding constant for PKCS5Padding
  68     private final static int PAD_PKCS5 = 6;
  69 
  70     private static interface Padding {
  71         // ENC: format the specified buffer with padding bytes and return the
  72         // actual padding length
  73         int setPaddingBytes(byte[] paddingBuffer, int padLen);
  74 
  75         // DEC: return the length of trailing padding bytes given the specified
  76         // padded data
  77         int unpad(byte[] paddedData, int len)
  78                 throws BadPaddingException, IllegalBlockSizeException;
  79     }
  80 
  81     private static class PKCS5Padding implements Padding {
  82 
  83         private final int blockSize;
  84 
  85         PKCS5Padding(int blockSize)
  86                 throws NoSuchPaddingException {
  87             if (blockSize == 0) {
  88                 throw new NoSuchPaddingException
  89                         ("PKCS#5 padding not supported with stream ciphers");
  90             }
  91             this.blockSize = blockSize;
  92         }
  93 
  94         public int setPaddingBytes(byte[] paddingBuffer, int padLen) {
  95             Arrays.fill(paddingBuffer, 0, padLen, (byte) (padLen & 0x007f));
  96             return padLen;
  97         }
  98 
  99         public int unpad(byte[] paddedData, int len)
 100                 throws BadPaddingException, IllegalBlockSizeException {
 101             if ((len < 1) || (len % blockSize != 0)) {
 102                 throw new IllegalBlockSizeException
 103                     ("Input length must be multiples of " + blockSize);
 104             }
 105             byte padValue = paddedData[len - 1];
 106             if (padValue < 1 || padValue > blockSize) {
 107                 throw new BadPaddingException("Invalid pad value!");
 108             }
 109             // sanity check padding bytes
 110             int padStartIndex = len - padValue;
 111             for (int i = padStartIndex; i < len; i++) {
 112                 if (paddedData[i] != padValue) {
 113                     throw new BadPaddingException("Invalid pad bytes!");
 114                 }
 115             }
 116             return padValue;
 117         }
 118     }
 119 
 120     // token instance
 121     private final Token token;
 122 
 123     // algorithm name
 124     private final String algorithm;
 125 
 126     // name of the key algorithm, e.g. DES instead of algorithm DES/CBC/...
 127     private final String keyAlgorithm;
 128 
 129     // mechanism id
 130     private final long mechanism;
 131 
 132     // associated session, if any
 133     private Session session;
 134 
 135     // key, if init() was called
 136     private P11Key p11Key;
 137 
 138     // flag indicating whether an operation is initialized
 139     private boolean initialized;
 140 
 141     // falg indicating encrypt or decrypt mode
 142     private boolean encrypt;
 143 
 144     // mode, one of MODE_* above (MODE_ECB for stream ciphers)
 145     private int blockMode;
 146 
 147     // block size, 0 for stream ciphers
 148     private final int blockSize;
 149 
 150     // padding type, on of PAD_* above (PAD_NONE for stream ciphers)
 151     private int paddingType;
 152 
 153     // when the padding is requested but unsupported by the native mechanism,
 154     // we use the following to do padding and necessary data buffering.
 155     // padding object which generate padding and unpad the decrypted data
 156     private Padding paddingObj;
 157     // buffer for holding back the block which contains padding bytes
 158     private byte[] padBuffer;
 159     private int padBufferLen;
 160 
 161     // original IV, if in MODE_CBC or MODE_CTR
 162     private byte[] iv;
 163 
 164     // number of bytes buffered internally by the native mechanism and padBuffer
 165     // if we do the padding
 166     private int bytesBuffered;
 167 
 168     // length of key size in bytes; currently only used by AES given its oid
 169     // specification mandates a fixed size of the key
 170     private int fixedKeySize = -1;
 171 
 172     P11Cipher(Token token, String algorithm, long mechanism)
 173             throws PKCS11Exception, NoSuchAlgorithmException {
 174         super();
 175         this.token = token;
 176         this.algorithm = algorithm;
 177         this.mechanism = mechanism;
 178 
 179         String[] algoParts = algorithm.split("/");
 180 
 181         if (algoParts[0].startsWith("AES")) {
 182             blockSize = 16;
 183             int index = algoParts[0].indexOf('_');
 184             if (index != -1) {
 185                 // should be well-formed since we specify what we support
 186                 fixedKeySize = Integer.parseInt(algoParts[0].substring(index+1))/8;
 187             }
 188             keyAlgorithm = "AES";
 189         } else {
 190             keyAlgorithm = algoParts[0];
 191             if (keyAlgorithm.equals("RC4") ||
 192                     keyAlgorithm.equals("ARCFOUR")) {
 193                 blockSize = 0;
 194             } else { // DES, DESede, Blowfish
 195                 blockSize = 8;
 196             }
 197         }
 198         this.blockMode =
 199             (algoParts.length > 1 ? parseMode(algoParts[1]) : MODE_ECB);
 200         String defPadding = (blockSize == 0 ? "NoPadding" : "PKCS5Padding");
 201         String paddingStr =
 202                 (algoParts.length > 2 ? algoParts[2] : defPadding);
 203         try {
 204             engineSetPadding(paddingStr);
 205         } catch (NoSuchPaddingException nspe) {
 206             // should not happen
 207             throw new ProviderException(nspe);
 208         }
 209     }
 210 
 211     protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
 212         // Disallow change of mode for now since currently it's explicitly
 213         // defined in transformation strings
 214         throw new NoSuchAlgorithmException("Unsupported mode " + mode);
 215     }
 216 
 217     private int parseMode(String mode) throws NoSuchAlgorithmException {
 218         mode = mode.toUpperCase(Locale.ENGLISH);
 219         int result;
 220         if (mode.equals("ECB")) {
 221             result = MODE_ECB;
 222         } else if (mode.equals("CBC")) {
 223             if (blockSize == 0) {
 224                 throw new NoSuchAlgorithmException
 225                         ("CBC mode not supported with stream ciphers");
 226             }
 227             result = MODE_CBC;
 228         } else if (mode.equals("CTR")) {
 229             result = MODE_CTR;
 230         } else {
 231             throw new NoSuchAlgorithmException("Unsupported mode " + mode);
 232         }
 233         return result;
 234     }
 235 
 236     // see JCE spec
 237     protected void engineSetPadding(String padding)
 238             throws NoSuchPaddingException {
 239         paddingObj = null;
 240         padBuffer = null;
 241         padding = padding.toUpperCase(Locale.ENGLISH);
 242         if (padding.equals("NOPADDING")) {
 243             paddingType = PAD_NONE;
 244         } else if (padding.equals("PKCS5PADDING")) {
 245             if (this.blockMode == MODE_CTR) {
 246                 throw new NoSuchPaddingException
 247                     ("PKCS#5 padding not supported with CTR mode");
 248             }
 249             paddingType = PAD_PKCS5;
 250             if (mechanism != CKM_DES_CBC_PAD && mechanism != CKM_DES3_CBC_PAD &&
 251                     mechanism != CKM_AES_CBC_PAD) {
 252                 // no native padding support; use our own padding impl
 253                 paddingObj = new PKCS5Padding(blockSize);
 254                 padBuffer = new byte[blockSize];
 255             }
 256         } else {
 257             throw new NoSuchPaddingException("Unsupported padding " + padding);
 258         }
 259     }
 260 
 261     // see JCE spec
 262     protected int engineGetBlockSize() {
 263         return blockSize;
 264     }
 265 
 266     // see JCE spec
 267     protected int engineGetOutputSize(int inputLen) {
 268         return doFinalLength(inputLen);
 269     }
 270 
 271     // see JCE spec
 272     protected byte[] engineGetIV() {
 273         return (iv == null) ? null : iv.clone();
 274     }
 275 
 276     // see JCE spec
 277     protected AlgorithmParameters engineGetParameters() {
 278         if (iv == null) {
 279             return null;
 280         }
 281         IvParameterSpec ivSpec = new IvParameterSpec(iv);
 282         try {
 283             AlgorithmParameters params =
 284                     AlgorithmParameters.getInstance(keyAlgorithm,
 285                     P11Util.getSunJceProvider());
 286             params.init(ivSpec);
 287             return params;
 288         } catch (GeneralSecurityException e) {
 289             // NoSuchAlgorithmException, NoSuchProviderException
 290             // InvalidParameterSpecException
 291             throw new ProviderException("Could not encode parameters", e);
 292         }
 293     }
 294 
 295     // see JCE spec
 296     protected void engineInit(int opmode, Key key, SecureRandom random)
 297             throws InvalidKeyException {
 298         try {
 299             implInit(opmode, key, null, random);
 300         } catch (InvalidAlgorithmParameterException e) {
 301             throw new InvalidKeyException("init() failed", e);
 302         }
 303     }
 304 
 305     // see JCE spec
 306     protected void engineInit(int opmode, Key key,
 307             AlgorithmParameterSpec params, SecureRandom random)
 308             throws InvalidKeyException, InvalidAlgorithmParameterException {
 309         byte[] ivValue;
 310         if (params != null) {
 311             if (params instanceof IvParameterSpec == false) {
 312                 throw new InvalidAlgorithmParameterException
 313                         ("Only IvParameterSpec supported");
 314             }
 315             IvParameterSpec ivSpec = (IvParameterSpec) params;
 316             ivValue = ivSpec.getIV();
 317         } else {
 318             ivValue = null;
 319         }
 320         implInit(opmode, key, ivValue, random);
 321     }
 322 
 323     // see JCE spec
 324     protected void engineInit(int opmode, Key key, AlgorithmParameters params,
 325             SecureRandom random)
 326             throws InvalidKeyException, InvalidAlgorithmParameterException {
 327         byte[] ivValue;
 328         if (params != null) {
 329             try {
 330                 IvParameterSpec ivSpec =
 331                         params.getParameterSpec(IvParameterSpec.class);
 332                 ivValue = ivSpec.getIV();
 333             } catch (InvalidParameterSpecException e) {
 334                 throw new InvalidAlgorithmParameterException
 335                         ("Could not decode IV", e);
 336             }
 337         } else {
 338             ivValue = null;
 339         }
 340         implInit(opmode, key, ivValue, random);
 341     }
 342 
 343     // actual init() implementation
 344     private void implInit(int opmode, Key key, byte[] iv,
 345             SecureRandom random)
 346             throws InvalidKeyException, InvalidAlgorithmParameterException {
 347         reset(true);
 348         if (fixedKeySize != -1 && key.getEncoded().length != fixedKeySize) {
 349             throw new InvalidKeyException("Key size is invalid");
 350         }
 351         switch (opmode) {
 352             case Cipher.ENCRYPT_MODE:
 353                 encrypt = true;
 354                 break;
 355             case Cipher.DECRYPT_MODE:
 356                 encrypt = false;
 357                 break;
 358             default:
 359                 throw new InvalidAlgorithmParameterException
 360                         ("Unsupported mode: " + opmode);
 361         }
 362         if (blockMode == MODE_ECB) { // ECB or stream cipher
 363             if (iv != null) {
 364                 if (blockSize == 0) {
 365                     throw new InvalidAlgorithmParameterException
 366                             ("IV not used with stream ciphers");
 367                 } else {
 368                     throw new InvalidAlgorithmParameterException
 369                             ("IV not used in ECB mode");
 370                 }
 371             }
 372         } else { // MODE_CBC or MODE_CTR
 373             if (iv == null) {
 374                 if (encrypt == false) {
 375                     String exMsg =
 376                         (blockMode == MODE_CBC ?
 377                          "IV must be specified for decryption in CBC mode" :
 378                          "IV must be specified for decryption in CTR mode");
 379                     throw new InvalidAlgorithmParameterException(exMsg);
 380                 }
 381                 // generate random IV
 382                 if (random == null) {
 383                     random = JCAUtil.getSecureRandom();
 384                 }
 385                 iv = new byte[blockSize];
 386                 random.nextBytes(iv);
 387             } else {
 388                 if (iv.length != blockSize) {
 389                     throw new InvalidAlgorithmParameterException
 390                             ("IV length must match block size");
 391                 }
 392             }
 393         }
 394         this.iv = iv;
 395         p11Key = P11SecretKeyFactory.convertKey(token, key, keyAlgorithm);
 396         try {
 397             initialize();
 398         } catch (PKCS11Exception e) {
 399             throw new InvalidKeyException("Could not initialize cipher", e);
 400         }
 401     }
 402 
 403     // reset the states to the pre-initialized values
 404     private void reset(boolean doCancel) {
 405         if (!initialized) {
 406             return;
 407         }
 408         initialized = false;
 409         try {
 410             if (session == null) {
 411                 return;
 412             }
 413             if (doCancel && token.explicitCancel) {
 414                 cancelOperation();
 415             }
 416         } finally {
 417             p11Key.decNativeKeyRef();
 418             session = token.releaseSession(session);
 419             bytesBuffered = 0;
 420             padBufferLen = 0;
 421         }
 422     }
 423 
 424     private void cancelOperation() {
 425         token.ensureValid();
 426         if (session.hasObjects() == false) {
 427             session = token.killSession(session);
 428             return;
 429         } else {
 430             try {
 431                 // cancel operation by finishing it
 432                 int bufLen = doFinalLength(0);
 433                 byte[] buffer = new byte[bufLen];
 434                 if (encrypt) {
 435                     token.p11.C_EncryptFinal(session.id(), 0, buffer, 0, bufLen);
 436                 } else {
 437                     token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen);
 438                 }
 439             } catch (PKCS11Exception e) {
 440                 throw new ProviderException("Cancel failed", e);
 441             }
 442         }
 443     }
 444 
 445     private void ensureInitialized() throws PKCS11Exception {
 446         if (initialized == false) {
 447             initialize();
 448         }
 449     }
 450 
 451     private void initialize() throws PKCS11Exception {
 452         if (p11Key == null) {
 453             initialized = false;
 454             throw new ProviderException(
 455                     "Operation cannot be performed without"
 456                     + " calling engineInit first");
 457         }
 458         token.ensureValid();
 459         p11Key.incNativeKeyRef();
 460         try {
 461             if (session == null) {
 462                 session = token.getOpSession();
 463             }
 464             CK_MECHANISM mechParams = (blockMode == MODE_CTR?
 465                     new CK_MECHANISM(mechanism, new CK_AES_CTR_PARAMS(iv)) :
 466                     new CK_MECHANISM(mechanism, iv));
 467             if (encrypt) {
 468                 token.p11.C_EncryptInit(session.id(), mechParams, p11Key.keyID);
 469             } else {
 470                 token.p11.C_DecryptInit(session.id(), mechParams, p11Key.keyID);
 471             }
 472         } catch (PKCS11Exception e) {
 473             p11Key.decNativeKeyRef();
 474             session = token.releaseSession(session);
 475             initialized = false;
 476             throw e;
 477         }
 478         initialized = true;
 479         bytesBuffered = 0;
 480         padBufferLen = 0;
 481     }
 482 
 483     // if update(inLen) is called, how big does the output buffer have to be?
 484     private int updateLength(int inLen) {
 485         if (inLen <= 0) {
 486             return 0;
 487         }
 488 
 489         int result = inLen + bytesBuffered;
 490         if (blockSize != 0 && blockMode != MODE_CTR) {
 491             // minus the number of bytes in the last incomplete block.
 492             result -= (result & (blockSize - 1));
 493         }
 494         return result;
 495     }
 496 
 497     // if doFinal(inLen) is called, how big does the output buffer have to be?
 498     private int doFinalLength(int inLen) {
 499         if (inLen < 0) {
 500             return 0;
 501         }
 502 
 503         int result = inLen + bytesBuffered;
 504         if (blockSize != 0 && encrypt && paddingType != PAD_NONE) {
 505             // add the number of bytes to make the last block complete.
 506             result += (blockSize - (result & (blockSize - 1)));
 507         }
 508         return result;
 509     }
 510 
 511     // see JCE spec
 512     protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
 513         try {
 514             byte[] out = new byte[updateLength(inLen)];
 515             int n = engineUpdate(in, inOfs, inLen, out, 0);
 516             return P11Util.convert(out, 0, n);
 517         } catch (ShortBufferException e) {
 518             // convert since the output length is calculated by updateLength()
 519             throw new ProviderException(e);
 520         }
 521     }
 522 
 523     // see JCE spec
 524     protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
 525             int outOfs) throws ShortBufferException {
 526         int outLen = out.length - outOfs;
 527         return implUpdate(in, inOfs, inLen, out, outOfs, outLen);
 528     }
 529 
 530     // see JCE spec
 531     @Override
 532     protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
 533             throws ShortBufferException {
 534         return implUpdate(inBuffer, outBuffer);
 535     }
 536 
 537     // see JCE spec
 538     protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
 539             throws IllegalBlockSizeException, BadPaddingException {
 540         try {
 541             byte[] out = new byte[doFinalLength(inLen)];
 542             int n = engineDoFinal(in, inOfs, inLen, out, 0);
 543             return P11Util.convert(out, 0, n);
 544         } catch (ShortBufferException e) {
 545             // convert since the output length is calculated by doFinalLength()
 546             throw new ProviderException(e);
 547         }
 548     }
 549 
 550     // see JCE spec
 551     protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
 552             int outOfs) throws ShortBufferException, IllegalBlockSizeException,
 553             BadPaddingException {
 554         int n = 0;
 555         if ((inLen != 0) && (in != null)) {
 556             n = engineUpdate(in, inOfs, inLen, out, outOfs);
 557             outOfs += n;
 558         }
 559         n += implDoFinal(out, outOfs, out.length - outOfs);
 560         return n;
 561     }
 562 
 563     // see JCE spec
 564     @Override
 565     protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
 566             throws ShortBufferException, IllegalBlockSizeException,
 567             BadPaddingException {
 568         int n = engineUpdate(inBuffer, outBuffer);
 569         n += implDoFinal(outBuffer);
 570         return n;
 571     }
 572 
 573     private int implUpdate(byte[] in, int inOfs, int inLen,
 574             byte[] out, int outOfs, int outLen) throws ShortBufferException {
 575         if (outLen < updateLength(inLen)) {
 576             throw new ShortBufferException();
 577         }
 578         try {
 579             ensureInitialized();
 580             int k = 0;
 581             if (encrypt) {
 582                 k = token.p11.C_EncryptUpdate(session.id(), 0, in, inOfs, inLen,
 583                         0, out, outOfs, outLen);
 584             } else {
 585                 int newPadBufferLen = 0;
 586                 if (paddingObj != null) {
 587                     if (padBufferLen != 0) {
 588                         // NSS throws up when called with data not in multiple
 589                         // of blocks. Try to work around this by holding the
 590                         // extra data in padBuffer.
 591                         if (padBufferLen != padBuffer.length) {
 592                             int bufCapacity = padBuffer.length - padBufferLen;
 593                             if (inLen > bufCapacity) {
 594                                 bufferInputBytes(in, inOfs, bufCapacity);
 595                                 inOfs += bufCapacity;
 596                                 inLen -= bufCapacity;
 597                             } else {
 598                                 bufferInputBytes(in, inOfs, inLen);
 599                                 return 0;
 600                             }
 601                         }
 602                         k = token.p11.C_DecryptUpdate(session.id(),
 603                                 0, padBuffer, 0, padBufferLen,
 604                                 0, out, outOfs, outLen);
 605                         padBufferLen = 0;
 606                     }
 607                     newPadBufferLen = inLen & (blockSize - 1);
 608                     if (newPadBufferLen == 0) {
 609                         newPadBufferLen = padBuffer.length;
 610                     }
 611                     inLen -= newPadBufferLen;
 612                 }
 613                 if (inLen > 0) {
 614                     k += token.p11.C_DecryptUpdate(session.id(), 0, in, inOfs,
 615                             inLen, 0, out, (outOfs + k), (outLen - k));
 616                 }
 617                 // update 'padBuffer' if using our own padding impl.
 618                 if (paddingObj != null) {
 619                     bufferInputBytes(in, inOfs + inLen, newPadBufferLen);
 620                 }
 621             }
 622             bytesBuffered += (inLen - k);
 623             return k;
 624         } catch (PKCS11Exception e) {
 625             if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
 626                 throw (ShortBufferException)
 627                         (new ShortBufferException().initCause(e));
 628             }
 629             reset(false);
 630             throw new ProviderException("update() failed", e);
 631         }
 632     }
 633 
 634     private int implUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
 635             throws ShortBufferException {
 636         int inLen = inBuffer.remaining();
 637         if (inLen <= 0) {
 638             return 0;
 639         }
 640 
 641         int outLen = outBuffer.remaining();
 642         if (outLen < updateLength(inLen)) {
 643             throw new ShortBufferException();
 644         }
 645         int origPos = inBuffer.position();
 646         try {
 647             ensureInitialized();
 648 
 649             long inAddr = 0;
 650             int inOfs = 0;
 651             byte[] inArray = null;
 652 
 653             if (inBuffer instanceof DirectBuffer) {
 654                 inAddr = ((DirectBuffer) inBuffer).address();
 655                 inOfs = origPos;
 656             } else if (inBuffer.hasArray()) {
 657                 inArray = inBuffer.array();
 658                 inOfs = (origPos + inBuffer.arrayOffset());
 659             }
 660 
 661             long outAddr = 0;
 662             int outOfs = 0;
 663             byte[] outArray = null;
 664             if (outBuffer instanceof DirectBuffer) {
 665                 outAddr = ((DirectBuffer) outBuffer).address();
 666                 outOfs = outBuffer.position();
 667             } else {
 668                 if (outBuffer.hasArray()) {
 669                     outArray = outBuffer.array();
 670                     outOfs = (outBuffer.position() + outBuffer.arrayOffset());
 671                 } else {
 672                     outArray = new byte[outLen];
 673                 }
 674             }
 675 
 676             int k = 0;
 677             if (encrypt) {
 678                 if (inAddr == 0 && inArray == null) {
 679                     inArray = new byte[inLen];
 680                     inBuffer.get(inArray);
 681                 } else {
 682                     inBuffer.position(origPos + inLen);
 683                 }
 684                 k = token.p11.C_EncryptUpdate(session.id(),
 685                         inAddr, inArray, inOfs, inLen,
 686                         outAddr, outArray, outOfs, outLen);
 687             } else {
 688                 int newPadBufferLen = 0;
 689                 if (paddingObj != null) {
 690                     if (padBufferLen != 0) {
 691                         // NSS throws up when called with data not in multiple
 692                         // of blocks. Try to work around this by holding the
 693                         // extra data in padBuffer.
 694                         if (padBufferLen != padBuffer.length) {
 695                             int bufCapacity = padBuffer.length - padBufferLen;
 696                             if (inLen > bufCapacity) {
 697                                 bufferInputBytes(inBuffer, bufCapacity);
 698                                 inOfs += bufCapacity;
 699                                 inLen -= bufCapacity;
 700                             } else {
 701                                 bufferInputBytes(inBuffer, inLen);
 702                                 return 0;
 703                             }
 704                         }
 705                         k = token.p11.C_DecryptUpdate(session.id(), 0,
 706                                 padBuffer, 0, padBufferLen, outAddr, outArray,
 707                                 outOfs, outLen);
 708                         padBufferLen = 0;
 709                     }
 710                     newPadBufferLen = inLen & (blockSize - 1);
 711                     if (newPadBufferLen == 0) {
 712                         newPadBufferLen = padBuffer.length;
 713                     }
 714                     inLen -= newPadBufferLen;
 715                 }
 716                 if (inLen > 0) {
 717                     if (inAddr == 0 && inArray == null) {
 718                         inArray = new byte[inLen];
 719                         inBuffer.get(inArray);
 720                     } else {
 721                         inBuffer.position(inBuffer.position() + inLen);
 722                     }
 723                     k += token.p11.C_DecryptUpdate(session.id(), inAddr,
 724                             inArray, inOfs, inLen, outAddr, outArray,
 725                             (outOfs + k), (outLen - k));
 726                 }
 727                 // update 'padBuffer' if using our own padding impl.
 728                 if (paddingObj != null && newPadBufferLen != 0) {
 729                     bufferInputBytes(inBuffer, newPadBufferLen);
 730                 }
 731             }
 732             bytesBuffered += (inLen - k);
 733             if (!(outBuffer instanceof DirectBuffer) &&
 734                     !outBuffer.hasArray()) {
 735                 outBuffer.put(outArray, outOfs, k);
 736             } else {
 737                 outBuffer.position(outBuffer.position() + k);
 738             }
 739             return k;
 740         } catch (PKCS11Exception e) {
 741             // Reset input buffer to its original position for
 742             inBuffer.position(origPos);
 743             if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
 744                 throw (ShortBufferException)
 745                         (new ShortBufferException().initCause(e));
 746             }
 747             reset(false);
 748             throw new ProviderException("update() failed", e);
 749         }
 750     }
 751 
 752     private int implDoFinal(byte[] out, int outOfs, int outLen)
 753             throws ShortBufferException, IllegalBlockSizeException,
 754             BadPaddingException {
 755         int requiredOutLen = doFinalLength(0);
 756         if (outLen < requiredOutLen) {
 757             throw new ShortBufferException();
 758         }
 759         boolean doCancel = true;
 760         try {
 761             ensureInitialized();
 762             int k = 0;
 763             if (encrypt) {
 764                 if (paddingObj != null) {
 765                     int actualPadLen = paddingObj.setPaddingBytes(padBuffer,
 766                             requiredOutLen - bytesBuffered);
 767                     k = token.p11.C_EncryptUpdate(session.id(),
 768                             0, padBuffer, 0, actualPadLen,
 769                             0, out, outOfs, outLen);
 770                 }
 771                 k += token.p11.C_EncryptFinal(session.id(),
 772                         0, out, (outOfs + k), (outLen - k));
 773                 doCancel = false;
 774             } else {
 775                 // Special handling to match SunJCE provider behavior
 776                 if (bytesBuffered == 0 && padBufferLen == 0) {
 777                     return 0;
 778                 }
 779                 if (paddingObj != null) {
 780                     if (padBufferLen != 0) {
 781                         k = token.p11.C_DecryptUpdate(session.id(), 0,
 782                                 padBuffer, 0, padBufferLen, 0, padBuffer, 0,
 783                                 padBuffer.length);
 784                     }
 785                     k += token.p11.C_DecryptFinal(session.id(), 0, padBuffer, k,
 786                             padBuffer.length - k);
 787                     doCancel = false;
 788 
 789                     int actualPadLen = paddingObj.unpad(padBuffer, k);
 790                     k -= actualPadLen;
 791                     System.arraycopy(padBuffer, 0, out, outOfs, k);
 792                 } else {
 793                     k = token.p11.C_DecryptFinal(session.id(), 0, out, outOfs,
 794                             outLen);
 795                     doCancel = false;
 796                 }
 797             }
 798             return k;
 799         } catch (PKCS11Exception e) {
 800             doCancel = false;
 801             handleException(e);
 802             throw new ProviderException("doFinal() failed", e);
 803         } finally {
 804             reset(doCancel);
 805         }
 806     }
 807 
 808     private int implDoFinal(ByteBuffer outBuffer)
 809             throws ShortBufferException, IllegalBlockSizeException,
 810             BadPaddingException {
 811         int outLen = outBuffer.remaining();
 812         int requiredOutLen = doFinalLength(0);
 813         if (outLen < requiredOutLen) {
 814             throw new ShortBufferException();
 815         }
 816 
 817         boolean doCancel = true;
 818         try {
 819             ensureInitialized();
 820 
 821             long outAddr = 0;
 822             byte[] outArray = null;
 823             int outOfs = 0;
 824             if (outBuffer instanceof DirectBuffer) {
 825                 outAddr = ((DirectBuffer) outBuffer).address();
 826                 outOfs = outBuffer.position();
 827             } else {
 828                 if (outBuffer.hasArray()) {
 829                     outArray = outBuffer.array();
 830                     outOfs = outBuffer.position() + outBuffer.arrayOffset();
 831                 } else {
 832                     outArray = new byte[outLen];
 833                 }
 834             }
 835 
 836             int k = 0;
 837 
 838             if (encrypt) {
 839                 if (paddingObj != null) {
 840                     int actualPadLen = paddingObj.setPaddingBytes(padBuffer,
 841                             requiredOutLen - bytesBuffered);
 842                     k = token.p11.C_EncryptUpdate(session.id(),
 843                             0, padBuffer, 0, actualPadLen,
 844                             outAddr, outArray, outOfs, outLen);
 845                 }
 846                 k += token.p11.C_EncryptFinal(session.id(),
 847                         outAddr, outArray, (outOfs + k), (outLen - k));
 848                 doCancel = false;
 849             } else {
 850                 // Special handling to match SunJCE provider behavior
 851                 if (bytesBuffered == 0 && padBufferLen == 0) {
 852                     return 0;
 853                 }
 854 
 855                 if (paddingObj != null) {
 856                     if (padBufferLen != 0) {
 857                         k = token.p11.C_DecryptUpdate(session.id(),
 858                                 0, padBuffer, 0, padBufferLen,
 859                                 0, padBuffer, 0, padBuffer.length);
 860                         padBufferLen = 0;
 861                     }
 862                     k += token.p11.C_DecryptFinal(session.id(),
 863                             0, padBuffer, k, padBuffer.length - k);
 864                     doCancel = false;
 865 
 866                     int actualPadLen = paddingObj.unpad(padBuffer, k);
 867                     k -= actualPadLen;
 868                     outArray = padBuffer;
 869                     outOfs = 0;
 870                 } else {
 871                     k = token.p11.C_DecryptFinal(session.id(),
 872                             outAddr, outArray, outOfs, outLen);
 873                     doCancel = false;
 874                 }
 875             }
 876             if ((!encrypt && paddingObj != null) ||
 877                     (!(outBuffer instanceof DirectBuffer) &&
 878                     !outBuffer.hasArray())) {
 879                 outBuffer.put(outArray, outOfs, k);
 880             } else {
 881                 outBuffer.position(outBuffer.position() + k);
 882             }
 883             return k;
 884         } catch (PKCS11Exception e) {
 885             doCancel = false;
 886             handleException(e);
 887             throw new ProviderException("doFinal() failed", e);
 888         } finally {
 889             reset(doCancel);
 890         }
 891     }
 892 
 893     private void handleException(PKCS11Exception e)
 894             throws ShortBufferException, IllegalBlockSizeException {
 895         long errorCode = e.getErrorCode();
 896         if (errorCode == CKR_BUFFER_TOO_SMALL) {
 897             throw (ShortBufferException)
 898                     (new ShortBufferException().initCause(e));
 899         } else if (errorCode == CKR_DATA_LEN_RANGE ||
 900                    errorCode == CKR_ENCRYPTED_DATA_LEN_RANGE) {
 901             throw (IllegalBlockSizeException)
 902                     (new IllegalBlockSizeException(e.toString()).initCause(e));
 903         }
 904     }
 905 
 906     // see JCE spec
 907     protected byte[] engineWrap(Key key) throws IllegalBlockSizeException,
 908             InvalidKeyException {
 909         // XXX key wrapping
 910         throw new UnsupportedOperationException("engineWrap()");
 911     }
 912 
 913     // see JCE spec
 914     protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
 915             int wrappedKeyType)
 916             throws InvalidKeyException, NoSuchAlgorithmException {
 917         // XXX key unwrapping
 918         throw new UnsupportedOperationException("engineUnwrap()");
 919     }
 920 
 921     // see JCE spec
 922     @Override
 923     protected int engineGetKeySize(Key key) throws InvalidKeyException {
 924         int n = P11SecretKeyFactory.convertKey
 925                 (token, key, keyAlgorithm).length();
 926         return n;
 927     }
 928 
 929     private final void bufferInputBytes(byte[] in, int inOfs, int len) {
 930         System.arraycopy(in, inOfs, padBuffer, padBufferLen, len);
 931         padBufferLen += len;
 932         bytesBuffered += len;
 933     }
 934 
 935     private final void bufferInputBytes(ByteBuffer inBuffer, int len) {
 936         inBuffer.get(padBuffer, padBufferLen, len);
 937         padBufferLen += len;
 938         bytesBuffered += len;
 939     }
 940 }