Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/pkcs11/P11PSSSignature.java
38919 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 static sun.security.pkcs11.wrapper.PKCS11Constants.*;414243/**44* RSASSA-PSS Signature implementation class. This class currently supports the45* following algorithms:46*47* . RSA-PSS:48* . RSASSA-PSS49* . SHA1withRSASSA-PSS50* . SHA224withRSASSA-PSS51* . SHA256withRSASSA-PSS52* . SHA384withRSASSA-PSS53* . SHA512withRSASSA-PSS54*55* Note that the underlying PKCS#11 token may support complete signature56* algorithm (e.g. CKM_<md>_RSA_PKCS_PSS), or it may just57* implement the signature algorithm without hashing (i.e. CKM_RSA_PKCS_PSS).58* This class uses what is available and adds whatever extra processing59* is needed.60*61* @since 1362*/63final class P11PSSSignature extends SignatureSpi {6465private final static boolean DEBUG = false;6667// mappings of digest algorithms and their output length in bytes68private static final Hashtable<String, Integer> DIGEST_LENGTHS =69new Hashtable<String, Integer>();7071static {72DIGEST_LENGTHS.put("SHA-1", 20);73DIGEST_LENGTHS.put("SHA", 20);74DIGEST_LENGTHS.put("SHA1", 20);75DIGEST_LENGTHS.put("SHA-224", 28);76DIGEST_LENGTHS.put("SHA224", 28);77DIGEST_LENGTHS.put("SHA-256", 32);78DIGEST_LENGTHS.put("SHA256", 32);79DIGEST_LENGTHS.put("SHA-384", 48);80DIGEST_LENGTHS.put("SHA384", 48);81DIGEST_LENGTHS.put("SHA-512", 64);82DIGEST_LENGTHS.put("SHA512", 64);83DIGEST_LENGTHS.put("SHA-512/224", 28);84DIGEST_LENGTHS.put("SHA512/224", 28);85DIGEST_LENGTHS.put("SHA-512/256", 32);86DIGEST_LENGTHS.put("SHA512/256", 32);87}8889// utility method for comparing digest algorithms90// NOTE that first argument is assumed to be standard digest name91private static boolean isDigestEqual(String stdAlg, String givenAlg) {92if (stdAlg == null || givenAlg == null) return false;9394if (givenAlg.indexOf("-") != -1) {95return stdAlg.equalsIgnoreCase(givenAlg);96} else {97if (stdAlg.equals("SHA-1")) {98return (givenAlg.equalsIgnoreCase("SHA")99|| givenAlg.equalsIgnoreCase("SHA1"));100} else {101StringBuilder sb = new StringBuilder(givenAlg);102// case-insensitive check103if (givenAlg.regionMatches(true, 0, "SHA", 0, 3)) {104givenAlg = sb.insert(3, "-").toString();105return stdAlg.equalsIgnoreCase(givenAlg);106} else {107throw new ProviderException("Unsupported digest algorithm "108+ givenAlg);109}110}111}112}113114// token instance115private final Token token;116117// algorithm name118private final String algorithm;119120// name of the key algorithm, currently just RSA121private static final String KEY_ALGO = "RSA";122123// mechanism id124private final CK_MECHANISM mechanism;125126// type, one of T_* below127private final int type;128129// key instance used, if init*() was called130private P11Key p11Key = null;131132// PSS parameters and the flag controlling its access133private PSSParameterSpec sigParams = null;134private boolean isActive = false;135136// message digest alg, if implied by the algorithm name137private final String mdAlg;138139// message digest, if we do the digesting ourselves140private MessageDigest md = null;141142// associated session, if any143private Session session;144145// mode, one of M_* below146private int mode;147148// flag indicating whether an operation is initialized149private boolean initialized = false;150151// buffer, for update(byte)152private final byte[] buffer = new byte[1];153154// total number of bytes processed in current operation155private int bytesProcessed = 0;156157// constant for signing mode158private final static int M_SIGN = 1;159// constant for verification mode160private final static int M_VERIFY = 2;161162// constant for type digesting, we do the hashing ourselves163private final static int T_DIGEST = 1;164// constant for type update, token does everything165private final static int T_UPDATE = 2;166167P11PSSSignature(Token token, String algorithm, long mechId)168throws NoSuchAlgorithmException, PKCS11Exception {169super();170this.token = token;171this.algorithm = algorithm;172this.mechanism = new CK_MECHANISM(mechId);173int idx = algorithm.indexOf("with");174this.mdAlg = (idx == -1? null : algorithm.substring(0, idx));175switch ((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:181type = T_UPDATE;182break;183case (int)CKM_RSA_PKCS_PSS:184type = T_DIGEST;185break;186default:187throw new ProviderException("Unsupported mechanism: " + mechId);188}189this.md = null;190}191192private void ensureInitialized() throws SignatureException {193token.ensureValid();194if (this.p11Key == null) {195throw new SignatureException("Missing key");196}197if (this.sigParams == null) {198if (this.mdAlg == null) {199// PSS Parameters are required for signature verification200throw new SignatureException201("Parameters required for RSASSA-PSS signature");202} else {203int saltLen = DIGEST_LENGTHS.get(this.mdAlg).intValue();204// generate default params for both sign and verify?205this.sigParams = new PSSParameterSpec(this.mdAlg,206"MGF1", new MGF1ParameterSpec(this.mdAlg),207saltLen, PSSParameterSpec.TRAILER_FIELD_BC);208this.mechanism.setParameter(new CK_RSA_PKCS_PSS_PARAMS(209this.mdAlg, "MGF1", this.mdAlg,210DIGEST_LENGTHS.get(this.mdAlg).intValue()));211}212}213214if (initialized == false) {215initialize();216}217}218219// reset the states to the pre-initialized values220private void reset(boolean doCancel) {221if (!initialized) {222return;223}224initialized = false;225226try {227if (session == null) {228return;229}230231if (doCancel && token.explicitCancel) {232cancelOperation();233}234} finally {235p11Key.releaseKeyID();236mechanism.freeHandle();237session = token.releaseSession(session);238isActive = false;239}240}241242private void cancelOperation() {243token.ensureValid();244if (DEBUG) System.out.print("Cancelling operation");245246// cancel operation by finishing it; avoid killSession as some247// hardware vendors may require re-login248try {249if (mode == M_SIGN) {250if (type == T_UPDATE) {251if (DEBUG) System.out.println(" by C_SignFinal");252token.p11.C_SignFinal(session.id(), 0);253} else {254byte[] digest =255(md == null? new byte[0] : md.digest());256if (DEBUG) System.out.println(" by C_Sign");257token.p11.C_Sign(session.id(), digest);258}259} else { // M_VERIFY260byte[] signature =261new byte[(p11Key.length() + 7) >> 3];262if (type == T_UPDATE) {263if (DEBUG) System.out.println(" by C_VerifyFinal");264token.p11.C_VerifyFinal(session.id(), signature);265} else {266byte[] digest =267(md == null? new byte[0] : md.digest());268if (DEBUG) System.out.println(" by C_Verify");269token.p11.C_Verify(session.id(), digest, signature);270}271}272} catch (PKCS11Exception e) {273if (e.getErrorCode() == CKR_OPERATION_NOT_INITIALIZED) {274// Cancel Operation may be invoked after an error on a PKCS#11275// call. If the operation inside the token was already cancelled,276// do not fail here. This is part of a defensive mechanism for277// PKCS#11 libraries that do not strictly follow the standard.278return;279}280if (mode == M_SIGN) {281throw new ProviderException("cancel failed", e);282}283// ignore failure for verification284}285}286287// assumes current state is initialized == false288private void initialize() {289if (DEBUG) System.out.println("Initializing");290291if (p11Key == null) {292throw new ProviderException(293"No Key found, call initSign/initVerify first");294}295296long keyID = p11Key.getKeyID();297try {298if (session == null) {299session = token.getOpSession();300}301if (mode == M_SIGN) {302token.p11.C_SignInit(session.id(), mechanism, keyID);303} else {304token.p11.C_VerifyInit(session.id(), mechanism, keyID);305}306} catch (PKCS11Exception e) {307p11Key.releaseKeyID();308session = token.releaseSession(session);309throw new ProviderException("Initialization failed", e);310}311if (bytesProcessed != 0) {312bytesProcessed = 0;313if (md != null) {314md.reset();315}316}317initialized = true;318isActive = false;319if (DEBUG) System.out.println("Initialized");320}321322private void checkKeySize(Key key) throws InvalidKeyException {323if (DEBUG) System.out.print("Checking Key");324325if (!key.getAlgorithm().equals(KEY_ALGO)) {326throw new InvalidKeyException("Only " + KEY_ALGO +327" keys are supported");328}329330CK_MECHANISM_INFO mechInfo = null;331try {332mechInfo = token.getMechanismInfo(mechanism.mechanism);333} catch (PKCS11Exception e) {334// should not happen, ignore for now335if (DEBUG) {336System.out.println("Unexpected exception");337e.printStackTrace();338}339}340341int keySize = 0; // in bytes342if (mechInfo != null) {343// check against available native info344int minKeySize = (int) mechInfo.ulMinKeySize;345int maxKeySize = (int) mechInfo.ulMaxKeySize;346if (key instanceof P11Key) {347keySize = (((P11Key) key).length() + 7) >> 3;348} else if (key instanceof RSAKey) {349keySize = ((RSAKey) key).getModulus().bitLength() >> 3;350} else {351throw new InvalidKeyException("Unrecognized key type " + key);352}353if ((minKeySize != -1) && (keySize < minKeySize)) {354throw new InvalidKeyException(KEY_ALGO +355" key must be at least " + minKeySize + " bytes");356}357if ((maxKeySize != -1) && (keySize > maxKeySize)) {358throw new InvalidKeyException(KEY_ALGO +359" key must be at most " + maxKeySize + " bytes");360}361}362if (this.sigParams != null) {363String digestAlg = this.sigParams.getDigestAlgorithm();364int sLen = this.sigParams.getSaltLength();365int hLen = DIGEST_LENGTHS.get(digestAlg).intValue();366int minKeyLen = Math.addExact(Math.addExact(sLen, hLen), 2);367368if (keySize < minKeyLen) {369throw new InvalidKeyException370("Key is too short for current params, need min " + minKeyLen);371}372}373}374375private void setSigParams(AlgorithmParameterSpec p)376throws InvalidAlgorithmParameterException {377if (p == null) {378throw new InvalidAlgorithmParameterException("PSS Parameter required");379}380if (!(p instanceof PSSParameterSpec)) {381throw new InvalidAlgorithmParameterException382("Only PSSParameterSpec is supported");383}384// no need to validate again if same as current signature parameters385PSSParameterSpec params = (PSSParameterSpec) p;386if (params == this.sigParams) return;387388String digestAlgorithm = params.getDigestAlgorithm();389if (this.mdAlg != null && !isDigestEqual(digestAlgorithm, this.mdAlg)) {390throw new InvalidAlgorithmParameterException391("Digest algorithm in Signature parameters must be " +392this.mdAlg);393}394Integer digestLen = DIGEST_LENGTHS.get(digestAlgorithm);395if (digestLen == null) {396throw new InvalidAlgorithmParameterException397("Unsupported digest algorithm in Signature parameters: " +398digestAlgorithm);399}400if (!(params.getMGFAlgorithm().equalsIgnoreCase("MGF1"))) {401throw new InvalidAlgorithmParameterException("Only supports MGF1");402}403if (params.getTrailerField() != PSSParameterSpec.TRAILER_FIELD_BC) {404throw new InvalidAlgorithmParameterException405("Only supports TrailerFieldBC(1)");406}407int saltLen = params.getSaltLength();408if (this.p11Key != null) {409int maxSaltLen = ((this.p11Key.length() + 7) >> 3) - digestLen.intValue() - 2;410411if (DEBUG) {412System.out.println("Max saltLen = " + maxSaltLen);413System.out.println("Curr saltLen = " + saltLen);414}415if (maxSaltLen < 0 || saltLen > maxSaltLen) {416throw new InvalidAlgorithmParameterException("Invalid with current key size");417}418} else {419if (DEBUG) System.out.println("No key available for validating saltLen");420}421422// validated, now try to store the parameter internally423try {424this.mechanism.setParameter(425new CK_RSA_PKCS_PSS_PARAMS(digestAlgorithm, "MGF1",426digestAlgorithm, saltLen));427this.sigParams = params;428} catch (IllegalArgumentException iae) {429throw new InvalidAlgorithmParameterException(iae);430}431}432433// see JCA spec434@Override435protected void engineInitVerify(PublicKey publicKey)436throws InvalidKeyException {437438if (publicKey == null) {439throw new InvalidKeyException("Key must not be null");440}441442// Need to check key length whenever a new key is set443if (publicKey != p11Key) {444checkKeySize(publicKey);445}446447reset(true);448mode = M_VERIFY;449p11Key = P11KeyFactory.convertKey(token, publicKey, KEY_ALGO);450451// For PSS, defer PKCS11 initialization calls to update/doFinal as it452// needs both key and params453}454455// see JCA spec456@Override457protected void engineInitSign(PrivateKey privateKey)458throws InvalidKeyException {459460if (privateKey == null) {461throw new InvalidKeyException("Key must not be null");462}463464// Need to check RSA key length whenever a new key is set465if (privateKey != p11Key) {466checkKeySize(privateKey);467}468469reset(true);470mode = M_SIGN;471p11Key = P11KeyFactory.convertKey(token, privateKey, KEY_ALGO);472473// For PSS, defer PKCS11 initialization calls to update/doFinal as it474// needs both key and params475}476477// see JCA spec478@Override479protected void engineUpdate(byte b) throws SignatureException {480ensureInitialized();481isActive = true;482buffer[0] = b;483engineUpdate(buffer, 0, 1);484}485486// see JCA spec487@Override488protected void engineUpdate(byte[] b, int ofs, int len)489throws SignatureException {490ensureInitialized();491if (len == 0) {492return;493}494// check for overflow495if (len + bytesProcessed < 0) {496throw new ProviderException("Processed bytes limits exceeded.");497}498isActive = true;499switch (type) {500case T_UPDATE:501try {502if (mode == M_SIGN) {503System.out.println(this + ": Calling C_SignUpdate");504token.p11.C_SignUpdate(session.id(), 0, b, ofs, len);505} else {506System.out.println(this + ": Calling C_VerfifyUpdate");507token.p11.C_VerifyUpdate(session.id(), 0, b, ofs, len);508}509bytesProcessed += len;510} catch (PKCS11Exception e) {511reset(false);512throw new ProviderException(e);513}514break;515case T_DIGEST:516// should not happen as this should be covered by earlier checks517if (md == null) {518throw new ProviderException("PSS Parameters required");519}520md.update(b, ofs, len);521bytesProcessed += len;522break;523default:524throw new ProviderException("Internal error");525}526}527528// see JCA spec529@Override530protected void engineUpdate(ByteBuffer byteBuffer) {531try {532ensureInitialized();533} catch (SignatureException se) {534throw new ProviderException(se);535}536int len = byteBuffer.remaining();537if (len <= 0) {538return;539}540isActive = true;541switch (type) {542case T_UPDATE:543if (byteBuffer instanceof DirectBuffer == false) {544// cannot do better than default impl545super.engineUpdate(byteBuffer);546return;547}548long addr = ((DirectBuffer)byteBuffer).address();549int ofs = byteBuffer.position();550try {551if (mode == M_SIGN) {552System.out.println(this + ": Calling C_SignUpdate");553token.p11.C_SignUpdate554(session.id(), addr + ofs, null, 0, len);555} else {556System.out.println(this + ": Calling C_VerifyUpdate");557token.p11.C_VerifyUpdate558(session.id(), addr + ofs, null, 0, len);559}560bytesProcessed += len;561byteBuffer.position(ofs + len);562} catch (PKCS11Exception e) {563reset(false);564throw new ProviderException("Update failed", e);565}566break;567case T_DIGEST:568// should not happen as this should be covered by earlier checks569if (md == null) {570throw new ProviderException("PSS Parameters required");571}572md.update(byteBuffer);573bytesProcessed += len;574break;575default:576reset(false);577throw new ProviderException("Internal error");578}579}580581// see JCA spec582@Override583protected byte[] engineSign() throws SignatureException {584ensureInitialized();585boolean doCancel = true;586if (DEBUG) System.out.print("Generating signature");587try {588byte[] signature;589if (type == T_UPDATE) {590if (DEBUG) System.out.println(" by C_SignFinal");591signature = token.p11.C_SignFinal(session.id(), 0);592} else {593if (md == null) {594throw new ProviderException("PSS Parameters required");595}596byte[] digest = md.digest();597if (DEBUG) System.out.println(" by C_Sign");598signature = token.p11.C_Sign(session.id(), digest);599}600doCancel = false;601return signature;602} catch (PKCS11Exception pe) {603// As per the PKCS#11 standard, C_Sign and C_SignFinal may only604// keep the operation active on CKR_BUFFER_TOO_SMALL errors or605// successful calls to determine the output length. However,606// these cases are handled at OpenJDK's libj2pkcs11 native607// library. Thus, doCancel can safely be 'false' here.608doCancel = false;609throw new ProviderException(pe);610} catch (ProviderException e) {611throw e;612} finally {613reset(doCancel);614}615}616617// see JCA spec618@Override619protected boolean engineVerify(byte[] signature) throws SignatureException {620ensureInitialized();621boolean doCancel = true;622if (DEBUG) System.out.print("Verifying signature");623try {624if (type == T_UPDATE) {625if (DEBUG) System.out.println(" by C_VerifyFinal");626token.p11.C_VerifyFinal(session.id(), signature);627} else {628if (md == null) {629throw new ProviderException("PSS Parameters required");630}631byte[] digest = md.digest();632if (DEBUG) System.out.println(" by C_Verify");633token.p11.C_Verify(session.id(), digest, signature);634}635doCancel = false;636return true;637} catch (PKCS11Exception pe) {638doCancel = false;639long errorCode = pe.getErrorCode();640if (errorCode == CKR_SIGNATURE_INVALID) {641return false;642}643if (errorCode == CKR_SIGNATURE_LEN_RANGE) {644// return false rather than throwing an exception645return false;646}647// ECF bug?648if (errorCode == CKR_DATA_LEN_RANGE) {649return false;650}651throw new ProviderException(pe);652} catch (ProviderException e) {653throw e;654} finally {655reset(doCancel);656}657}658659// see JCA spec660@SuppressWarnings("deprecation")661@Override662protected void engineSetParameter(String param, Object value)663throws InvalidParameterException {664throw new UnsupportedOperationException("setParameter() not supported");665}666667// see JCA spec668@Override669protected void engineSetParameter(AlgorithmParameterSpec params)670throws InvalidAlgorithmParameterException {671// disallow changing parameters when update has been called672if (isActive) {673throw new ProviderException674("Cannot set parameters during operations");675}676setSigParams(params);677if (type == T_DIGEST) {678try {679this.md = MessageDigest.getInstance(sigParams.getDigestAlgorithm());680} catch (NoSuchAlgorithmException nsae) {681throw new InvalidAlgorithmParameterException(nsae);682}683}684}685686// see JCA spec687@SuppressWarnings("deprecation")688@Override689protected Object engineGetParameter(String param)690throws InvalidParameterException {691throw new UnsupportedOperationException("getParameter() not supported");692}693694// see JCA spec695@Override696protected AlgorithmParameters engineGetParameters() {697if (this.sigParams != null) {698try {699AlgorithmParameters ap = AlgorithmParameters.getInstance("RSASSA-PSS");700ap.init(this.sigParams);701return ap;702} catch (GeneralSecurityException e) {703throw new RuntimeException(e);704}705}706return null;707}708}709710711