--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.crypto.pkcs11/share/classes/sun/security/pkcs11/P11Cipher.java Sun Aug 17 15:54:13 2014 +0100
@@ -0,0 +1,902 @@
+/*
+ * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.security.pkcs11;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Locale;
+
+import java.security.*;
+import java.security.spec.*;
+
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+import sun.nio.ch.DirectBuffer;
+import sun.security.pkcs11.wrapper.*;
+import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
+
+/**
+ * Cipher implementation class. This class currently supports
+ * DES, DESede, AES, ARCFOUR, and Blowfish.
+ *
+ * This class is designed to support ECB, CBC, CTR with NoPadding
+ * and ECB, CBC with PKCS5Padding. It will use its own padding impl
+ * if the native mechanism does not support padding.
+ *
+ * Note that PKCS#11 currently only supports ECB, CBC, and CTR.
+ * There are no provisions for other modes such as CFB, OFB, and PCBC.
+ *
+ * @author Andreas Sterbenz
+ * @since 1.5
+ */
+final class P11Cipher extends CipherSpi {
+
+ // mode constant for ECB mode
+ private final static int MODE_ECB = 3;
+ // mode constant for CBC mode
+ private final static int MODE_CBC = 4;
+ // mode constant for CTR mode
+ private final static int MODE_CTR = 5;
+
+ // padding constant for NoPadding
+ private final static int PAD_NONE = 5;
+ // padding constant for PKCS5Padding
+ private final static int PAD_PKCS5 = 6;
+
+ private static interface Padding {
+ // ENC: format the specified buffer with padding bytes and return the
+ // actual padding length
+ int setPaddingBytes(byte[] paddingBuffer, int padLen);
+
+ // DEC: return the length of trailing padding bytes given the specified
+ // padded data
+ int unpad(byte[] paddedData, int len)
+ throws BadPaddingException, IllegalBlockSizeException;
+ }
+
+ private static class PKCS5Padding implements Padding {
+
+ private final int blockSize;
+
+ PKCS5Padding(int blockSize)
+ throws NoSuchPaddingException {
+ if (blockSize == 0) {
+ throw new NoSuchPaddingException
+ ("PKCS#5 padding not supported with stream ciphers");
+ }
+ this.blockSize = blockSize;
+ }
+
+ public int setPaddingBytes(byte[] paddingBuffer, int padLen) {
+ Arrays.fill(paddingBuffer, 0, padLen, (byte) (padLen & 0x007f));
+ return padLen;
+ }
+
+ public int unpad(byte[] paddedData, int len)
+ throws BadPaddingException, IllegalBlockSizeException {
+ if ((len < 1) || (len % blockSize != 0)) {
+ throw new IllegalBlockSizeException
+ ("Input length must be multiples of " + blockSize);
+ }
+ byte padValue = paddedData[len - 1];
+ if (padValue < 1 || padValue > blockSize) {
+ throw new BadPaddingException("Invalid pad value!");
+ }
+ // sanity check padding bytes
+ int padStartIndex = len - padValue;
+ for (int i = padStartIndex; i < len; i++) {
+ if (paddedData[i] != padValue) {
+ throw new BadPaddingException("Invalid pad bytes!");
+ }
+ }
+ return padValue;
+ }
+ }
+
+ // token instance
+ private final Token token;
+
+ // algorithm name
+ private final String algorithm;
+
+ // name of the key algorithm, e.g. DES instead of algorithm DES/CBC/...
+ private final String keyAlgorithm;
+
+ // mechanism id
+ private final long mechanism;
+
+ // associated session, if any
+ private Session session;
+
+ // key, if init() was called
+ private P11Key p11Key;
+
+ // flag indicating whether an operation is initialized
+ private boolean initialized;
+
+ // falg indicating encrypt or decrypt mode
+ private boolean encrypt;
+
+ // mode, one of MODE_* above (MODE_ECB for stream ciphers)
+ private int blockMode;
+
+ // block size, 0 for stream ciphers
+ private final int blockSize;
+
+ // padding type, on of PAD_* above (PAD_NONE for stream ciphers)
+ private int paddingType;
+
+ // when the padding is requested but unsupported by the native mechanism,
+ // we use the following to do padding and necessary data buffering.
+ // padding object which generate padding and unpad the decrypted data
+ private Padding paddingObj;
+ // buffer for holding back the block which contains padding bytes
+ private byte[] padBuffer;
+ private int padBufferLen;
+
+ // original IV, if in MODE_CBC or MODE_CTR
+ private byte[] iv;
+
+ // number of bytes buffered internally by the native mechanism and padBuffer
+ // if we do the padding
+ private int bytesBuffered;
+
+ // length of key size in bytes; currently only used by AES given its oid
+ // specification mandates a fixed size of the key
+ private int fixedKeySize = -1;
+
+ P11Cipher(Token token, String algorithm, long mechanism)
+ throws PKCS11Exception, NoSuchAlgorithmException {
+ super();
+ this.token = token;
+ this.algorithm = algorithm;
+ this.mechanism = mechanism;
+
+ String algoParts[] = algorithm.split("/");
+
+ if (algoParts[0].startsWith("AES")) {
+ blockSize = 16;
+ int index = algoParts[0].indexOf('_');
+ if (index != -1) {
+ // should be well-formed since we specify what we support
+ fixedKeySize = Integer.parseInt(algoParts[0].substring(index+1))/8;
+ }
+ keyAlgorithm = "AES";
+ } else {
+ keyAlgorithm = algoParts[0];
+ if (keyAlgorithm.equals("RC4") ||
+ keyAlgorithm.equals("ARCFOUR")) {
+ blockSize = 0;
+ } else { // DES, DESede, Blowfish
+ blockSize = 8;
+ }
+ }
+ this.blockMode =
+ (algoParts.length > 1 ? parseMode(algoParts[1]) : MODE_ECB);
+ String defPadding = (blockSize == 0 ? "NoPadding" : "PKCS5Padding");
+ String paddingStr =
+ (algoParts.length > 2 ? algoParts[2] : defPadding);
+ try {
+ engineSetPadding(paddingStr);
+ } catch (NoSuchPaddingException nspe) {
+ // should not happen
+ throw new ProviderException(nspe);
+ }
+ }
+
+ protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
+ // Disallow change of mode for now since currently it's explicitly
+ // defined in transformation strings
+ throw new NoSuchAlgorithmException("Unsupported mode " + mode);
+ }
+
+ private int parseMode(String mode) throws NoSuchAlgorithmException {
+ mode = mode.toUpperCase(Locale.ENGLISH);
+ int result;
+ if (mode.equals("ECB")) {
+ result = MODE_ECB;
+ } else if (mode.equals("CBC")) {
+ if (blockSize == 0) {
+ throw new NoSuchAlgorithmException
+ ("CBC mode not supported with stream ciphers");
+ }
+ result = MODE_CBC;
+ } else if (mode.equals("CTR")) {
+ result = MODE_CTR;
+ } else {
+ throw new NoSuchAlgorithmException("Unsupported mode " + mode);
+ }
+ return result;
+ }
+
+ // see JCE spec
+ protected void engineSetPadding(String padding)
+ throws NoSuchPaddingException {
+ paddingObj = null;
+ padBuffer = null;
+ padding = padding.toUpperCase(Locale.ENGLISH);
+ if (padding.equals("NOPADDING")) {
+ paddingType = PAD_NONE;
+ } else if (padding.equals("PKCS5PADDING")) {
+ if (this.blockMode == MODE_CTR) {
+ throw new NoSuchPaddingException
+ ("PKCS#5 padding not supported with CTR mode");
+ }
+ paddingType = PAD_PKCS5;
+ if (mechanism != CKM_DES_CBC_PAD && mechanism != CKM_DES3_CBC_PAD &&
+ mechanism != CKM_AES_CBC_PAD) {
+ // no native padding support; use our own padding impl
+ paddingObj = new PKCS5Padding(blockSize);
+ padBuffer = new byte[blockSize];
+ }
+ } else {
+ throw new NoSuchPaddingException("Unsupported padding " + padding);
+ }
+ }
+
+ // see JCE spec
+ protected int engineGetBlockSize() {
+ return blockSize;
+ }
+
+ // see JCE spec
+ protected int engineGetOutputSize(int inputLen) {
+ return doFinalLength(inputLen);
+ }
+
+ // see JCE spec
+ protected byte[] engineGetIV() {
+ return (iv == null) ? null : iv.clone();
+ }
+
+ // see JCE spec
+ protected AlgorithmParameters engineGetParameters() {
+ if (iv == null) {
+ return null;
+ }
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ try {
+ AlgorithmParameters params =
+ AlgorithmParameters.getInstance(keyAlgorithm,
+ P11Util.getSunJceProvider());
+ params.init(ivSpec);
+ return params;
+ } catch (GeneralSecurityException e) {
+ // NoSuchAlgorithmException, NoSuchProviderException
+ // InvalidParameterSpecException
+ throw new ProviderException("Could not encode parameters", e);
+ }
+ }
+
+ // see JCE spec
+ protected void engineInit(int opmode, Key key, SecureRandom random)
+ throws InvalidKeyException {
+ try {
+ implInit(opmode, key, null, random);
+ } catch (InvalidAlgorithmParameterException e) {
+ throw new InvalidKeyException("init() failed", e);
+ }
+ }
+
+ // see JCE spec
+ protected void engineInit(int opmode, Key key,
+ AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ byte[] ivValue;
+ if (params != null) {
+ if (params instanceof IvParameterSpec == false) {
+ throw new InvalidAlgorithmParameterException
+ ("Only IvParameterSpec supported");
+ }
+ IvParameterSpec ivSpec = (IvParameterSpec) params;
+ ivValue = ivSpec.getIV();
+ } else {
+ ivValue = null;
+ }
+ implInit(opmode, key, ivValue, random);
+ }
+
+ // see JCE spec
+ protected void engineInit(int opmode, Key key, AlgorithmParameters params,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ byte[] ivValue;
+ if (params != null) {
+ try {
+ IvParameterSpec ivSpec =
+ params.getParameterSpec(IvParameterSpec.class);
+ ivValue = ivSpec.getIV();
+ } catch (InvalidParameterSpecException e) {
+ throw new InvalidAlgorithmParameterException
+ ("Could not decode IV", e);
+ }
+ } else {
+ ivValue = null;
+ }
+ implInit(opmode, key, ivValue, random);
+ }
+
+ // actual init() implementation
+ private void implInit(int opmode, Key key, byte[] iv,
+ SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ cancelOperation();
+ if (fixedKeySize != -1 && key.getEncoded().length != fixedKeySize) {
+ throw new InvalidKeyException("Key size is invalid");
+ }
+ switch (opmode) {
+ case Cipher.ENCRYPT_MODE:
+ encrypt = true;
+ break;
+ case Cipher.DECRYPT_MODE:
+ encrypt = false;
+ break;
+ default:
+ throw new InvalidAlgorithmParameterException
+ ("Unsupported mode: " + opmode);
+ }
+ if (blockMode == MODE_ECB) { // ECB or stream cipher
+ if (iv != null) {
+ if (blockSize == 0) {
+ throw new InvalidAlgorithmParameterException
+ ("IV not used with stream ciphers");
+ } else {
+ throw new InvalidAlgorithmParameterException
+ ("IV not used in ECB mode");
+ }
+ }
+ } else { // MODE_CBC or MODE_CTR
+ if (iv == null) {
+ if (encrypt == false) {
+ String exMsg =
+ (blockMode == MODE_CBC ?
+ "IV must be specified for decryption in CBC mode" :
+ "IV must be specified for decryption in CTR mode");
+ throw new InvalidAlgorithmParameterException(exMsg);
+ }
+ // generate random IV
+ if (random == null) {
+ random = new SecureRandom();
+ }
+ iv = new byte[blockSize];
+ random.nextBytes(iv);
+ } else {
+ if (iv.length != blockSize) {
+ throw new InvalidAlgorithmParameterException
+ ("IV length must match block size");
+ }
+ }
+ }
+ this.iv = iv;
+ p11Key = P11SecretKeyFactory.convertKey(token, key, keyAlgorithm);
+ try {
+ initialize();
+ } catch (PKCS11Exception e) {
+ throw new InvalidKeyException("Could not initialize cipher", e);
+ }
+ }
+
+ private void cancelOperation() {
+ if (initialized == false) {
+ return;
+ }
+ initialized = false;
+ if ((session == null) || (token.explicitCancel == false)) {
+ return;
+ }
+ // cancel operation by finishing it
+ int bufLen = doFinalLength(0);
+ byte[] buffer = new byte[bufLen];
+ try {
+ if (encrypt) {
+ token.p11.C_EncryptFinal(session.id(), 0, buffer, 0, bufLen);
+ } else {
+ token.p11.C_DecryptFinal(session.id(), 0, buffer, 0, bufLen);
+ }
+ } catch (PKCS11Exception e) {
+ throw new ProviderException("Cancel failed", e);
+ } finally {
+ reset();
+ }
+ }
+
+ private void ensureInitialized() throws PKCS11Exception {
+ if (initialized == false) {
+ initialize();
+ }
+ }
+
+ private void initialize() throws PKCS11Exception {
+ if (session == null) {
+ session = token.getOpSession();
+ }
+ CK_MECHANISM mechParams = (blockMode == MODE_CTR?
+ new CK_MECHANISM(mechanism, new CK_AES_CTR_PARAMS(iv)) :
+ new CK_MECHANISM(mechanism, iv));
+
+ try {
+ if (encrypt) {
+ token.p11.C_EncryptInit(session.id(), mechParams, p11Key.keyID);
+ } else {
+ token.p11.C_DecryptInit(session.id(), mechParams, p11Key.keyID);
+ }
+ } catch (PKCS11Exception ex) {
+ // release session when initialization failed
+ session = token.releaseSession(session);
+ throw ex;
+ }
+ bytesBuffered = 0;
+ padBufferLen = 0;
+ initialized = true;
+ }
+
+ // if update(inLen) is called, how big does the output buffer have to be?
+ private int updateLength(int inLen) {
+ if (inLen <= 0) {
+ return 0;
+ }
+
+ int result = inLen + bytesBuffered;
+ if (blockSize != 0 && blockMode != MODE_CTR) {
+ // minus the number of bytes in the last incomplete block.
+ result -= (result & (blockSize - 1));
+ }
+ return result;
+ }
+
+ // if doFinal(inLen) is called, how big does the output buffer have to be?
+ private int doFinalLength(int inLen) {
+ if (inLen < 0) {
+ return 0;
+ }
+
+ int result = inLen + bytesBuffered;
+ if (blockSize != 0 && encrypt && paddingType != PAD_NONE) {
+ // add the number of bytes to make the last block complete.
+ result += (blockSize - (result & (blockSize - 1)));
+ }
+ return result;
+ }
+
+ // reset the states to the pre-initialized values
+ private void reset() {
+ initialized = false;
+ bytesBuffered = 0;
+ padBufferLen = 0;
+ if (session != null) {
+ session = token.releaseSession(session);
+ }
+ }
+
+ // see JCE spec
+ protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
+ try {
+ byte[] out = new byte[updateLength(inLen)];
+ int n = engineUpdate(in, inOfs, inLen, out, 0);
+ return P11Util.convert(out, 0, n);
+ } catch (ShortBufferException e) {
+ // convert since the output length is calculated by updateLength()
+ throw new ProviderException(e);
+ }
+ }
+
+ // see JCE spec
+ protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
+ int outOfs) throws ShortBufferException {
+ int outLen = out.length - outOfs;
+ return implUpdate(in, inOfs, inLen, out, outOfs, outLen);
+ }
+
+ // see JCE spec
+ @Override
+ protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
+ throws ShortBufferException {
+ return implUpdate(inBuffer, outBuffer);
+ }
+
+ // see JCE spec
+ protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
+ throws IllegalBlockSizeException, BadPaddingException {
+ try {
+ byte[] out = new byte[doFinalLength(inLen)];
+ int n = engineDoFinal(in, inOfs, inLen, out, 0);
+ return P11Util.convert(out, 0, n);
+ } catch (ShortBufferException e) {
+ // convert since the output length is calculated by doFinalLength()
+ throw new ProviderException(e);
+ }
+ }
+
+ // see JCE spec
+ protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
+ int outOfs) throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
+ int n = 0;
+ if ((inLen != 0) && (in != null)) {
+ n = engineUpdate(in, inOfs, inLen, out, outOfs);
+ outOfs += n;
+ }
+ n += implDoFinal(out, outOfs, out.length - outOfs);
+ return n;
+ }
+
+ // see JCE spec
+ @Override
+ protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
+ throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
+ int n = engineUpdate(inBuffer, outBuffer);
+ n += implDoFinal(outBuffer);
+ return n;
+ }
+
+ private int implUpdate(byte[] in, int inOfs, int inLen,
+ byte[] out, int outOfs, int outLen) throws ShortBufferException {
+ if (outLen < updateLength(inLen)) {
+ throw new ShortBufferException();
+ }
+ try {
+ ensureInitialized();
+ int k = 0;
+ if (encrypt) {
+ k = token.p11.C_EncryptUpdate(session.id(), 0, in, inOfs, inLen,
+ 0, out, outOfs, outLen);
+ } else {
+ int newPadBufferLen = 0;
+ if (paddingObj != null) {
+ if (padBufferLen != 0) {
+ // NSS throws up when called with data not in multiple
+ // of blocks. Try to work around this by holding the
+ // extra data in padBuffer.
+ if (padBufferLen != padBuffer.length) {
+ int bufCapacity = padBuffer.length - padBufferLen;
+ if (inLen > bufCapacity) {
+ bufferInputBytes(in, inOfs, bufCapacity);
+ inOfs += bufCapacity;
+ inLen -= bufCapacity;
+ } else {
+ bufferInputBytes(in, inOfs, inLen);
+ return 0;
+ }
+ }
+ k = token.p11.C_DecryptUpdate(session.id(),
+ 0, padBuffer, 0, padBufferLen,
+ 0, out, outOfs, outLen);
+ padBufferLen = 0;
+ }
+ newPadBufferLen = inLen & (blockSize - 1);
+ if (newPadBufferLen == 0) {
+ newPadBufferLen = padBuffer.length;
+ }
+ inLen -= newPadBufferLen;
+ }
+ if (inLen > 0) {
+ k += token.p11.C_DecryptUpdate(session.id(), 0, in, inOfs,
+ inLen, 0, out, (outOfs + k), (outLen - k));
+ }
+ // update 'padBuffer' if using our own padding impl.
+ if (paddingObj != null) {
+ bufferInputBytes(in, inOfs + inLen, newPadBufferLen);
+ }
+ }
+ bytesBuffered += (inLen - k);
+ return k;
+ } catch (PKCS11Exception e) {
+ if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
+ throw (ShortBufferException)
+ (new ShortBufferException().initCause(e));
+ }
+ reset();
+ throw new ProviderException("update() failed", e);
+ }
+ }
+
+ private int implUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
+ throws ShortBufferException {
+ int inLen = inBuffer.remaining();
+ if (inLen <= 0) {
+ return 0;
+ }
+
+ int outLen = outBuffer.remaining();
+ if (outLen < updateLength(inLen)) {
+ throw new ShortBufferException();
+ }
+ int origPos = inBuffer.position();
+ try {
+ ensureInitialized();
+
+ long inAddr = 0;
+ int inOfs = 0;
+ byte[] inArray = null;
+
+ if (inBuffer instanceof DirectBuffer) {
+ inAddr = ((DirectBuffer) inBuffer).address();
+ inOfs = origPos;
+ } else if (inBuffer.hasArray()) {
+ inArray = inBuffer.array();
+ inOfs = (origPos + inBuffer.arrayOffset());
+ }
+
+ long outAddr = 0;
+ int outOfs = 0;
+ byte[] outArray = null;
+ if (outBuffer instanceof DirectBuffer) {
+ outAddr = ((DirectBuffer) outBuffer).address();
+ outOfs = outBuffer.position();
+ } else {
+ if (outBuffer.hasArray()) {
+ outArray = outBuffer.array();
+ outOfs = (outBuffer.position() + outBuffer.arrayOffset());
+ } else {
+ outArray = new byte[outLen];
+ }
+ }
+
+ int k = 0;
+ if (encrypt) {
+ if (inAddr == 0 && inArray == null) {
+ inArray = new byte[inLen];
+ inBuffer.get(inArray);
+ } else {
+ inBuffer.position(origPos + inLen);
+ }
+ k = token.p11.C_EncryptUpdate(session.id(),
+ inAddr, inArray, inOfs, inLen,
+ outAddr, outArray, outOfs, outLen);
+ } else {
+ int newPadBufferLen = 0;
+ if (paddingObj != null) {
+ if (padBufferLen != 0) {
+ // NSS throws up when called with data not in multiple
+ // of blocks. Try to work around this by holding the
+ // extra data in padBuffer.
+ if (padBufferLen != padBuffer.length) {
+ int bufCapacity = padBuffer.length - padBufferLen;
+ if (inLen > bufCapacity) {
+ bufferInputBytes(inBuffer, bufCapacity);
+ inOfs += bufCapacity;
+ inLen -= bufCapacity;
+ } else {
+ bufferInputBytes(inBuffer, inLen);
+ return 0;
+ }
+ }
+ k = token.p11.C_DecryptUpdate(session.id(), 0,
+ padBuffer, 0, padBufferLen, outAddr, outArray,
+ outOfs, outLen);
+ padBufferLen = 0;
+ }
+ newPadBufferLen = inLen & (blockSize - 1);
+ if (newPadBufferLen == 0) {
+ newPadBufferLen = padBuffer.length;
+ }
+ inLen -= newPadBufferLen;
+ }
+ if (inLen > 0) {
+ if (inAddr == 0 && inArray == null) {
+ inArray = new byte[inLen];
+ inBuffer.get(inArray);
+ } else {
+ inBuffer.position(inBuffer.position() + inLen);
+ }
+ k += token.p11.C_DecryptUpdate(session.id(), inAddr,
+ inArray, inOfs, inLen, outAddr, outArray,
+ (outOfs + k), (outLen - k));
+ }
+ // update 'padBuffer' if using our own padding impl.
+ if (paddingObj != null && newPadBufferLen != 0) {
+ bufferInputBytes(inBuffer, newPadBufferLen);
+ }
+ }
+ bytesBuffered += (inLen - k);
+ if (!(outBuffer instanceof DirectBuffer) &&
+ !outBuffer.hasArray()) {
+ outBuffer.put(outArray, outOfs, k);
+ } else {
+ outBuffer.position(outBuffer.position() + k);
+ }
+ return k;
+ } catch (PKCS11Exception e) {
+ // Reset input buffer to its original position for
+ inBuffer.position(origPos);
+ if (e.getErrorCode() == CKR_BUFFER_TOO_SMALL) {
+ throw (ShortBufferException)
+ (new ShortBufferException().initCause(e));
+ }
+ reset();
+ throw new ProviderException("update() failed", e);
+ }
+ }
+
+ private int implDoFinal(byte[] out, int outOfs, int outLen)
+ throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
+ int requiredOutLen = doFinalLength(0);
+ if (outLen < requiredOutLen) {
+ throw new ShortBufferException();
+ }
+ try {
+ ensureInitialized();
+ int k = 0;
+ if (encrypt) {
+ if (paddingObj != null) {
+ int actualPadLen = paddingObj.setPaddingBytes(padBuffer,
+ requiredOutLen - bytesBuffered);
+ k = token.p11.C_EncryptUpdate(session.id(),
+ 0, padBuffer, 0, actualPadLen,
+ 0, out, outOfs, outLen);
+ }
+ k += token.p11.C_EncryptFinal(session.id(),
+ 0, out, (outOfs + k), (outLen - k));
+ } else {
+ if (paddingObj != null) {
+ if (padBufferLen != 0) {
+ k = token.p11.C_DecryptUpdate(session.id(), 0,
+ padBuffer, 0, padBufferLen, 0, padBuffer, 0,
+ padBuffer.length);
+ }
+ k += token.p11.C_DecryptFinal(session.id(), 0, padBuffer, k,
+ padBuffer.length - k);
+ int actualPadLen = paddingObj.unpad(padBuffer, k);
+ k -= actualPadLen;
+ System.arraycopy(padBuffer, 0, out, outOfs, k);
+ } else {
+ k = token.p11.C_DecryptFinal(session.id(), 0, out, outOfs,
+ outLen);
+ }
+ }
+ return k;
+ } catch (PKCS11Exception e) {
+ handleException(e);
+ throw new ProviderException("doFinal() failed", e);
+ } finally {
+ reset();
+ }
+ }
+
+ private int implDoFinal(ByteBuffer outBuffer)
+ throws ShortBufferException, IllegalBlockSizeException,
+ BadPaddingException {
+ int outLen = outBuffer.remaining();
+ int requiredOutLen = doFinalLength(0);
+ if (outLen < requiredOutLen) {
+ throw new ShortBufferException();
+ }
+
+ try {
+ ensureInitialized();
+
+ long outAddr = 0;
+ byte[] outArray = null;
+ int outOfs = 0;
+ if (outBuffer instanceof DirectBuffer) {
+ outAddr = ((DirectBuffer) outBuffer).address();
+ outOfs = outBuffer.position();
+ } else {
+ if (outBuffer.hasArray()) {
+ outArray = outBuffer.array();
+ outOfs = outBuffer.position() + outBuffer.arrayOffset();
+ } else {
+ outArray = new byte[outLen];
+ }
+ }
+
+ int k = 0;
+
+ if (encrypt) {
+ if (paddingObj != null) {
+ int actualPadLen = paddingObj.setPaddingBytes(padBuffer,
+ requiredOutLen - bytesBuffered);
+ k = token.p11.C_EncryptUpdate(session.id(),
+ 0, padBuffer, 0, actualPadLen,
+ outAddr, outArray, outOfs, outLen);
+ }
+ k += token.p11.C_EncryptFinal(session.id(),
+ outAddr, outArray, (outOfs + k), (outLen - k));
+ } else {
+ if (paddingObj != null) {
+ if (padBufferLen != 0) {
+ k = token.p11.C_DecryptUpdate(session.id(),
+ 0, padBuffer, 0, padBufferLen,
+ 0, padBuffer, 0, padBuffer.length);
+ padBufferLen = 0;
+ }
+ k += token.p11.C_DecryptFinal(session.id(),
+ 0, padBuffer, k, padBuffer.length - k);
+ int actualPadLen = paddingObj.unpad(padBuffer, k);
+ k -= actualPadLen;
+ outArray = padBuffer;
+ outOfs = 0;
+ } else {
+ k = token.p11.C_DecryptFinal(session.id(),
+ outAddr, outArray, outOfs, outLen);
+ }
+ }
+ if ((!encrypt && paddingObj != null) ||
+ (!(outBuffer instanceof DirectBuffer) &&
+ !outBuffer.hasArray())) {
+ outBuffer.put(outArray, outOfs, k);
+ } else {
+ outBuffer.position(outBuffer.position() + k);
+ }
+ return k;
+ } catch (PKCS11Exception e) {
+ handleException(e);
+ throw new ProviderException("doFinal() failed", e);
+ } finally {
+ reset();
+ }
+ }
+
+ private void handleException(PKCS11Exception e)
+ throws ShortBufferException, IllegalBlockSizeException {
+ long errorCode = e.getErrorCode();
+ if (errorCode == CKR_BUFFER_TOO_SMALL) {
+ throw (ShortBufferException)
+ (new ShortBufferException().initCause(e));
+ } else if (errorCode == CKR_DATA_LEN_RANGE ||
+ errorCode == CKR_ENCRYPTED_DATA_LEN_RANGE) {
+ throw (IllegalBlockSizeException)
+ (new IllegalBlockSizeException(e.toString()).initCause(e));
+ }
+ }
+
+ // see JCE spec
+ protected byte[] engineWrap(Key key) throws IllegalBlockSizeException,
+ InvalidKeyException {
+ // XXX key wrapping
+ throw new UnsupportedOperationException("engineWrap()");
+ }
+
+ // see JCE spec
+ protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,
+ int wrappedKeyType)
+ throws InvalidKeyException, NoSuchAlgorithmException {
+ // XXX key unwrapping
+ throw new UnsupportedOperationException("engineUnwrap()");
+ }
+
+ // see JCE spec
+ @Override
+ protected int engineGetKeySize(Key key) throws InvalidKeyException {
+ int n = P11SecretKeyFactory.convertKey
+ (token, key, keyAlgorithm).length();
+ return n;
+ }
+
+ private final void bufferInputBytes(byte[] in, int inOfs, int len) {
+ System.arraycopy(in, inOfs, padBuffer, padBufferLen, len);
+ padBufferLen += len;
+ bytesBuffered += len;
+ }
+
+ private final void bufferInputBytes(ByteBuffer inBuffer, int len) {
+ inBuffer.get(padBuffer, padBufferLen, len);
+ padBufferLen += len;
+ bytesBuffered += len;
+ }
+}