1 /*
   2  * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.oracle.security.ucrypto;
  27 
  28 import java.util.Set;
  29 import java.util.Arrays;
  30 import java.util.concurrent.ConcurrentSkipListSet;
  31 import java.lang.ref.*;
  32 import java.math.BigInteger;
  33 import java.nio.ByteBuffer;
  34 
  35 import java.security.SignatureSpi;
  36 import java.security.NoSuchAlgorithmException;
  37 import java.security.InvalidParameterException;
  38 import java.security.InvalidKeyException;
  39 import java.security.SignatureException;
  40 import java.security.Key;
  41 import java.security.PrivateKey;
  42 import java.security.PublicKey;
  43 
  44 import java.security.*;
  45 import java.security.interfaces.*;
  46 import java.security.spec.*;
  47 
  48 import sun.nio.ch.DirectBuffer;
  49 import java.nio.ByteBuffer;
  50 
  51 /**
  52  * Signature implementation class. This class currently supports the
  53  * following algorithms:
  54  *
  55  * . RSA:
  56  *   . MD5withRSA
  57  *   . SHA1withRSA
  58  *   . SHA256withRSA
  59  *   . SHA384withRSA
  60  *   . SHA512withRSA
  61  *
  62  * @since 9
  63  */
  64 class NativeRSASignature extends SignatureSpi {
  65 
  66     private static final int PKCS1PADDING_LEN = 11;
  67 
  68     // fields set in constructor
  69     private final UcryptoMech mech;
  70     private final int encodedLen;
  71 
  72     // field for ensuring native memory is freed
  73     private SignatureContextRef pCtxt = null;
  74 
  75     //
  76     // fields (re)set in every init()
  77     //
  78     private boolean initialized = false;
  79     private boolean sign = true;
  80     private int sigLength;
  81     private NativeKey key;
  82     private NativeRSAKeyFactory keyFactory; // may need a more generic type later
  83 
  84     // public implementation classes
  85     public static final class MD5 extends NativeRSASignature {
  86         public MD5() throws NoSuchAlgorithmException {
  87             super(UcryptoMech.CRYPTO_MD5_RSA_PKCS, 34);
  88         }
  89     }
  90 
  91     public static final class SHA1 extends NativeRSASignature {
  92         public SHA1() throws NoSuchAlgorithmException {
  93             super(UcryptoMech.CRYPTO_SHA1_RSA_PKCS, 35);
  94         }
  95     }
  96 
  97     public static final class SHA256 extends NativeRSASignature {
  98         public SHA256() throws NoSuchAlgorithmException {
  99             super(UcryptoMech.CRYPTO_SHA256_RSA_PKCS, 51);
 100         }
 101     }
 102 
 103     public static final class SHA384 extends NativeRSASignature {
 104         public SHA384() throws NoSuchAlgorithmException {
 105             super(UcryptoMech.CRYPTO_SHA384_RSA_PKCS, 67);
 106         }
 107     }
 108 
 109     public static final class SHA512 extends NativeRSASignature {
 110         public SHA512() throws NoSuchAlgorithmException {
 111             super(UcryptoMech.CRYPTO_SHA512_RSA_PKCS, 83);
 112         }
 113     }
 114 
 115     // internal class for native resource cleanup
 116     private static class SignatureContextRef extends PhantomReference<NativeRSASignature>
 117         implements Comparable<SignatureContextRef> {
 118 
 119         private static ReferenceQueue<NativeRSASignature> refQueue =
 120             new ReferenceQueue<NativeRSASignature>();
 121 
 122         // Needed to keep these references from being GC'ed until when their
 123         // referents are GC'ed so we can do post-mortem processing
 124         private static Set<SignatureContextRef> refList =
 125             new ConcurrentSkipListSet<SignatureContextRef>();
 126         //           Collections.synchronizedSortedSet(new TreeSet<SignatureContextRef>());
 127 
 128         private final long id;
 129         private final boolean sign;
 130 
 131         private static void drainRefQueueBounded() {
 132             while (true) {
 133                 SignatureContextRef next = (SignatureContextRef) refQueue.poll();
 134                 if (next == null) break;
 135                 next.dispose(true);
 136             }
 137         }
 138 
 139         SignatureContextRef(NativeRSASignature ns, long id, boolean sign) {
 140             super(ns, refQueue);
 141             this.id = id;
 142             this.sign = sign;
 143             refList.add(this);
 144             UcryptoProvider.debug("Resource: track Signature Ctxt " + this.id);
 145             drainRefQueueBounded();
 146         }
 147 
 148         public int compareTo(SignatureContextRef other) {
 149             if (this.id == other.id) {
 150                 return 0;
 151             } else {
 152                 return (this.id < other.id) ? -1 : 1;
 153             }
 154         }
 155 
 156         void dispose(boolean doCancel) {
 157             refList.remove(this);
 158             try {
 159                 if (doCancel) {
 160                     UcryptoProvider.debug("Resource: free Signature Ctxt " + this.id);
 161                     NativeRSASignature.nativeFinal(id, sign, null, 0, 0);
 162                 } else {
 163                     UcryptoProvider.debug("Resource: stop tracking Signature Ctxt " + this.id);
 164                 }
 165             } finally {
 166                 this.clear();
 167             }
 168         }
 169     }
 170 
 171     NativeRSASignature(UcryptoMech mech, int encodedLen)
 172         throws NoSuchAlgorithmException {
 173         this.mech = mech;
 174         this.encodedLen = encodedLen;
 175         this.keyFactory = new NativeRSAKeyFactory();
 176     }
 177 
 178     // deprecated but abstract
 179     @Override
 180     @SuppressWarnings("deprecation")
 181     protected Object engineGetParameter(String param) throws InvalidParameterException {
 182         throw new UnsupportedOperationException("getParameter() not supported");
 183     }
 184 
 185     @Override
 186     protected AlgorithmParameters engineGetParameters() {
 187         return null;
 188     }
 189 
 190     @Override
 191     protected synchronized void engineInitSign(PrivateKey privateKey)
 192             throws InvalidKeyException {
 193         if (privateKey == null) {
 194             throw new InvalidKeyException("Key must not be null");
 195         }
 196         NativeKey newKey = key;
 197         int newSigLength = sigLength;
 198         // Need to check RSA key length whenever a new private key is set
 199         if (privateKey != key) {
 200             if (!(privateKey instanceof RSAPrivateKey)) {
 201                 throw new InvalidKeyException("RSAPrivateKey required. " +
 202                     "Received: " + privateKey.getClass().getName());
 203             }
 204             RSAPrivateKey rsaPrivKey = (RSAPrivateKey) privateKey;
 205             BigInteger mod = rsaPrivKey.getModulus();
 206             newSigLength = checkRSAKeyLength(mod);
 207             BigInteger pe = rsaPrivKey.getPrivateExponent();
 208             try {
 209                 if (rsaPrivKey instanceof RSAPrivateCrtKey) {
 210                     RSAPrivateCrtKey rsaPrivCrtKey = (RSAPrivateCrtKey) rsaPrivKey;
 211                     newKey = (NativeKey) keyFactory.engineGeneratePrivate
 212                         (new RSAPrivateCrtKeySpec(mod,
 213                                                   rsaPrivCrtKey.getPublicExponent(),
 214                                                   pe,
 215                                                   rsaPrivCrtKey.getPrimeP(),
 216                                                   rsaPrivCrtKey.getPrimeQ(),
 217                                                   rsaPrivCrtKey.getPrimeExponentP(),
 218                                                   rsaPrivCrtKey.getPrimeExponentQ(),
 219                                                   rsaPrivCrtKey.getCrtCoefficient()));
 220                 } else {
 221                     newKey = (NativeKey) keyFactory.engineGeneratePrivate
 222                            (new RSAPrivateKeySpec(mod, pe));
 223                 }
 224             } catch (InvalidKeySpecException ikse) {
 225                 throw new InvalidKeyException(ikse);
 226             }
 227         }
 228         init(true, newKey, newSigLength);
 229     }
 230 
 231 
 232     @Override
 233     protected synchronized void engineInitVerify(PublicKey publicKey)
 234             throws InvalidKeyException {
 235         if (publicKey == null) {
 236             throw new InvalidKeyException("Key must not be null");
 237         }
 238         NativeKey newKey = key;
 239         int newSigLength = sigLength;
 240         // Need to check RSA key length whenever a new public key is set
 241         if (publicKey != key) {
 242             if (publicKey instanceof RSAPublicKey) {
 243                 BigInteger mod = ((RSAPublicKey) publicKey).getModulus();
 244                 newSigLength = checkRSAKeyLength(mod);
 245                 try {
 246                     newKey = (NativeKey) keyFactory.engineGeneratePublic
 247                         (new RSAPublicKeySpec(mod, ((RSAPublicKey) publicKey).getPublicExponent()));
 248                 } catch (InvalidKeySpecException ikse) {
 249                     throw new InvalidKeyException(ikse);
 250                 }
 251             } else {
 252                 throw new InvalidKeyException("RSAPublicKey required. " +
 253                     "Received: " + publicKey.getClass().getName());
 254             }
 255         }
 256         init(false, newKey, newSigLength);
 257     }
 258 
 259     // deprecated but abstract
 260     @Override
 261     @SuppressWarnings("deprecation")
 262     protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
 263         throw new UnsupportedOperationException("setParameter() not supported");
 264     }
 265 
 266     @Override
 267     protected void engineSetParameter(AlgorithmParameterSpec params)
 268             throws InvalidAlgorithmParameterException {
 269         if (params != null) {
 270             throw new InvalidAlgorithmParameterException("No parameter accepted");
 271         }
 272     }
 273 
 274     @Override
 275     protected synchronized byte[] engineSign() throws SignatureException {
 276         try {
 277             byte[] sig = new byte[sigLength];
 278             int rv = doFinal(sig, 0, sigLength);
 279             if (rv < 0) {
 280                 throw new SignatureException(new UcryptoException(-rv));
 281             }
 282             return sig;
 283         } finally {
 284             // doFinal should already be called, no need to cancel
 285             reset(false);
 286         }
 287     }
 288 
 289     @Override
 290     protected synchronized int engineSign(byte[] outbuf, int offset, int len)
 291         throws SignatureException {
 292         boolean doCancel = true;
 293         try {
 294             if (outbuf == null || (offset < 0) ||
 295                     ((outbuf.length - offset) < sigLength) ||
 296                     (len < sigLength)) {
 297                 throw new SignatureException("Invalid output buffer. offset: " +
 298                     offset + ". len: " + len + ". sigLength: " + sigLength);
 299             }
 300             int rv = doFinal(outbuf, offset, sigLength);
 301             doCancel = false;
 302             if (rv < 0) {
 303                 throw new SignatureException(new UcryptoException(-rv));
 304             }
 305             return sigLength;
 306         } finally {
 307             reset(doCancel);
 308         }
 309     }
 310 
 311     @Override
 312     protected synchronized void engineUpdate(byte b) throws SignatureException {
 313         byte[] in = { b };
 314         int rv = update(in, 0, 1);
 315         if (rv < 0) {
 316             throw new SignatureException(new UcryptoException(-rv));
 317         }
 318     }
 319 
 320     @Override
 321     protected synchronized void engineUpdate(byte[] in, int inOfs, int inLen)
 322             throws SignatureException {
 323         if (in == null || inOfs < 0 || inLen == 0) return;
 324 
 325         int rv = update(in, inOfs, inLen);
 326         if (rv < 0) {
 327             throw new SignatureException(new UcryptoException(-rv));
 328         }
 329     }
 330 
 331     @Override
 332     protected synchronized void engineUpdate(ByteBuffer in) {
 333         if (in == null || in.remaining() == 0) return;
 334 
 335         if (in instanceof DirectBuffer == false) {
 336             // cannot do better than default impl
 337             super.engineUpdate(in);
 338             return;
 339         }
 340         long inAddr = ((DirectBuffer)in).address();
 341         int inOfs = in.position();
 342         int inLen = in.remaining();
 343 
 344         int rv = update((inAddr + inOfs), inLen);
 345         if (rv < 0) {
 346             throw new UcryptoException(-rv);
 347         }
 348         in.position(inOfs + inLen);
 349     }
 350 
 351     @Override
 352     protected synchronized boolean engineVerify(byte[] sigBytes) throws SignatureException {
 353         return engineVerify(sigBytes, 0, sigBytes.length);
 354     }
 355 
 356     @Override
 357     protected synchronized boolean engineVerify(byte[] sigBytes, int sigOfs, int sigLen)
 358         throws SignatureException {
 359         boolean doCancel = true;
 360         try {
 361             if (sigBytes == null || (sigOfs < 0) ||
 362                     ((sigBytes.length - sigOfs) < this.sigLength) ||
 363                     (sigLen != this.sigLength)) {
 364                 throw new SignatureException("Invalid signature length: got " +
 365                     sigLen + " but was expecting " + this.sigLength);
 366             }
 367 
 368             int rv = doFinal(sigBytes, sigOfs, sigLen);
 369             doCancel = false;
 370             if (rv == 0) {
 371                 return true;
 372             } else {
 373                 UcryptoProvider.debug("Signature: " + mech + " verification error " +
 374                              new UcryptoException(-rv).getMessage());
 375                 return false;
 376             }
 377         } finally {
 378             reset(doCancel);
 379         }
 380     }
 381 
 382     void reset(boolean doCancel) {
 383         initialized = false;
 384         if (pCtxt != null) {
 385             pCtxt.dispose(doCancel);
 386             pCtxt = null;
 387         }
 388     }
 389 
 390     /**
 391      * calls ucrypto_sign_init(...) or ucrypto_verify_init(...)
 392      * @return pointer to the context
 393      */
 394     private native static long nativeInit(int mech, boolean sign,
 395                                           long keyValue, int keyLength);
 396 
 397     /**
 398      * calls ucrypto_sign_update(...) or ucrypto_verify_update(...)
 399      * @return an error status code (0 means SUCCESS)
 400      */
 401     private native static int nativeUpdate(long pContext, boolean sign,
 402                                            byte[] in, int inOfs, int inLen);
 403     /**
 404      * calls ucrypto_sign_update(...) or ucrypto_verify_update(...)
 405      * @return an error status code (0 means SUCCESS)
 406      */
 407     private native static int nativeUpdate(long pContext, boolean sign,
 408                                            long pIn, int inLen);
 409 
 410     /**
 411      * calls ucrypto_sign_final(...) or ucrypto_verify_final(...)
 412      * @return the length of signature bytes or verification status.
 413      * If negative, it indicates an error status code
 414      */
 415     private native static int nativeFinal(long pContext, boolean sign,
 416                                           byte[] sig, int sigOfs, int sigLen);
 417 
 418     // actual init() implementation - caller should clone key if needed
 419     private void init(boolean sign, NativeKey key, int sigLength) {
 420         reset(true);
 421         this.sign = sign;
 422         this.sigLength = sigLength;
 423         this.key = key;
 424         long pCtxtVal = nativeInit(mech.value(), sign, key.value(),
 425                                    key.length());
 426         initialized = (pCtxtVal != 0L);
 427         if (initialized) {
 428             pCtxt = new SignatureContextRef(this, pCtxtVal, sign);
 429         } else {
 430             throw new UcryptoException("Cannot initialize Signature");
 431         }
 432     }
 433 
 434     private void ensureInitialized() {
 435         if (!initialized) {
 436             init(sign, key, sigLength);
 437             if (!initialized) {
 438                 throw new UcryptoException("Cannot initialize Signature");
 439             }
 440         }
 441     }
 442 
 443     // returns 0 (success) or negative (ucrypto error occurred)
 444     private int update(byte[] in, int inOfs, int inLen) {
 445         if (inOfs < 0 || inOfs > (in.length - inLen)) {
 446             throw new ArrayIndexOutOfBoundsException("inOfs :" + inOfs +
 447                 ". inLen: " + inLen + ". in.length: " + in.length);
 448         }
 449         ensureInitialized();
 450         int k = nativeUpdate(pCtxt.id, sign, in, inOfs, inLen);
 451         if (k < 0) {
 452             reset(false);
 453         }
 454         return k;
 455     }
 456 
 457     // returns 0 (success) or negative (ucrypto error occurred)
 458     private int update(long pIn, int inLen) {
 459         ensureInitialized();
 460         int k = nativeUpdate(pCtxt.id, sign, pIn, inLen);
 461         if (k < 0) {
 462             reset(false);
 463         }
 464         return k;
 465     }
 466 
 467     // returns 0 (success) or negative (ucrypto error occurred)
 468     private int doFinal(byte[] sigBytes, int sigOfs, int sigLen) {
 469         ensureInitialized();
 470         int k = nativeFinal(pCtxt.id, sign, sigBytes, sigOfs, sigLen);
 471         return k;
 472     }
 473 
 474     // check and return RSA key size in number of bytes
 475     private int checkRSAKeyLength(BigInteger mod) throws InvalidKeyException {
 476         int keySize = (mod.bitLength() + 7) >> 3;
 477         int maxDataSize = keySize - PKCS1PADDING_LEN;
 478         if (maxDataSize < encodedLen) {
 479             throw new InvalidKeyException
 480                 ("Key is too short for this signature algorithm. maxDataSize: " +
 481                     maxDataSize + ". encodedLen: " + encodedLen);
 482         }
 483         return keySize;
 484     }
 485 }