Path: blob/master/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/P11PSSSignature.java
41154 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*/2425package sun.security.pkcs11;2627import java.io.ByteArrayOutputStream;28import java.io.IOException;29import java.nio.ByteBuffer;30import sun.nio.ch.DirectBuffer;3132import java.util.Hashtable;33import java.util.Arrays;34import java.security.*;35import java.security.spec.AlgorithmParameterSpec;36import java.security.spec.MGF1ParameterSpec;37import java.security.spec.PSSParameterSpec;38import java.security.interfaces.*;39import sun.security.pkcs11.wrapper.*;40import sun.security.util.KnownOIDs;41import static sun.security.pkcs11.wrapper.PKCS11Constants.*;42import static sun.security.pkcs11.wrapper.PKCS11Exception.*;4344/**45* RSASSA-PSS Signature implementation class. This class currently supports the46* following algorithms:47*48* . RSA-PSS:49* . RSASSA-PSS50* . SHA1withRSASSA-PSS51* . SHA224withRSASSA-PSS52* . SHA256withRSASSA-PSS53* . SHA384withRSASSA-PSS54* . SHA512withRSASSA-PSS55* . SHA3-224withRSASSA-PSS56* . SHA3-256withRSASSA-PSS57* . SHA3-384withRSASSA-PSS58* . SHA3-512withRSASSA-PSS59*60* Note that the underlying PKCS#11 token may support complete signature61* algorithm (e.g. CKM_<md>_RSA_PKCS_PSS), or it may just62* implement the signature algorithm without hashing (i.e. CKM_RSA_PKCS_PSS).63* This class uses what is available and adds whatever extra processing64* is needed.65*66* @since 1367*/68final class P11PSSSignature extends SignatureSpi {6970private static final boolean DEBUG = false;7172// mappings of digest algorithms and their output length in bytes73private static final Hashtable<String, Integer> DIGEST_LENGTHS =74new Hashtable<String, Integer>();7576static {77DIGEST_LENGTHS.put("SHA-1", 20);78DIGEST_LENGTHS.put("SHA-224", 28);79DIGEST_LENGTHS.put("SHA-256", 32);80DIGEST_LENGTHS.put("SHA-384", 48);81DIGEST_LENGTHS.put("SHA-512", 64);82DIGEST_LENGTHS.put("SHA-512/224", 28);83DIGEST_LENGTHS.put("SHA-512/256", 32);84DIGEST_LENGTHS.put("SHA3-224", 28);85DIGEST_LENGTHS.put("SHA3-256", 32);86DIGEST_LENGTHS.put("SHA3-384", 48);87DIGEST_LENGTHS.put("SHA3-512", 64);88}8990// utility method for looking up the std digest algorithms91private static String toStdName(String givenDigestAlg) {92if (givenDigestAlg == null) return null;9394KnownOIDs given2 = KnownOIDs.findMatch(givenDigestAlg);95if (given2 == null) {96return givenDigestAlg;97} else {98return given2.stdName();99}100}101102// utility method for comparing digest algorithms103// NOTE that first argument is assumed to be standard digest name104private static boolean isDigestEqual(String stdAlg, String givenAlg) {105if (stdAlg == null || givenAlg == null) return false;106107givenAlg = toStdName(givenAlg);108return stdAlg.equalsIgnoreCase(givenAlg);109}110111// token instance112private final Token token;113114// algorithm name115private final String algorithm;116117// name of the key algorithm, currently just RSA118private static final String KEY_ALGO = "RSA";119120// mechanism id121private final CK_MECHANISM mechanism;122123// type, one of T_* below124private final int type;125126// key instance used, if init*() was called127private P11Key p11Key = null;128129// PSS parameters and the flag controlling its access130private PSSParameterSpec sigParams = null;131private boolean isActive = false;132133// message digest alg, if implied by the algorithm name134private final String mdAlg;135136// message digest, if we do the digesting ourselves137private MessageDigest md = null;138139// associated session, if any140private Session session;141142// mode, one of M_* below143private int mode;144145// flag indicating whether an operation is initialized146private boolean initialized = false;147148// buffer, for update(byte)149private final byte[] buffer = new byte[1];150151// total number of bytes processed in current operation152private int bytesProcessed = 0;153154// constant for signing mode155private static final int M_SIGN = 1;156// constant for verification mode157private static final int M_VERIFY = 2;158159// constant for type digesting, we do the hashing ourselves160private static final int T_DIGEST = 1;161// constant for type update, token does everything162private static final int T_UPDATE = 2;163164P11PSSSignature(Token token, String algorithm, long mechId)165throws NoSuchAlgorithmException, PKCS11Exception {166super();167this.token = token;168this.algorithm = algorithm;169this.mechanism = new CK_MECHANISM(mechId);170int idx = algorithm.indexOf("with");171// convert to stdName172this.mdAlg = (idx == -1?173null : toStdName(algorithm.substring(0, idx)));174175switch ((int)mechId) {176case (int)CKM_SHA1_RSA_PKCS_PSS:177case (int)CKM_SHA224_RSA_PKCS_PSS:178case (int)CKM_SHA256_RSA_PKCS_PSS:179case (int)CKM_SHA384_RSA_PKCS_PSS:180case (int)CKM_SHA512_RSA_PKCS_PSS:181case (int)CKM_SHA3_224_RSA_PKCS_PSS:182case (int)CKM_SHA3_256_RSA_PKCS_PSS:183case (int)CKM_SHA3_384_RSA_PKCS_PSS:184case (int)CKM_SHA3_512_RSA_PKCS_PSS:185type = T_UPDATE;186this.md = null;187break;188case (int)CKM_RSA_PKCS_PSS:189// check if the digest algo is supported by underlying PKCS11 lib190if (this.mdAlg != null && token.getMechanismInfo191(Functions.getHashMechId(this.mdAlg)) == null) {192throw new NoSuchAlgorithmException("Unsupported algorithm: " +193algorithm);194}195this.md = (this.mdAlg == null? null :196MessageDigest.getInstance(this.mdAlg));197type = T_DIGEST;198break;199default:200throw new ProviderException("Unsupported mechanism: " + mechId);201}202}203204private static PSSParameterSpec genDefaultParams(String digestAlg,205P11Key key) throws SignatureException {206int mdLen;207try {208mdLen = DIGEST_LENGTHS.get(digestAlg);209} catch (NullPointerException npe) {210throw new SignatureException("Unsupported digest: " +211digestAlg);212}213int saltLen = Integer.min(mdLen, (key.length() >> 3) - mdLen -2);214return new PSSParameterSpec(digestAlg,215"MGF1", new MGF1ParameterSpec(digestAlg),216saltLen, PSSParameterSpec.TRAILER_FIELD_BC);217}218219private void ensureInitialized() throws SignatureException {220token.ensureValid();221222if (this.p11Key == null) {223throw new SignatureException("Missing key");224}225if (this.sigParams == null) {226if (this.mdAlg == null) {227// PSS Parameters are required for signature verification228throw new SignatureException229("Parameters required for RSASSA-PSS signature");230}231// generate default params for both sign and verify?232this.sigParams = genDefaultParams(this.mdAlg, this.p11Key);233this.mechanism.setParameter(new CK_RSA_PKCS_PSS_PARAMS(234this.mdAlg, "MGF1", this.mdAlg, sigParams.getSaltLength()));235}236237if (initialized == false) {238try {239initialize();240} catch (ProviderException pe) {241throw new SignatureException(pe);242}243}244}245246// reset the states to the pre-initialized values247private void reset(boolean doCancel) {248if (!initialized) {249return;250}251initialized = false;252253try {254if (session == null) {255return;256}257258if (doCancel && token.explicitCancel) {259cancelOperation();260}261} finally {262p11Key.releaseKeyID();263mechanism.freeHandle();264session = token.releaseSession(session);265isActive = false;266}267}268269private void cancelOperation() {270token.ensureValid();271if (DEBUG) System.out.print("Cancelling operation");272273// cancel operation by finishing it; avoid killSession as some274// hardware vendors may require re-login275try {276if (mode == M_SIGN) {277if (type == T_UPDATE) {278if (DEBUG) System.out.println(" by C_SignFinal");279token.p11.C_SignFinal(session.id(), 0);280} else {281byte[] digest =282(md == null? new byte[0] : md.digest());283if (DEBUG) System.out.println(" by C_Sign");284token.p11.C_Sign(session.id(), digest);285}286} else { // M_VERIFY287byte[] signature =288new byte[(p11Key.length() + 7) >> 3];289if (type == T_UPDATE) {290if (DEBUG) System.out.println(" by C_VerifyFinal");291token.p11.C_VerifyFinal(session.id(), signature);292} else {293byte[] digest =294(md == null? new byte[0] : md.digest());295if (DEBUG) System.out.println(" by C_Verify");296token.p11.C_Verify(session.id(), digest, signature);297}298}299} catch (PKCS11Exception e) {300if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {301// Cancel Operation may be invoked after an error on a PKCS#11302// call. If the operation inside the token was already cancelled,303// do not fail here. This is part of a defensive mechanism for304// PKCS#11 libraries that do not strictly follow the standard.305return;306}307if (mode == M_SIGN) {308throw new ProviderException("cancel failed", e);309}310// ignore failure for verification311}312}313314// assumes current state is initialized == false315private void initialize() throws ProviderException {316if (DEBUG) System.out.println("Initializing");317318if (p11Key == null) {319throw new ProviderException(320"No Key found, call initSign/initVerify first");321}322323long keyID = p11Key.getKeyID();324try {325if (session == null) {326session = token.getOpSession();327}328if (mode == M_SIGN) {329token.p11.C_SignInit(session.id(), mechanism, keyID);330} else {331token.p11.C_VerifyInit(session.id(), mechanism, keyID);332}333} catch (PKCS11Exception e) {334p11Key.releaseKeyID();335session = token.releaseSession(session);336throw new ProviderException("Initialization failed", e);337}338if (bytesProcessed != 0) {339bytesProcessed = 0;340if (md != null) {341md.reset();342}343}344initialized = true;345isActive = false;346if (DEBUG) System.out.println("Initialized");347}348349private void checkKeySize(Key key) throws InvalidKeyException {350if (DEBUG) System.out.print("Checking Key");351352if (!key.getAlgorithm().equals(KEY_ALGO)) {353throw new InvalidKeyException("Only " + KEY_ALGO +354" keys are supported");355}356357CK_MECHANISM_INFO mechInfo = null;358try {359mechInfo = token.getMechanismInfo(mechanism.mechanism);360} catch (PKCS11Exception e) {361// should not happen, ignore for now362if (DEBUG) {363System.out.println("Unexpected exception");364e.printStackTrace();365}366}367368int keySize = 0; // in bytes369if (mechInfo != null) {370if (key instanceof P11Key) {371keySize = (((P11Key) key).length() + 7) >> 3;372} else if (key instanceof RSAKey) {373keySize = ((RSAKey) key).getModulus().bitLength() >> 3;374} else {375throw new InvalidKeyException("Unrecognized key type " + key);376}377// check against available native info which are in bits378if ((mechInfo.iMinKeySize != 0) &&379(keySize < (mechInfo.iMinKeySize >> 3))) {380throw new InvalidKeyException(KEY_ALGO +381" key must be at least " + mechInfo.iMinKeySize + " bits");382}383if ((mechInfo.iMaxKeySize != Integer.MAX_VALUE) &&384(keySize > (mechInfo.iMaxKeySize >> 3))) {385throw new InvalidKeyException(KEY_ALGO +386" key must be at most " + mechInfo.iMaxKeySize + " bits");387}388}389if (this.sigParams != null) {390String digestAlg = this.sigParams.getDigestAlgorithm();391int sLen = this.sigParams.getSaltLength();392393int hLen = DIGEST_LENGTHS.get(toStdName(digestAlg)).intValue();394int minKeyLen = Math.addExact(Math.addExact(sLen, hLen), 2);395396if (keySize < minKeyLen) {397throw new InvalidKeyException398("Key is too short for current params, need min " + minKeyLen);399}400}401}402403private void setSigParams(AlgorithmParameterSpec p)404throws InvalidAlgorithmParameterException {405if (p == null) {406throw new InvalidAlgorithmParameterException("PSS Parameter required");407}408if (!(p instanceof PSSParameterSpec)) {409throw new InvalidAlgorithmParameterException410("Only PSSParameterSpec is supported");411}412// no need to validate again if same as current signature parameters413PSSParameterSpec params = (PSSParameterSpec) p;414if (params == this.sigParams) return;415416String digestAlgorithm = params.getDigestAlgorithm();417if (this.mdAlg != null && !isDigestEqual(this.mdAlg, digestAlgorithm)) {418throw new InvalidAlgorithmParameterException419("Digest algorithm in Signature parameters must be " +420this.mdAlg);421}422423try {424if (token.getMechanismInfo(Functions.getHashMechId425(digestAlgorithm)) == null) {426throw new InvalidAlgorithmParameterException427("Unsupported digest algorithm: " + digestAlgorithm);428}429} catch (PKCS11Exception pe) {430// should not happen431throw new InvalidAlgorithmParameterException(pe);432}433434Integer digestLen = DIGEST_LENGTHS.get(toStdName(digestAlgorithm));435if (digestLen == null) {436throw new InvalidAlgorithmParameterException437("Unsupported digest algorithm in Signature parameters: " +438digestAlgorithm);439}440441if (!(params.getMGFAlgorithm().equalsIgnoreCase("MGF1"))) {442throw new InvalidAlgorithmParameterException("Only supports MGF1");443}444445// defaults to the digest algorithm unless overridden446String mgfDigestAlgo = digestAlgorithm;447AlgorithmParameterSpec mgfParams = params.getMGFParameters();448if (mgfParams != null) {449if (!(mgfParams instanceof MGF1ParameterSpec)) {450throw new InvalidAlgorithmParameterException451("Only MGF1ParameterSpec is supported");452}453mgfDigestAlgo = ((MGF1ParameterSpec)mgfParams).getDigestAlgorithm();454}455456if (params.getTrailerField() != PSSParameterSpec.TRAILER_FIELD_BC) {457throw new InvalidAlgorithmParameterException458("Only supports TrailerFieldBC(1)");459}460461int saltLen = params.getSaltLength();462if (this.p11Key != null) {463int maxSaltLen = ((this.p11Key.length() + 7) >> 3) -464digestLen.intValue() - 2;465466if (DEBUG) {467System.out.println("Max saltLen = " + maxSaltLen);468System.out.println("Curr saltLen = " + saltLen);469}470if (maxSaltLen < 0 || saltLen > maxSaltLen) {471throw new InvalidAlgorithmParameterException472("Invalid with current key size");473}474} else if (DEBUG) {475System.out.println("No key available for validating saltLen");476}477478// validated, now try to store the parameter internally479try {480this.mechanism.setParameter(481new CK_RSA_PKCS_PSS_PARAMS(digestAlgorithm, "MGF1",482mgfDigestAlgo, saltLen));483this.sigParams = params;484} catch (IllegalArgumentException iae) {485throw new InvalidAlgorithmParameterException(iae);486}487}488489// see JCA spec490@Override491protected void engineInitVerify(PublicKey publicKey)492throws InvalidKeyException {493494if (publicKey == null) {495throw new InvalidKeyException("Key must not be null");496}497498// Need to check key length whenever a new key is set499if (publicKey != p11Key) {500checkKeySize(publicKey);501}502503reset(true);504mode = M_VERIFY;505p11Key = P11KeyFactory.convertKey(token, publicKey, KEY_ALGO);506507// attempt initialization when key and params are both available508if (this.p11Key != null && this.sigParams != null) {509try {510initialize();511} catch (ProviderException pe) {512throw new InvalidKeyException(pe);513}514}515}516517// see JCA spec518@Override519protected void engineInitSign(PrivateKey privateKey)520throws InvalidKeyException {521522if (privateKey == null) {523throw new InvalidKeyException("Key must not be null");524}525526// Need to check RSA key length whenever a new key is set527if (privateKey != p11Key) {528checkKeySize(privateKey);529}530531reset(true);532mode = M_SIGN;533p11Key = P11KeyFactory.convertKey(token, privateKey, KEY_ALGO);534535// attempt initialization when key and params are both available536if (this.p11Key != null && this.sigParams != null) {537try {538initialize();539} catch (ProviderException pe) {540throw new InvalidKeyException(pe);541}542}543}544545// see JCA spec546@Override547protected void engineUpdate(byte b) throws SignatureException {548ensureInitialized();549isActive = true;550buffer[0] = b;551engineUpdate(buffer, 0, 1);552}553554// see JCA spec555@Override556protected void engineUpdate(byte[] b, int ofs, int len)557throws SignatureException {558ensureInitialized();559if (len == 0) {560return;561}562// check for overflow563if (len + bytesProcessed < 0) {564throw new ProviderException("Processed bytes limits exceeded.");565}566isActive = true;567switch (type) {568case T_UPDATE:569try {570if (mode == M_SIGN) {571System.out.println(this + ": Calling C_SignUpdate");572token.p11.C_SignUpdate(session.id(), 0, b, ofs, len);573} else {574System.out.println(this + ": Calling C_VerfifyUpdate");575token.p11.C_VerifyUpdate(session.id(), 0, b, ofs, len);576}577bytesProcessed += len;578} catch (PKCS11Exception e) {579reset(false);580throw new ProviderException(e);581}582break;583case T_DIGEST:584// should not happen as this should be covered by earlier checks585if (md == null) {586throw new ProviderException("PSS Parameters required");587}588md.update(b, ofs, len);589bytesProcessed += len;590break;591default:592throw new ProviderException("Internal error");593}594}595596// see JCA spec597@Override598protected void engineUpdate(ByteBuffer byteBuffer) {599try {600ensureInitialized();601} catch (SignatureException se) {602throw new ProviderException(se);603}604int len = byteBuffer.remaining();605if (len <= 0) {606return;607}608isActive = true;609switch (type) {610case T_UPDATE:611if (byteBuffer instanceof DirectBuffer == false) {612// cannot do better than default impl613super.engineUpdate(byteBuffer);614return;615}616long addr = ((DirectBuffer)byteBuffer).address();617int ofs = byteBuffer.position();618try {619if (mode == M_SIGN) {620System.out.println(this + ": Calling C_SignUpdate");621token.p11.C_SignUpdate622(session.id(), addr + ofs, null, 0, len);623} else {624System.out.println(this + ": Calling C_VerifyUpdate");625token.p11.C_VerifyUpdate626(session.id(), addr + ofs, null, 0, len);627}628bytesProcessed += len;629byteBuffer.position(ofs + len);630} catch (PKCS11Exception e) {631reset(false);632throw new ProviderException("Update failed", e);633}634break;635case T_DIGEST:636// should not happen as this should be covered by earlier checks637if (md == null) {638throw new ProviderException("PSS Parameters required");639}640md.update(byteBuffer);641bytesProcessed += len;642break;643default:644reset(false);645throw new ProviderException("Internal error");646}647}648649// see JCA spec650@Override651protected byte[] engineSign() throws SignatureException {652ensureInitialized();653boolean doCancel = true;654if (DEBUG) System.out.print("Generating signature");655try {656byte[] signature;657if (type == T_UPDATE) {658if (DEBUG) System.out.println(" by C_SignFinal");659signature = token.p11.C_SignFinal(session.id(), 0);660} else {661if (md == null) {662throw new ProviderException("PSS Parameters required");663}664byte[] digest = md.digest();665if (DEBUG) System.out.println(" by C_Sign");666signature = token.p11.C_Sign(session.id(), digest);667}668doCancel = false;669return signature;670} catch (PKCS11Exception pe) {671// As per the PKCS#11 standard, C_Sign and C_SignFinal may only672// keep the operation active on CKR_BUFFER_TOO_SMALL errors or673// successful calls to determine the output length. However,674// these cases are handled at OpenJDK's libj2pkcs11 native675// library. Thus, doCancel can safely be 'false' here.676doCancel = false;677throw new ProviderException(pe);678} catch (ProviderException e) {679throw e;680} finally {681reset(doCancel);682}683}684685// see JCA spec686@Override687protected boolean engineVerify(byte[] signature) throws SignatureException {688ensureInitialized();689boolean doCancel = true;690if (DEBUG) System.out.print("Verifying signature");691try {692if (type == T_UPDATE) {693if (DEBUG) System.out.println(" by C_VerifyFinal");694token.p11.C_VerifyFinal(session.id(), signature);695} else {696if (md == null) {697throw new ProviderException("PSS Parameters required");698}699byte[] digest = md.digest();700if (DEBUG) System.out.println(" by C_Verify");701token.p11.C_Verify(session.id(), digest, signature);702}703doCancel = false;704return true;705} catch (PKCS11Exception pe) {706doCancel = false;707long errorCode = pe.getErrorCode();708if (errorCode == CKR_SIGNATURE_INVALID) {709return false;710}711if (errorCode == CKR_SIGNATURE_LEN_RANGE) {712// return false rather than throwing an exception713return false;714}715// ECF bug?716if (errorCode == CKR_DATA_LEN_RANGE) {717return false;718}719throw new ProviderException(pe);720} catch (ProviderException e) {721throw e;722} finally {723reset(doCancel);724}725}726727// see JCA spec728@SuppressWarnings("deprecation")729@Override730protected void engineSetParameter(String param, Object value)731throws InvalidParameterException {732throw new UnsupportedOperationException("setParameter() not supported");733}734735// see JCA spec736@Override737protected void engineSetParameter(AlgorithmParameterSpec params)738throws InvalidAlgorithmParameterException {739// disallow changing parameters when update has been called740if (isActive) {741throw new ProviderException742("Cannot set parameters during operations");743}744setSigParams(params);745if (type == T_DIGEST) {746try {747this.md = MessageDigest.getInstance(sigParams.getDigestAlgorithm());748} catch (NoSuchAlgorithmException nsae) {749throw new InvalidAlgorithmParameterException(nsae);750}751}752753// attempt initialization when key and params are both available754if (this.p11Key != null && this.sigParams != null) {755try {756initialize();757} catch (ProviderException pe) {758throw new InvalidAlgorithmParameterException(pe);759}760}761}762763// see JCA spec764@SuppressWarnings("deprecation")765@Override766protected Object engineGetParameter(String param)767throws InvalidParameterException {768throw new UnsupportedOperationException("getParameter() not supported");769}770771// see JCA spec772@Override773protected AlgorithmParameters engineGetParameters() {774if (this.sigParams != null) {775try {776AlgorithmParameters ap = AlgorithmParameters.getInstance("RSASSA-PSS");777ap.init(this.sigParams);778return ap;779} catch (GeneralSecurityException e) {780throw new RuntimeException(e);781}782}783return null;784}785}786787788