Path: blob/master/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11AEADCipher.java
66646 views
/*1* Copyright (c) 2019, 2021, 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 it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* 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 WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 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 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/24package sun.security.pkcs11;2526import java.io.ByteArrayOutputStream;27import java.nio.ByteBuffer;28import java.util.Arrays;29import java.util.Locale;3031import java.security.*;32import java.security.spec.*;3334import javax.crypto.*;35import javax.crypto.spec.*;3637import sun.nio.ch.DirectBuffer;38import sun.security.jca.JCAUtil;39import sun.security.pkcs11.wrapper.*;40import static sun.security.pkcs11.wrapper.PKCS11Constants.*;41import static sun.security.pkcs11.wrapper.PKCS11Exception.*;4243/**44* P11 AEAD Cipher implementation class. This class currently supports45* AES cipher in GCM mode and CHACHA20-POLY1305 cipher.46*47* Note that AEAD modes do not use padding, so this class does not have48* its own padding impl. In addition, some vendors such as NSS may not support49* multi-part encryption/decryption for AEAD cipher algorithms, thus the50* current impl uses PKCS#11 C_Encrypt/C_Decrypt calls and buffers data until51* doFinal is called.52*53* @since 1354*/55final class P11AEADCipher extends CipherSpi {5657// supported AEAD algorithms/transformations58private enum Transformation {59AES_GCM("AES", "GCM", "NOPADDING", 16, 16),60CHACHA20_POLY1305("CHACHA20", "NONE", "NOPADDING", 12, 16);6162final String keyAlgo;63final String mode;64final String padding;65final int defIvLen; // in bytes66final int defTagLen; // in bytes6768Transformation(String keyAlgo, String mode, String padding,69int defIvLen, int defTagLen) {70this.keyAlgo = keyAlgo;71this.mode = mode;72this.padding = padding;73this.defIvLen = defIvLen;74this.defTagLen = defTagLen;75}76}7778// token instance79private final Token token;8081// mechanism id82private final long mechanism;8384// type of this AEAD cipher, one of Transformation enum above85private final Transformation type;8687// acceptable key size in bytes, -1 if more than 1 key sizes are accepted88private final int fixedKeySize;8990// associated session, if any91private Session session = null;9293// key, if init() was called94private P11Key p11Key = null;9596// flag indicating whether an operation is initialized97private boolean initialized = false;9899// flag indicating encrypt or decrypt mode100private boolean encrypt = true;101102// parameters103private byte[] iv = null;104private int tagLen = -1;105private SecureRandom random = JCAUtil.getSecureRandom();106107// dataBuffer is cleared upon doFinal calls108private ByteArrayOutputStream dataBuffer = new ByteArrayOutputStream();109// aadBuffer is cleared upon successful init calls110private ByteArrayOutputStream aadBuffer = new ByteArrayOutputStream();111private boolean updateCalled = false;112113private boolean requireReinit = false;114private P11Key lastEncKey = null;115private byte[] lastEncIv = null;116117P11AEADCipher(Token token, String algorithm, long mechanism)118throws PKCS11Exception, NoSuchAlgorithmException {119super();120this.token = token;121this.mechanism = mechanism;122123String[] algoParts = algorithm.split("/");124if (algoParts[0].startsWith("AES")) {125// for AES_GCM, need 3 parts126if (algoParts.length != 3) {127throw new AssertionError("Invalid Transformation format: " +128algorithm);129}130int index = algoParts[0].indexOf('_');131if (index != -1) {132// should be well-formed since we specify what we support133fixedKeySize = Integer.parseInt(algoParts[0].substring(index+1)) >> 3;134} else {135fixedKeySize = -1;136}137this.type = Transformation.AES_GCM;138engineSetMode(algoParts[1]);139try {140engineSetPadding(algoParts[2]);141} catch (NoSuchPaddingException e) {142throw new NoSuchAlgorithmException();143}144} else if (algoParts[0].equals("ChaCha20-Poly1305")) {145fixedKeySize = 32;146this.type = Transformation.CHACHA20_POLY1305;147if (algoParts.length > 3) {148throw new AssertionError(149"Invalid Transformation format: " + algorithm);150} else {151if (algoParts.length > 1) {152engineSetMode(algoParts[1]);153}154try {155if (algoParts.length > 2) {156engineSetPadding(algoParts[2]);157}158} catch (NoSuchPaddingException e) {159throw new NoSuchAlgorithmException();160}161}162} else {163throw new AssertionError("Unsupported transformation " + algorithm);164}165}166167@Override168protected void engineSetMode(String mode) throws NoSuchAlgorithmException {169if (!mode.toUpperCase(Locale.ENGLISH).equals(type.mode)) {170throw new NoSuchAlgorithmException("Unsupported mode " + mode);171}172}173174// see JCE spec175@Override176protected void engineSetPadding(String padding)177throws NoSuchPaddingException {178if (!padding.toUpperCase(Locale.ENGLISH).equals(type.padding)) {179throw new NoSuchPaddingException("Unsupported padding " + padding);180}181}182183// see JCE spec184@Override185protected int engineGetBlockSize() {186return switch (type) {187case AES_GCM -> 16;188case CHACHA20_POLY1305 -> 0;189default -> throw new AssertionError("Unsupported type " + type);190};191}192193// see JCE spec194@Override195protected int engineGetOutputSize(int inputLen) {196return doFinalLength(inputLen);197}198199// see JCE spec200@Override201protected byte[] engineGetIV() {202return (iv == null) ? null : iv.clone();203}204205// see JCE spec206protected AlgorithmParameters engineGetParameters() {207String apAlgo;208AlgorithmParameterSpec spec = null;209switch (type) {210case AES_GCM:211apAlgo = "GCM";212if (encrypt && iv == null && tagLen == -1) {213iv = new byte[type.defIvLen];214tagLen = type.defTagLen;215random.nextBytes(iv);216}217if (iv != null) {218spec = new GCMParameterSpec(tagLen << 3, iv);219}220break;221case CHACHA20_POLY1305:222if (encrypt && iv == null) {223iv = new byte[type.defIvLen];224random.nextBytes(iv);225}226apAlgo = "ChaCha20-Poly1305";227if (iv != null) {228spec = new IvParameterSpec(iv);229}230break;231default:232throw new AssertionError("Unsupported type " + type);233}234if (spec != null) {235try {236AlgorithmParameters params =237AlgorithmParameters.getInstance(apAlgo);238params.init(spec);239return params;240} catch (GeneralSecurityException e) {241// NoSuchAlgorithmException, NoSuchProviderException242// InvalidParameterSpecException243throw new ProviderException("Could not encode parameters", e);244}245}246return null;247}248249// see JCE spec250protected void engineInit(int opmode, Key key, SecureRandom sr)251throws InvalidKeyException {252if (opmode == Cipher.DECRYPT_MODE) {253throw new InvalidKeyException("Parameters required for decryption");254}255updateCalled = false;256try {257implInit(opmode, key, null, -1, sr);258} catch (InvalidAlgorithmParameterException e) {259throw new InvalidKeyException("init() failed", e);260}261}262263// see JCE spec264protected void engineInit(int opmode, Key key,265AlgorithmParameterSpec params, SecureRandom sr)266throws InvalidKeyException, InvalidAlgorithmParameterException {267if (opmode == Cipher.DECRYPT_MODE && params == null) {268throw new InvalidAlgorithmParameterException269("Parameters required for decryption");270}271updateCalled = false;272byte[] ivValue = null;273int tagLen = -1;274switch (type) {275case AES_GCM:276if (params != null) {277if (!(params instanceof GCMParameterSpec)) {278throw new InvalidAlgorithmParameterException279("Only GCMParameterSpec is supported");280}281ivValue = ((GCMParameterSpec) params).getIV();282tagLen = ((GCMParameterSpec) params).getTLen() >> 3;283}284break;285case CHACHA20_POLY1305:286if (params != null) {287if (!(params instanceof IvParameterSpec)) {288throw new InvalidAlgorithmParameterException289("Only IvParameterSpec is supported");290}291ivValue = ((IvParameterSpec) params).getIV();292tagLen = type.defTagLen;293}294break;295default:296throw new AssertionError("Unsupported type " + type);297};298implInit(opmode, key, ivValue, tagLen, sr);299}300301// see JCE spec302protected void engineInit(int opmode, Key key, AlgorithmParameters params,303SecureRandom sr)304throws InvalidKeyException, InvalidAlgorithmParameterException {305if (opmode == Cipher.DECRYPT_MODE && params == null) {306throw new InvalidAlgorithmParameterException307("Parameters required for decryption");308}309updateCalled = false;310try {311AlgorithmParameterSpec paramSpec = null;312if (params != null) {313switch (type) {314case AES_GCM:315paramSpec =316params.getParameterSpec(GCMParameterSpec.class);317break;318case CHACHA20_POLY1305:319paramSpec =320params.getParameterSpec(IvParameterSpec.class);321break;322default:323throw new AssertionError("Unsupported type " + type);324}325}326engineInit(opmode, key, paramSpec, sr);327} catch (InvalidParameterSpecException ex) {328throw new InvalidAlgorithmParameterException(ex);329}330}331332// actual init() implementation333private void implInit(int opmode, Key key, byte[] iv, int tagLen,334SecureRandom sr)335throws InvalidKeyException, InvalidAlgorithmParameterException {336reset(true);337if (fixedKeySize != -1 &&338((key instanceof P11Key) ? ((P11Key) key).length() >> 3 :339key.getEncoded().length) != fixedKeySize) {340throw new InvalidKeyException("Key size is invalid");341}342P11Key newKey = P11SecretKeyFactory.convertKey(token, key,343type.keyAlgo);344switch (opmode) {345case Cipher.ENCRYPT_MODE:346encrypt = true;347requireReinit = Arrays.equals(iv, lastEncIv) &&348(newKey == lastEncKey);349if (requireReinit) {350throw new InvalidAlgorithmParameterException(351"Cannot reuse the same key and iv pair");352}353break;354case Cipher.DECRYPT_MODE:355encrypt = false;356requireReinit = false;357break;358case Cipher.WRAP_MODE:359case Cipher.UNWRAP_MODE:360throw new UnsupportedOperationException361("Unsupported mode: " + opmode);362default:363// should never happen; checked by Cipher.init()364throw new AssertionError("Unknown mode: " + opmode);365}366367// decryption without parameters is checked in all engineInit() calls368if (sr != null) {369this.random = sr;370}371372if (iv == null && tagLen == -1) {373// generate default values374switch (type) {375case AES_GCM:376iv = new byte[type.defIvLen];377this.random.nextBytes(iv);378tagLen = type.defTagLen;379break;380case CHACHA20_POLY1305:381iv = new byte[type.defIvLen];382this.random.nextBytes(iv);383tagLen = type.defTagLen;384break;385default:386throw new AssertionError("Unsupported type " + type);387}388}389this.iv = iv;390this.tagLen = tagLen;391this.p11Key = newKey;392try {393initialize();394} catch (PKCS11Exception e) {395if (e.getErrorCode() == CKR_MECHANISM_PARAM_INVALID) {396throw new InvalidAlgorithmParameterException("Bad params", e);397}398throw new InvalidKeyException("Could not initialize cipher", e);399}400}401402private void cancelOperation() {403// cancel operation by finishing it; avoid killSession as some404// hardware vendors may require re-login405int bufLen = doFinalLength(0);406byte[] buffer = new byte[bufLen];407byte[] in = dataBuffer.toByteArray();408int inLen = in.length;409try {410if (encrypt) {411token.p11.C_Encrypt(session.id(), 0, in, 0, inLen,4120, buffer, 0, bufLen);413} else {414token.p11.C_Decrypt(session.id(), 0, in, 0, inLen,4150, buffer, 0, bufLen);416}417} catch (PKCS11Exception e) {418if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {419// Cancel Operation may be invoked after an error on a PKCS#11420// call. If the operation inside the token was already cancelled,421// do not fail here. This is part of a defensive mechanism for422// PKCS#11 libraries that do not strictly follow the standard.423return;424}425if (encrypt) {426throw new ProviderException("Cancel failed", e);427}428// ignore failure for decryption429}430}431432private void ensureInitialized() throws PKCS11Exception {433if (initialized && aadBuffer.size() > 0) {434// need to cancel first to avoid CKR_OPERATION_ACTIVE435reset(true);436}437if (!initialized) {438initialize();439}440}441442private void initialize() throws PKCS11Exception {443if (p11Key == null) {444throw new ProviderException(445"Operation cannot be performed without"446+ " calling engineInit first");447}448if (requireReinit) {449throw new IllegalStateException450("Must use either different key or iv");451}452453token.ensureValid();454455byte[] aad = (aadBuffer.size() > 0? aadBuffer.toByteArray() : null);456457long p11KeyID = p11Key.getKeyID();458try {459CK_MECHANISM mechWithParams;460switch (type) {461case AES_GCM:462mechWithParams = new CK_MECHANISM(mechanism,463new CK_GCM_PARAMS(tagLen << 3, iv, aad));464break;465case CHACHA20_POLY1305:466mechWithParams = new CK_MECHANISM(mechanism,467new CK_SALSA20_CHACHA20_POLY1305_PARAMS(iv, aad));468break;469default:470throw new AssertionError("Unsupported type: " + type);471}472if (session == null) {473session = token.getOpSession();474}475if (encrypt) {476token.p11.C_EncryptInit(session.id(), mechWithParams,477p11KeyID);478} else {479token.p11.C_DecryptInit(session.id(), mechWithParams,480p11KeyID);481}482} catch (PKCS11Exception e) {483p11Key.releaseKeyID();484session = token.releaseSession(session);485throw e;486} finally {487dataBuffer.reset();488aadBuffer.reset();489}490initialized = true;491}492493// if doFinal(inLen) is called, how big does the output buffer have to be?494private int doFinalLength(int inLen) {495if (inLen < 0) {496throw new ProviderException("Invalid negative input length");497}498499int result = inLen + dataBuffer.size();500if (encrypt) {501result += tagLen;502} else {503// In earlier NSS versions, AES_GCM would report504// CKR_BUFFER_TOO_SMALL error if minus tagLen505if (type == Transformation.CHACHA20_POLY1305) {506result -= tagLen;507}508}509return (result > 0? result : 0);510}511512// reset the states to the pre-initialized values513private void reset(boolean doCancel) {514if (!initialized) {515return;516}517initialized = false;518519try {520if (session == null) {521return;522}523524if (doCancel && token.explicitCancel) {525cancelOperation();526}527} finally {528p11Key.releaseKeyID();529session = token.releaseSession(session);530dataBuffer.reset();531}532}533534// see JCE spec535protected byte[] engineUpdate(byte[] in, int inOfs, int inLen) {536updateCalled = true;537int n = implUpdate(in, inOfs, inLen);538return new byte[0];539}540541// see JCE spec542protected int engineUpdate(byte[] in, int inOfs, int inLen, byte[] out,543int outOfs) throws ShortBufferException {544updateCalled = true;545implUpdate(in, inOfs, inLen);546return 0;547}548549// see JCE spec550@Override551protected int engineUpdate(ByteBuffer inBuffer, ByteBuffer outBuffer)552throws ShortBufferException {553updateCalled = true;554implUpdate(inBuffer);555return 0;556}557558// see JCE spec559@Override560protected synchronized void engineUpdateAAD(byte[] src, int srcOfs, int srcLen)561throws IllegalStateException {562if ((src == null) || (srcOfs < 0) || (srcOfs + srcLen > src.length)) {563throw new IllegalArgumentException("Invalid AAD");564}565if (requireReinit) {566throw new IllegalStateException567("Must use either different key or iv for encryption");568}569if (p11Key == null) {570throw new IllegalStateException("Need to initialize Cipher first");571}572if (updateCalled) {573throw new IllegalStateException574("Update has been called; no more AAD data");575}576aadBuffer.write(src, srcOfs, srcLen);577}578579// see JCE spec580@Override581protected void engineUpdateAAD(ByteBuffer src)582throws IllegalStateException {583if (src == null) {584throw new IllegalArgumentException("Invalid AAD");585}586byte[] srcBytes = new byte[src.remaining()];587src.get(srcBytes);588engineUpdateAAD(srcBytes, 0, srcBytes.length);589}590591// see JCE spec592protected byte[] engineDoFinal(byte[] in, int inOfs, int inLen)593throws IllegalBlockSizeException, BadPaddingException {594int minOutLen = doFinalLength(inLen);595try {596byte[] out = new byte[minOutLen];597int n = engineDoFinal(in, inOfs, inLen, out, 0);598return P11Util.convert(out, 0, n);599} catch (ShortBufferException e) {600// convert since the output length is calculated by doFinalLength()601throw new ProviderException(e);602} finally {603updateCalled = false;604}605}606// see JCE spec607protected int engineDoFinal(byte[] in, int inOfs, int inLen, byte[] out,608int outOfs) throws ShortBufferException, IllegalBlockSizeException,609BadPaddingException {610try {611return implDoFinal(in, inOfs, inLen, out, outOfs, out.length - outOfs);612} finally {613updateCalled = false;614}615}616617// see JCE spec618@Override619protected int engineDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)620throws ShortBufferException, IllegalBlockSizeException,621BadPaddingException {622try {623return implDoFinal(inBuffer, outBuffer);624} finally {625updateCalled = false;626}627}628629private int implUpdate(byte[] in, int inOfs, int inLen) {630if (inLen > 0) {631updateCalled = true;632try {633ensureInitialized();634} catch (PKCS11Exception e) {635//e.printStackTrace();636reset(false);637throw new ProviderException("update() failed", e);638}639dataBuffer.write(in, inOfs, inLen);640}641// always 0 as NSS only supports single-part encryption/decryption642return 0;643}644645private int implUpdate(ByteBuffer inBuf) {646int inLen = inBuf.remaining();647if (inLen > 0) {648try {649ensureInitialized();650} catch (PKCS11Exception e) {651reset(false);652throw new ProviderException("update() failed", e);653}654byte[] data = new byte[inLen];655inBuf.get(data);656dataBuffer.write(data, 0, data.length);657}658// always 0 as NSS only supports single-part encryption/decryption659return 0;660}661662private int implDoFinal(byte[] in, int inOfs, int inLen,663byte[] out, int outOfs, int outLen)664throws ShortBufferException, IllegalBlockSizeException,665BadPaddingException {666int requiredOutLen = doFinalLength(inLen);667if (outLen < requiredOutLen) {668throw new ShortBufferException();669}670671boolean doCancel = true;672try {673ensureInitialized();674if (dataBuffer.size() > 0) {675if (in != null && inOfs > 0 && inLen > 0 &&676inOfs < (in.length - inLen)) {677dataBuffer.write(in, inOfs, inLen);678}679in = dataBuffer.toByteArray();680inOfs = 0;681inLen = in.length;682}683int k = 0;684if (encrypt) {685k = token.p11.C_Encrypt(session.id(), 0, in, inOfs, inLen,6860, out, outOfs, outLen);687doCancel = false;688} else {689// Special handling to match SunJCE provider behavior690if (inLen == 0) {691return 0;692}693k = token.p11.C_Decrypt(session.id(), 0, in, inOfs, inLen,6940, out, outOfs, outLen);695doCancel = false;696}697return k;698} catch (PKCS11Exception e) {699// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only700// keep the operation active on CKR_BUFFER_TOO_SMALL errors or701// successful calls to determine the output length. However,702// these cases are not expected here because the output length703// is checked in the OpenJDK side before making the PKCS#11 call.704// Thus, doCancel can safely be 'false'.705doCancel = false;706handleException(e);707throw new ProviderException("doFinal() failed", e);708} finally {709if (encrypt) {710lastEncKey = this.p11Key;711lastEncIv = this.iv;712requireReinit = true;713}714reset(doCancel);715}716}717718private int implDoFinal(ByteBuffer inBuffer, ByteBuffer outBuffer)719throws ShortBufferException, IllegalBlockSizeException,720BadPaddingException {721int outLen = outBuffer.remaining();722int inLen = inBuffer.remaining();723724int requiredOutLen = doFinalLength(inLen);725if (outLen < requiredOutLen) {726throw new ShortBufferException();727}728729boolean doCancel = true;730try {731ensureInitialized();732733long inAddr = 0;734byte[] in = null;735int inOfs = 0;736if (dataBuffer.size() > 0) {737if (inLen > 0) {738byte[] temp = new byte[inLen];739inBuffer.get(temp);740dataBuffer.write(temp, 0, temp.length);741}742in = dataBuffer.toByteArray();743inOfs = 0;744inLen = in.length;745} else {746if (inBuffer instanceof DirectBuffer) {747inAddr = ((DirectBuffer) inBuffer).address();748inOfs = inBuffer.position();749} else {750if (inBuffer.hasArray()) {751in = inBuffer.array();752inOfs = inBuffer.position() + inBuffer.arrayOffset();753} else {754in = new byte[inLen];755inBuffer.get(in);756}757}758}759long outAddr = 0;760byte[] outArray = null;761int outOfs = 0;762if (outBuffer instanceof DirectBuffer) {763outAddr = ((DirectBuffer) outBuffer).address();764outOfs = outBuffer.position();765} else {766if (outBuffer.hasArray()) {767outArray = outBuffer.array();768outOfs = outBuffer.position() + outBuffer.arrayOffset();769} else {770outArray = new byte[outLen];771}772}773774int k = 0;775if (encrypt) {776k = token.p11.C_Encrypt(session.id(), inAddr, in, inOfs, inLen,777outAddr, outArray, outOfs, outLen);778doCancel = false;779} else {780// Special handling to match SunJCE provider behavior781if (inLen == 0) {782return 0;783}784k = token.p11.C_Decrypt(session.id(), inAddr, in, inOfs, inLen,785outAddr, outArray, outOfs, outLen);786doCancel = false;787}788inBuffer.position(inBuffer.limit());789outBuffer.position(outBuffer.position() + k);790return k;791} catch (PKCS11Exception e) {792// As per the PKCS#11 standard, C_Encrypt and C_Decrypt may only793// keep the operation active on CKR_BUFFER_TOO_SMALL errors or794// successful calls to determine the output length. However,795// these cases are not expected here because the output length796// is checked in the OpenJDK side before making the PKCS#11 call.797// Thus, doCancel can safely be 'false'.798doCancel = false;799handleException(e);800throw new ProviderException("doFinal() failed", e);801} finally {802if (encrypt) {803lastEncKey = this.p11Key;804lastEncIv = this.iv;805requireReinit = true;806}807reset(doCancel);808}809}810811private void handleException(PKCS11Exception e)812throws ShortBufferException, IllegalBlockSizeException,813BadPaddingException {814long errorCode = e.getErrorCode();815if (errorCode == CKR_BUFFER_TOO_SMALL) {816throw (ShortBufferException)817(new ShortBufferException().initCause(e));818} else if (errorCode == CKR_DATA_LEN_RANGE ||819errorCode == CKR_ENCRYPTED_DATA_LEN_RANGE) {820throw (IllegalBlockSizeException)821(new IllegalBlockSizeException(e.toString()).initCause(e));822} else if (errorCode == CKR_ENCRYPTED_DATA_INVALID ||823// Solaris-specific824errorCode == CKR_GENERAL_ERROR) {825throw (AEADBadTagException)826(new AEADBadTagException(e.toString()).initCause(e));827}828}829830// see JCE spec831protected byte[] engineWrap(Key key) throws IllegalBlockSizeException,832InvalidKeyException {833// XXX key wrapping834throw new UnsupportedOperationException("engineWrap()");835}836837// see JCE spec838protected Key engineUnwrap(byte[] wrappedKey, String wrappedKeyAlgorithm,839int wrappedKeyType)840throws InvalidKeyException, NoSuchAlgorithmException {841// XXX key unwrapping842throw new UnsupportedOperationException("engineUnwrap()");843}844845// see JCE spec846@Override847protected int engineGetKeySize(Key key) throws InvalidKeyException {848int n = P11SecretKeyFactory.convertKey849(token, key, type.keyAlgo).length();850return n;851}852}853854855856