1 /*
   2  * Copyright (c) 2014, 2015, 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 1.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     @SuppressWarnings("deprecation")
 180     protected Object engineGetParameter(String param) throws InvalidParameterException {
 181         throw new UnsupportedOperationException("getParameter() not supported");
 182     }
 183 
 184     @Override
 185     protected synchronized void engineInitSign(PrivateKey privateKey)
 186             throws InvalidKeyException {
 187         if (privateKey == null) {
 188             throw new InvalidKeyException("Key must not be null");
 189         }
 190         NativeKey newKey = key;
 191         int newSigLength = sigLength;
 192         // Need to check RSA key length whenever a new private key is set
 193         if (privateKey != key) {
 194             if (!(privateKey instanceof RSAPrivateKey)) {
 195                 throw new InvalidKeyException("RSAPrivateKey required. " +
 196                     "Received: " + privateKey.getClass().getName());
 197             }
 198             RSAPrivateKey rsaPrivKey = (RSAPrivateKey) privateKey;
 199             BigInteger mod = rsaPrivKey.getModulus();
 200             newSigLength = checkRSAKeyLength(mod);
 201             BigInteger pe = rsaPrivKey.getPrivateExponent();
 202             try {
 203                 if (rsaPrivKey instanceof RSAPrivateCrtKey) {
 204                     RSAPrivateCrtKey rsaPrivCrtKey = (RSAPrivateCrtKey) rsaPrivKey;
 205                     newKey = (NativeKey) keyFactory.engineGeneratePrivate
 206                         (new RSAPrivateCrtKeySpec(mod,
 207                                                   rsaPrivCrtKey.getPublicExponent(),
 208                                                   pe,
 209                                                   rsaPrivCrtKey.getPrimeP(),
 210                                                   rsaPrivCrtKey.getPrimeQ(),
 211                                                   rsaPrivCrtKey.getPrimeExponentP(),
 212                                                   rsaPrivCrtKey.getPrimeExponentQ(),
 213                                                   rsaPrivCrtKey.getCrtCoefficient()));
 214                 } else {
 215                     newKey = (NativeKey) keyFactory.engineGeneratePrivate
 216                            (new RSAPrivateKeySpec(mod, pe));
 217                 }
 218             } catch (InvalidKeySpecException ikse) {
 219                 throw new InvalidKeyException(ikse);
 220             }
 221         }
 222         init(true, newKey, newSigLength);
 223     }
 224 
 225 
 226     @Override
 227     protected synchronized void engineInitVerify(PublicKey publicKey)
 228             throws InvalidKeyException {
 229         if (publicKey == null) {
 230             throw new InvalidKeyException("Key must not be null");
 231         }
 232         NativeKey newKey = key;
 233         int newSigLength = sigLength;
 234         // Need to check RSA key length whenever a new public key is set
 235         if (publicKey != key) {
 236             if (publicKey instanceof RSAPublicKey) {
 237                 BigInteger mod = ((RSAPublicKey) publicKey).getModulus();
 238                 newSigLength = checkRSAKeyLength(mod);
 239                 try {
 240                     newKey = (NativeKey) keyFactory.engineGeneratePublic
 241                         (new RSAPublicKeySpec(mod, ((RSAPublicKey) publicKey).getPublicExponent()));
 242                 } catch (InvalidKeySpecException ikse) {
 243                     throw new InvalidKeyException(ikse);
 244                 }
 245             } else {
 246                 throw new InvalidKeyException("RSAPublicKey required. " +
 247                     "Received: " + publicKey.getClass().getName());
 248             }
 249         }
 250         init(false, newKey, newSigLength);
 251     }
 252 
 253     // deprecated but abstract
 254     @SuppressWarnings("deprecation")
 255     protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
 256         throw new UnsupportedOperationException("setParameter() not supported");
 257     }
 258 
 259     @Override
 260     protected synchronized byte[] engineSign() throws SignatureException {
 261         byte[] sig = new byte[sigLength];
 262         int rv = doFinal(sig, 0, sigLength);
 263         if (rv < 0) {
 264             throw new SignatureException(new UcryptoException(-rv));
 265         }
 266         return sig;
 267     }
 268 
 269     @Override
 270     protected synchronized int engineSign(byte[] outbuf, int offset, int len)
 271         throws SignatureException {
 272         if (outbuf == null || (offset < 0) || (outbuf.length < (offset + sigLength))
 273             || (len < sigLength)) {
 274             throw new SignatureException("Invalid output buffer. offset: " +
 275                 offset + ". len: " + len + ". sigLength: " + sigLength);
 276         }
 277         int rv = doFinal(outbuf, offset, sigLength);
 278         if (rv < 0) {
 279             throw new SignatureException(new UcryptoException(-rv));
 280         }
 281         return sigLength;
 282     }
 283 
 284     @Override
 285     protected synchronized void engineUpdate(byte b) throws SignatureException {
 286         byte[] in = { b };
 287         int rv = update(in, 0, 1);
 288         if (rv < 0) {
 289             throw new SignatureException(new UcryptoException(-rv));
 290         }
 291     }
 292 
 293     @Override
 294     protected synchronized void engineUpdate(byte[] in, int inOfs, int inLen)
 295             throws SignatureException {
 296         if (in == null || inOfs < 0 || inLen == 0) return;
 297 
 298         int rv = update(in, inOfs, inLen);
 299         if (rv < 0) {
 300             throw new SignatureException(new UcryptoException(-rv));
 301         }
 302     }
 303 
 304     @Override
 305     protected synchronized void engineUpdate(ByteBuffer in) {
 306         if (in == null || in.remaining() == 0) return;
 307 
 308         if (in instanceof DirectBuffer == false) {
 309             // cannot do better than default impl
 310             super.engineUpdate(in);
 311             return;
 312         }
 313         long inAddr = ((DirectBuffer)in).address();
 314         int inOfs = in.position();
 315         int inLen = in.remaining();
 316 
 317         int rv = update((inAddr + inOfs), inLen);
 318         if (rv < 0) {
 319             throw new UcryptoException(-rv);
 320         }
 321         in.position(inOfs + inLen);
 322     }
 323 
 324     @Override
 325     protected synchronized boolean engineVerify(byte[] sigBytes) throws SignatureException {
 326         return engineVerify(sigBytes, 0, sigBytes.length);
 327     }
 328 
 329     @Override
 330     protected synchronized boolean engineVerify(byte[] sigBytes, int sigOfs, int sigLen)
 331         throws SignatureException {
 332         if (sigBytes == null || (sigOfs < 0) || (sigBytes.length < (sigOfs + this.sigLength))
 333             || (sigLen < this.sigLength)) {
 334             throw new SignatureException("Invalid signature buffer. sigOfs: " +
 335                 sigOfs + ". sigLen: " + sigLen + ". this.sigLength: " + this.sigLength);
 336         }
 337 
 338         int rv = doFinal(sigBytes, sigOfs, sigLen);
 339         if (rv == 0) {
 340             return true;
 341         } else {
 342             UcryptoProvider.debug("Signature: " + mech + " verification error " +
 343                              new UcryptoException(-rv).getMessage());
 344             return false;
 345         }
 346     }
 347 
 348     void reset(boolean doCancel) {
 349         initialized = false;
 350         if (pCtxt != null) {
 351             pCtxt.dispose(doCancel);
 352             pCtxt = null;
 353         }
 354     }
 355 
 356     /**
 357      * calls ucrypto_sign_init(...) or ucrypto_verify_init(...)
 358      * @return pointer to the context
 359      */
 360     private native static long nativeInit(int mech, boolean sign,
 361                                           long keyValue, int keyLength);
 362 
 363     /**
 364      * calls ucrypto_sign_update(...) or ucrypto_verify_update(...)
 365      * @return an error status code (0 means SUCCESS)
 366      */
 367     private native static int nativeUpdate(long pContext, boolean sign,
 368                                            byte[] in, int inOfs, int inLen);
 369     /**
 370      * calls ucrypto_sign_update(...) or ucrypto_verify_update(...)
 371      * @return an error status code (0 means SUCCESS)
 372      */
 373     private native static int nativeUpdate(long pContext, boolean sign,
 374                                            long pIn, int inLen);
 375 
 376     /**
 377      * calls ucrypto_sign_final(...) or ucrypto_verify_final(...)
 378      * @return the length of signature bytes or verification status.
 379      * If negative, it indicates an error status code
 380      */
 381     private native static int nativeFinal(long pContext, boolean sign,
 382                                           byte[] sig, int sigOfs, int sigLen);
 383 
 384     // actual init() implementation - caller should clone key if needed
 385     private void init(boolean sign, NativeKey key, int sigLength) {
 386         reset(true);
 387         this.sign = sign;
 388         this.sigLength = sigLength;
 389         this.key = key;
 390         long pCtxtVal = nativeInit(mech.value(), sign, key.value(),
 391                                    key.length());
 392         initialized = (pCtxtVal != 0L);
 393         if (initialized) {
 394             pCtxt = new SignatureContextRef(this, pCtxtVal, sign);
 395         } else {
 396             throw new UcryptoException("Cannot initialize Signature");
 397         }
 398     }
 399 
 400     private void ensureInitialized() {
 401         if (!initialized) {
 402             init(sign, key, sigLength);
 403             if (!initialized) {
 404                 throw new UcryptoException("Cannot initialize Signature");
 405             }
 406         }
 407     }
 408 
 409     // returns 0 (success) or negative (ucrypto error occurred)
 410     private int update(byte[] in, int inOfs, int inLen) {
 411         if (inOfs < 0 || inOfs + inLen > in.length) {
 412             throw new ArrayIndexOutOfBoundsException("inOfs :" + inOfs +
 413                 ". inLen: " + inLen + ". in.length: " + in.length);
 414         }
 415         ensureInitialized();
 416         int k = nativeUpdate(pCtxt.id, sign, in, inOfs, inLen);
 417         if (k < 0) {
 418             reset(false);
 419         }
 420         return k;
 421     }
 422 
 423     // returns 0 (success) or negative (ucrypto error occurred)
 424     private int update(long pIn, int inLen) {
 425         ensureInitialized();
 426         int k = nativeUpdate(pCtxt.id, sign, pIn, inLen);
 427         if (k < 0) {
 428             reset(false);
 429         }
 430         return k;
 431     }
 432 
 433     // returns 0 (success) or negative (ucrypto error occurred)
 434     private int doFinal(byte[] sigBytes, int sigOfs, int sigLen) {
 435         try {
 436             ensureInitialized();
 437             int k = nativeFinal(pCtxt.id, sign, sigBytes, sigOfs, sigLen);
 438             return k;
 439         } finally {
 440             reset(false);
 441         }
 442     }
 443 
 444     // check and return RSA key size in number of bytes
 445     private int checkRSAKeyLength(BigInteger mod) throws InvalidKeyException {
 446         int keySize = (mod.bitLength() + 7) >> 3;
 447         int maxDataSize = keySize - PKCS1PADDING_LEN;
 448         if (maxDataSize < encodedLen) {
 449             throw new InvalidKeyException
 450                 ("Key is too short for this signature algorithm. maxDataSize: " +
 451                     maxDataSize + ". encodedLen: " + encodedLen);
 452         }
 453         return keySize;
 454     }
 455 }