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