blob: b41e939e9062f4e55fad4f53557749570ef63e62 [file]
/*
* Copyright (c) 2019, 2025, 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.io.ByteArrayOutputStream;
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 jdk.internal.access.JavaNioAccess;
import jdk.internal.access.SharedSecrets;
import sun.nio.ch.DirectBuffer;
import sun.security.jca.JCAUtil;
import sun.security.pkcs11.wrapper.*;
import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
import static sun.security.pkcs11.wrapper.PKCS11Exception.RV.*;
/**
* P11 AEAD Cipher implementation class. This class currently supports
* AES cipher in GCM mode and CHACHA20-POLY1305 cipher.
*
* Note that AEAD modes do not use padding, so this class does not have
* its own padding impl. In addition, some vendors such as NSS may not support
* multi-part encryption/decryption for AEAD cipher algorithms, thus the
* current impl uses PKCS#11 C_Encrypt/C_Decrypt calls and buffers data until
* doFinal is called.
*
* @since 13
*/
final class P11AEADCipher extends CipherSpi {
private static final JavaNioAccess NIO_ACCESS = SharedSecrets.getJavaNioAccess();
// supported AEAD algorithms/transformations
private enum Transformation {
AES_GCM("AES", "GCM", "NOPADDING", 16, 16),
CHACHA20_POLY1305("CHACHA20", "NONE", "NOPADDING", 12, 16);
final String keyAlgo;
final String mode;
final String padding;
final int defIvLen; // in bytes
final int defTagLen; // in bytes
Transformation(String keyAlgo, String mode, String padding,
int defIvLen, int defTagLen) {
this.keyAlgo = keyAlgo;
this.mode = mode;
this.padding = padding;
this.defIvLen = defIvLen;
this.defTagLen = defTagLen;
}
}
// token instance
private final Token token;
// mechanism id
private final long mechanism;
// type of this AEAD cipher, one of Transformation enum above
private final Transformation type;
// acceptable key size in bytes, -1 if more than 1 key sizes are accepted
private final int fixedKeySize;
// associated session, if any
private Session session = null;
// key, if init() was called
private P11Key p11Key = null;
// flag indicating whether an operation is initialized
private boolean initialized = false;
// flag indicating encrypt or decrypt mode
private boolean encrypt = true;
// parameters
private byte[] iv = null;
private int tagLen = -1;
private SecureRandom random = JCAUtil.getSecureRandom();
// dataBuffer is cleared upon doFinal calls
private final ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream();
// aadBuffer is cleared upon successful init calls
private final ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();
private boolean updateCalled = false;
private boolean requireReinit = false;
private P11Key lastEncKey = null;
private byte[] lastEncIv = null;
P11AEADCipher(Token token, String algorithm, long mechanism)
throws PKCS11Exception, NoSuchAlgorithmException {
super();
this.token = token;
this.mechanism = mechanism;
String[] algoParts = algorithm.split("/");
if (algoParts[0].startsWith("AES")) {
// for AES_GCM, need 3 parts
if (algoParts.length != 3) {
throw new AssertionError("Invalid Transformation format: " +
algorithm);
}
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)) >> 3;
} else {
fixedKeySize = -1;
}
this.type = Transformation.AES_GCM;
engineSetMode(algoParts[1]);
try {
engineSetPadding(algoParts[2]);
} catch (NoSuchPaddingException e) {
throw new NoSuchAlgorithmException(e);
}
} else if (algoParts[0].equals("ChaCha20-Poly1305")) {
fixedKeySize = 32;
this.type = Transformation.CHACHA20_POLY1305;
if (algoParts.length > 3) {
throw new AssertionError(
"Invalid Transformation format: " + algorithm);
} else {
if (algoParts.length > 1) {
engineSetMode(algoParts[1]);
}
try {
if (algoParts.length > 2) {
engineSetPadding(algoParts[2]);
}
} catch (NoSuchPaddingException e) {
throw new NoSuchAlgorithmException();
}
}
} else {
throw new AssertionError("Unsupported transformation " + algorithm);
}
}
@Override
protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
if (!mode.toUpperCase(Locale.ENGLISH).equals(type.mode)) {
throw new NoSuchAlgorithmException("Unsupported mode " + mode);
}
}
// see JCE spec
@Override
protected void engineSetPadding(String padding)
throws NoSuchPaddingException {
if (!padding.toUpperCase(Locale.ENGLISH).equals(type.padding)) {
throw new NoSuchPaddingException("Unsupported padding " + padding);
}
}
// see JCE spec
@Override
protected int engineGetBlockSize() {
return switch (type) {
case AES_GCM -> 16;
case CHACHA20_POLY1305 -> 0;
default -> throw new AssertionError("Unsupported type " + type);
};
}
// see JCE spec
@Override
protected int engineGetOutputSize(int inputLen) {
return doFinalLength(inputLen);
}
// see JCE spec
@Override
protected byte[] engineGetIV() {
return (iv == null) ? null : iv.clone();
}
// see JCE spec
protected AlgorithmParameters engineGetParameters() {
String apAlgo;
AlgorithmParameterSpec spec = null;
switch (type) {
case AES_GCM -> {
apAlgo = "GCM";
if (encrypt && iv == null && tagLen == -1) {
iv = new byte[type.defIvLen];
tagLen = type.defTagLen;
random.nextBytes(iv);
}
if (iv != null) {
spec = new GCMParameterSpec(tagLen << 3, iv);
}
}
case CHACHA20_POLY1305 -> {
if (encrypt && iv == null) {
iv = new byte[type.defIvLen];
random.nextBytes(iv);
}
apAlgo = "ChaCha20-Poly1305";
if (iv != null) {
spec = new IvParameterSpec(iv);
}
}
default -> throw new AssertionError("Unsupported type " + type);
}
if (spec != null) {
try {
AlgorithmParameters params =
AlgorithmParameters.getInstance(apAlgo);
params.init(spec);
return params;
} catch (GeneralSecurityException e) {
// NoSuchAlgorithmException, NoSuchProviderException
// InvalidParameterSpecException
throw new ProviderException("Could not encode parameters", e);
}
}
return null;
}
// see JCE spec
protected void engineInit(int opmode, Key key, SecureRandom sr)
throws InvalidKeyException {
if (opmode == Cipher.DECRYPT_MODE) {
throw new InvalidKeyException("Parameters required for decryption");
}
updateCalled = false;
try {
implInit(opmode, key, null, -1, sr);
} catch (InvalidAlgorithmParameterException e) {
throw new InvalidKeyException("init() failed", e);
}
}
// see JCE spec
protected void engineInit(int opmode, Key key,
AlgorithmParameterSpec params, SecureRandom sr)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (opmode == Cipher.DECRYPT_MODE && params == null) {
throw new InvalidAlgorithmParameterException
("Parameters required for decryption");
}
updateCalled = false;
byte[] ivValue = null;
int tagLen = -1;
switch (type) {
case AES_GCM:
if (params != null) {
if (!(params instanceof GCMParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Only GCMParameterSpec is supported");
}
ivValue = ((GCMParameterSpec) params).getIV();
tagLen = ((GCMParameterSpec) params).getTLen() >> 3;
}
break;
case CHACHA20_POLY1305:
if (params != null) {
if (!(params instanceof IvParameterSpec)) {
throw new InvalidAlgorithmParameterException
("Only IvParameterSpec is supported");
}
ivValue = ((IvParameterSpec) params).getIV();
tagLen = type.defTagLen;
}
break;
default:
throw new AssertionError("Unsupported type " + type);
};
implInit(opmode, key, ivValue, tagLen, sr);
}
// see JCE spec
protected void engineInit(int opmode, Key key, AlgorithmParameters params,
SecureRandom sr)
throws InvalidKeyException, InvalidAlgorithmParameterException {
if (opmode == Cipher.DECRYPT_MODE && params == null) {
throw new InvalidAlgorithmParameterException
("Parameters required for decryption");
}
updateCalled = false;
try {
AlgorithmParameterSpec paramSpec = null;
if (params != null) {
paramSpec = switch (type) {
case AES_GCM -> params.getParameterSpec(GCMParameterSpec.class);
case CHACHA20_POLY1305 -> params.getParameterSpec(IvParameterSpec.class);
};
}
engineInit(opmode, key, paramSpec, sr);
} catch (InvalidParameterSpecException ex) {
throw new InvalidAlgorithmParameterException(ex);
}
}
// actual init() implementation
private void implInit(int opmode, Key key, byte[] iv, int tagLen,
SecureRandom sr)
throws InvalidKeyException, InvalidAlgorithmParameterException {
reset(true);
if (fixedKeySize != -1 &&
((key instanceof P11Key) ? ((P11Key) key).length() >> 3 :
key.getEncoded().length) != fixedKeySize) {
throw new InvalidKeyException("Key size is invalid");
}
P11Key newKey = P11SecretKeyFactory.convertKey(token, key,
type.keyAlgo);
switch (opmode) {
case Cipher.ENCRYPT_MODE -> {
encrypt = true;
requireReinit = Arrays.equals(iv, lastEncIv) &&
(newKey == lastEncKey);
if (requireReinit) {
throw new InvalidAlgorithmParameterException(
"Cannot reuse the same key and iv pair");
}
}
case Cipher.DECRYPT_MODE -> {
encrypt = false;
requireReinit = false;
}
case Cipher.WRAP_MODE, Cipher.UNWRAP_MODE -> throw new UnsupportedOperationException
("Unsupported mode: " + opmode);
default ->
// should never happen; checked by Cipher.init()
throw new AssertionError("Unknown mode: " + opmode);
}
// decryption without parameters is checked in all engineInit() calls
if (sr != null) {
this.random = sr;
}
if (iv == null && tagLen == -1) {
// generate default values
iv = new byte[type.defIvLen];
this.random.nextBytes(iv);
tagLen = type.defTagLen;
}
this.iv = iv;
this.tagLen = tagLen;
this.p11Key = newKey;
try {
initialize();
} catch (PKCS11Exception e) {
if (e.match(CKR_MECHANISM_PARAM_INVALID)) {
throw new InvalidAlgorithmParameterException("Bad params", e);
}
throw new InvalidKeyException("Could not initialize cipher", e);
}
}
private void cancelOperation() {
token.ensureValid();
if (P11Util.trySessionCancel(token, session,
(encrypt ? CKF_ENCRYPT : CKF_DECRYPT))) {
return;
}
// cancel by finishing operations; avoid killSession as some
// hardware vendors may require re-login
int bufLen = doFinalLength(0);
byte[] buffer = new byte[bufLen];
byte[] in = dataBuffer.toByteArray();
int inLen = in.length;
try {
if (encrypt) {
token.p11.C_Encrypt(session.id(), 0, in, 0, inLen,
0, buffer, 0, bufLen);
} else {
token.p11.C_Decrypt(session.id(), 0, in, 0, inLen,
0, buffer, 0, bufLen);
}
} catch (PKCS11Exception e) {
if (e.match(CKR_OPERATION_NOT_INITIALIZED)) {
// Cancel Operation may be invoked after an error on a PKCS#11
// call. If the operation inside the token was already cancelled,
// do not fail here. This is part of a defensive mechanism for
// PKCS#11 libraries that do not strictly follow the standard.
return;
}
if (encrypt) {
throw new ProviderException("Cancel failed", e);
}
// ignore failure for decryption
}
}
private void ensureInitialized() throws PKCS11Exception {
if (initialized && aadBuffer.size() > 0) {
// need to cancel first to avoid CKR_OPERATION_ACTIVE
reset(true);
}
if (!initialized) {
initialize();
}
}
private void initialize() throws PKCS11Exception {
if (p11Key == null) {
throw new ProviderException(
"Operation cannot be performed without"
+ " calling engineInit first");
}
if (requireReinit) {
throw new IllegalStateException
("Must use either different key or iv");
}
token.ensureValid();
byte[] aad = (aadBuffer.size() > 0 ? aadBuffer.toByteArray() : null);
long p11KeyID = p11Key.getKeyID();
try {
CK_MECHANISM mechWithParams = switch (type) {
case AES_GCM -> new CK_MECHANISM(mechanism,
new CK_GCM_PARAMS(tagLen << 3, iv, aad));
case CHACHA20_POLY1305 -> new CK_MECHANISM(mechanism,
new CK_SALSA20_CHACHA20_POLY1305_PARAMS(iv, aad));
};
if (session == null) {
session = token.getOpSession();
}
if (type == Transformation.AES_GCM) {
CK_VERSION cryptokiVersion = token.p11.getVersion();
boolean useNormativeMechFirst = cryptokiVersion.major > 2 ||
(cryptokiVersion.major == 2 && cryptokiVersion.minor >= 40);
if (encrypt) {
token.p11.C_GCMEncryptInitWithRetry(session.id(), mechWithParams,
p11KeyID, useNormativeMechFirst);
} else {
token.p11.C_GCMDecryptInitWithRetry(session.id(), mechWithParams,
p11KeyID, useNormativeMechFirst);
}
} else {
if (encrypt) {
token.p11.C_EncryptInit(session.id(), mechWithParams,
p11KeyID);
} else {
token.p11.C_DecryptInit(session.id(), mechWithParams,
p11KeyID);
}
}
} catch (PKCS11Exception e) {
p11Key.releaseKeyID();
session = token.releaseSession(session);
throw e;
} finally {
dataBuffer.reset();
aadBuffer.reset();
}
initialized = true;
}
// if doFinal(inLen) is called, how big does the output buffer have to be?
private int doFinalLength(int inLen) {
if (inLen < 0) {
throw new ProviderException("Invalid negative input length");
}
int result = inLen + dataBuffer.size();
if (encrypt) {
result += tagLen;
} else {
// In earlier NSS versions, AES_GCM would report
// CKR_BUFFER_TOO_SMALL error if minus tagLen
if (type == Transformation.CHACHA20_POLY1305) {
result -= tagLen;
}
}
return (Math.max(result, 0));
}
// reset the states to the pre-initialized values
private void reset(boolean doCancel) {
if (!initialized) {
return;
}
initialized = false;
try {
if (session == null) {
return;
}
if (doCancel && token.explicitCancel) {
cancelOperation();
}
} finally {
p11Key.releaseKeyID();
session = token.releaseSession(session);
dataBuffer.reset();
}
}
// see JCE spec
protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {
updateCalled = true;
int n = implUpdate(in, inOfs, inLen);
return new byte[0];
}
// see JCE spec
protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) throws ShortBufferException {
updateCalled = true;
implUpdate(in, inOfs, inLen);
return 0;
}
// see JCE spec
@Override
protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException {
updateCalled = true;
implUpdate(inBuffer);
return 0;
}
// see JCE spec
@Override
protected synchronized void engineUpdateAAD(byte[] src, int srcOfs, int srcLen)
throws IllegalStateException {
if ((src == null) || (srcOfs < 0) || (srcOfs + srcLen > src.length)) {
throw new IllegalArgumentException("Invalid AAD");
}
if (requireReinit) {
throw new IllegalStateException
("Must use either different key or iv for encryption");
}
if (p11Key == null) {
throw new IllegalStateException("Need to initialize Cipher first");
}
if (updateCalled) {
throw new IllegalStateException
("Update has been called; no more AAD data");
}
aadBuffer.write(src, srcOfs, srcLen);
}
// see JCE spec
@Override
protected void engineUpdateAAD(ByteBuffer src)
throws IllegalStateException {
if (src == null) {
throw new IllegalArgumentException("Invalid AAD");
}
byte[] srcBytes = new byte[src.remaining()];
src.get(srcBytes);
engineUpdateAAD(srcBytes, 0, srcBytes.length);
}
// see JCE spec
protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)
throws IllegalBlockSizeException, BadPaddingException {
int minOutLen = doFinalLength(inLen);
try {
byte[] out = new byte[minOutLen];
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);
} finally {
updateCalled = false;
}
}
// see JCE spec
protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,
int outOfs) throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
try {
return implDoFinal(in, inOfs, inLen, out, outOfs, out.length - outOfs);
} finally {
updateCalled = false;
}
}
// see JCE spec
@Override
protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
try {
return implDoFinal(inBuffer, outBuffer);
} finally {
updateCalled = false;
}
}
private int implUpdate(byte[] in, int inOfs, int inLen) {
if (inLen > 0) {
updateCalled = true;
try {
ensureInitialized();
} catch (PKCS11Exception e) {
//e.printStackTrace();
reset(false);
throw new ProviderException("update() failed", e);
}
dataBuffer.write(in, inOfs, inLen);
}
// always 0 as NSS only supports single-part encryption/decryption
return 0;
}
private int implUpdate(ByteBuffer inBuf) {
int inLen = inBuf.remaining();
if (inLen > 0) {
try {
ensureInitialized();
} catch (PKCS11Exception e) {
reset(false);
throw new ProviderException("update() failed", e);
}
byte[] data = new byte[inLen];
inBuf.get(data);
dataBuffer.write(data, 0, data.length);
}
// always 0 as NSS only supports single-part encryption/decryption
return 0;
}
private int implDoFinal(byte[] in, int inOfs, int inLen,
byte[] out, int outOfs, int outLen)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
int requiredOutLen = doFinalLength(inLen);
if (outLen < requiredOutLen) {
throw new ShortBufferException();
}
boolean doCancel = true;
try {
ensureInitialized();
if (dataBuffer.size() > 0) {
if (in != null && inOfs > 0 && inLen > 0 &&
inOfs < (in.length - inLen)) {
dataBuffer.write(in, inOfs, inLen);
}
in = dataBuffer.toByteArray();
inOfs = 0;
inLen = in.length;
}
int k = 0;
if (encrypt) {
k = token.p11.C_Encrypt(session.id(), 0, in, inOfs, inLen,
0, out, outOfs, outLen);
doCancel = false;
} else {
// Special handling to match SunJCE provider behavior
if (inLen == 0) {
return 0;
}
k = token.p11.C_Decrypt(session.id(), 0, in, inOfs, inLen,
0, out, outOfs, outLen);
doCancel = false;
}
return k;
} catch (PKCS11Exception e) {
// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only
// keep the operation active on CKR_BUFFER_TOO_SMALL errors or
// successful calls to determine the output length. However,
// these cases are not expected here because the output length
// is checked in the OpenJDK side before making the PKCS#11 call.
// Thus, doCancel can safely be 'false'.
doCancel = false;
handleException(e);
throw new ProviderException("doFinal() failed", e);
} finally {
if (encrypt) {
lastEncKey = this.p11Key;
lastEncIv = this.iv;
requireReinit = true;
}
reset(doCancel);
}
}
private int implDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
int outLen = outBuffer.remaining();
int inLen = inBuffer.remaining();
int requiredOutLen = doFinalLength(inLen);
if (outLen < requiredOutLen) {
throw new ShortBufferException();
}
boolean doCancel = true;
NIO_ACCESS.acquireSession(inBuffer);
try {
NIO_ACCESS.acquireSession(outBuffer);
try {
try {
ensureInitialized();
long inAddr = 0;
byte[] in = null;
int inOfs = 0;
if (dataBuffer.size() > 0) {
if (inLen > 0) {
byte[] temp = new byte[inLen];
inBuffer.get(temp);
dataBuffer.write(temp, 0, temp.length);
}
in = dataBuffer.toByteArray();
inOfs = 0;
inLen = in.length;
} else {
if (inBuffer instanceof DirectBuffer) {
inAddr = NIO_ACCESS.getBufferAddress(inBuffer);
inOfs = inBuffer.position();
} else {
if (inBuffer.hasArray()) {
in = inBuffer.array();
inOfs = inBuffer.position() + inBuffer.arrayOffset();
} else {
in = new byte[inLen];
inBuffer.get(in);
}
}
}
long outAddr = 0;
byte[] outArray = null;
int outOfs = 0;
if (outBuffer instanceof DirectBuffer) {
outAddr = NIO_ACCESS.getBufferAddress(outBuffer);
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) {
k = token.p11.C_Encrypt(session.id(), inAddr, in, inOfs, inLen,
outAddr, outArray, outOfs, outLen);
doCancel = false;
} else {
// Special handling to match SunJCE provider behavior
if (inLen == 0) {
return 0;
}
k = token.p11.C_Decrypt(session.id(), inAddr, in, inOfs, inLen,
outAddr, outArray, outOfs, outLen);
doCancel = false;
}
inBuffer.position(inBuffer.limit());
outBuffer.position(outBuffer.position() + k);
return k;
} catch (PKCS11Exception e) {
// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only
// keep the operation active on CKR_BUFFER_TOO_SMALL errors or
// successful calls to determine the output length. However,
// these cases are not expected here because the output length
// is checked in the OpenJDK side before making the PKCS#11 call.
// Thus, doCancel can safely be 'false'.
doCancel = false;
handleException(e);
throw new ProviderException("doFinal() failed", e);
} finally {
if (encrypt) {
lastEncKey = this.p11Key;
lastEncIv = this.iv;
requireReinit = true;
}
reset(doCancel);
}
} finally {
NIO_ACCESS.releaseSession(outBuffer);
}
} finally {
NIO_ACCESS.releaseSession(inBuffer);
}
}
private void handleException(PKCS11Exception e)
throws ShortBufferException, IllegalBlockSizeException,
BadPaddingException {
if (e.match(CKR_BUFFER_TOO_SMALL)) {
throw (ShortBufferException)
(new ShortBufferException().initCause(e));
} else if (e.match(CKR_DATA_LEN_RANGE) ||
e.match(CKR_ENCRYPTED_DATA_LEN_RANGE)) {
throw (IllegalBlockSizeException)
(new IllegalBlockSizeException(e.toString()).initCause(e));
} else if (e.match(CKR_ENCRYPTED_DATA_INVALID) ||
e.match(CKR_GENERAL_ERROR)) {
// CKR_GENERAL_ERROR is Solaris-specific workaround
throw (AEADBadTagException)
(new AEADBadTagException(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, type.keyAlgo).length();
return n;
}
}