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.RSAPrivateCrtKeySpec;
  47 import java.security.spec.RSAPublicKeySpec;
  48 import java.security.spec.InvalidKeySpecException;
  49 import sun.nio.ch.DirectBuffer;
  50 import java.nio.ByteBuffer;
  51 
  52 /**
  53  * Signature implementation class. This class currently supports the
  54  * following algorithms:
  55  *
  56  * . RSA:
  57  *   . MD5withRSA
  58  *   . SHA1withRSA
  59  *   . SHA256withRSA
  60  *   . SHA384withRSA
  61  *   . SHA512withRSA
  62  *
  63  * @since 1.9
  64  */
  65 class NativeRSASignature extends SignatureSpi {
  66 
  67     private static final int PKCS1PADDING_LEN = 11;
  68 
  69     // fields set in constructor
  70     private final UcryptoMech mech;
  71     private final int encodedLen;
  72 
  73     // field for ensuring native memory is freed
  74     private SignatureContextRef pCtxt = null;
  75 
  76     //
  77     // fields (re)set in every init()
  78     //
  79     private boolean initialized = false;
  80     private boolean sign = true;
  81     private int sigLength;
  82     private NativeKey key;
  83     private NativeRSAKeyFactory keyFactory; // may need a more generic type later
  84 
  85     // public implementation classes
  86     public static final class MD5 extends NativeRSASignature {
  87         public MD5() throws NoSuchAlgorithmException {
  88             super(UcryptoMech.CRYPTO_MD5_RSA_PKCS, 34);
  89         }
  90     }
  91 
  92     public static final class SHA1 extends NativeRSASignature {
  93         public SHA1() throws NoSuchAlgorithmException {
  94             super(UcryptoMech.CRYPTO_SHA1_RSA_PKCS, 35);
  95         }
  96     }
  97 
  98     public static final class SHA256 extends NativeRSASignature {
  99         public SHA256() throws NoSuchAlgorithmException {
 100             super(UcryptoMech.CRYPTO_SHA256_RSA_PKCS, 51);
 101         }
 102     }
 103 
 104     public static final class SHA384 extends NativeRSASignature {
 105         public SHA384() throws NoSuchAlgorithmException {
 106             super(UcryptoMech.CRYPTO_SHA384_RSA_PKCS, 67);
 107         }
 108     }
 109 
 110     public static final class SHA512 extends NativeRSASignature {
 111         public SHA512() throws NoSuchAlgorithmException {
 112             super(UcryptoMech.CRYPTO_SHA512_RSA_PKCS, 83);
 113         }
 114     }
 115 
 116     // internal class for native resource cleanup
 117     private static class SignatureContextRef extends PhantomReference<NativeRSASignature>
 118         implements Comparable<SignatureContextRef> {
 119 
 120         private static ReferenceQueue<NativeRSASignature> refQueue =
 121             new ReferenceQueue<NativeRSASignature>();
 122 
 123         // Needed to keep these references from being GC'ed until when their
 124         // referents are GC'ed so we can do post-mortem processing
 125         private static Set<SignatureContextRef> refList =
 126             new ConcurrentSkipListSet<SignatureContextRef>();
 127         //           Collections.synchronizedSortedSet(new TreeSet<SignatureContextRef>());
 128 
 129         private final long id;
 130         private final boolean sign;
 131 
 132         private static void drainRefQueueBounded() {
 133             while (true) {
 134                 SignatureContextRef next = (SignatureContextRef) refQueue.poll();
 135                 if (next == null) break;
 136                 next.dispose(true);
 137             }
 138         }
 139 
 140         SignatureContextRef(NativeRSASignature ns, long id, boolean sign) {
 141             super(ns, refQueue);
 142             this.id = id;
 143             this.sign = sign;
 144             refList.add(this);
 145             UcryptoProvider.debug("Resource: track Signature Ctxt " + this.id);
 146             drainRefQueueBounded();
 147         }
 148 
 149         public int compareTo(SignatureContextRef other) {
 150             if (this.id == other.id) {
 151                 return 0;
 152             } else {
 153                 return (this.id < other.id) ? -1 : 1;
 154             }
 155         }
 156 
 157         void dispose(boolean doCancel) {
 158             refList.remove(this);
 159             try {
 160                 if (doCancel) {
 161                     UcryptoProvider.debug("Resource: free Signature Ctxt " + this.id);
 162                     NativeRSASignature.nativeFinal(id, sign, null, 0, 0);
 163                 } else {
 164                     UcryptoProvider.debug("Resource: stop tracking Signature Ctxt " + this.id);
 165                 }
 166             } finally {
 167                 this.clear();
 168             }
 169         }
 170     }
 171 
 172     NativeRSASignature(UcryptoMech mech, int encodedLen)
 173         throws NoSuchAlgorithmException {
 174         this.mech = mech;
 175         this.encodedLen = encodedLen;
 176         this.keyFactory = new NativeRSAKeyFactory();
 177     }
 178 
 179     // deprecated but abstract
 180     @SuppressWarnings("deprecation")
 181     protected Object engineGetParameter(String param) throws InvalidParameterException {
 182         throw new UnsupportedOperationException("getParameter() not supported");
 183     }
 184 
 185     @Override
 186     protected synchronized void engineInitSign(PrivateKey privateKey)
 187             throws InvalidKeyException {
 188         if (privateKey == null) {
 189             throw new InvalidKeyException("Key must not be null");
 190         }
 191         NativeKey newKey = key;
 192         int newSigLength = sigLength;
 193         // Need to check RSA key length whenever a new private key is set
 194         if (privateKey != key) {
 195             if (privateKey instanceof RSAPrivateCrtKey) {
 196                 RSAPrivateCrtKey rsaPrivKey = (RSAPrivateCrtKey) privateKey;
 197                 BigInteger mod = rsaPrivKey.getModulus();
 198                 newSigLength = checkRSAKeyLength(mod);
 199                 try {
 200                     newKey = (NativeKey) keyFactory.engineGeneratePrivate
 201                         (new RSAPrivateCrtKeySpec(mod,
 202                                                   rsaPrivKey.getPublicExponent(),
 203                                                   rsaPrivKey.getPrivateExponent(),
 204                                                   rsaPrivKey.getPrimeP(),
 205                                                   rsaPrivKey.getPrimeQ(),
 206                                                   rsaPrivKey.getPrimeExponentP(),
 207                                                   rsaPrivKey.getPrimeExponentQ(),
 208                                                   rsaPrivKey.getCrtCoefficient()));
 209                 } catch (InvalidKeySpecException ikse) {
 210                     throw new InvalidKeyException(ikse);
 211                 }
 212             } else {
 213                 throw new InvalidKeyException("RSAPrivateCrtKey required");
 214             }
 215         }
 216         init(true, newKey, newSigLength);
 217     }
 218 
 219 
 220     @Override
 221     protected synchronized void engineInitVerify(PublicKey publicKey)
 222             throws InvalidKeyException {
 223         if (publicKey == null) {
 224             throw new InvalidKeyException("Key must not be null");
 225         }
 226         NativeKey newKey = key;
 227         int newSigLength = sigLength;
 228         // Need to check RSA key length whenever a new public key is set
 229         if (publicKey != key) {
 230             if (publicKey instanceof RSAPublicKey) {
 231                 BigInteger mod = ((RSAPublicKey) publicKey).getModulus();
 232                 newSigLength = checkRSAKeyLength(mod);
 233                 try {
 234                     newKey = (NativeKey) keyFactory.engineGeneratePublic
 235                         (new RSAPublicKeySpec(mod, ((RSAPublicKey) publicKey).getPublicExponent()));
 236                 } catch (InvalidKeySpecException ikse) {
 237                     throw new InvalidKeyException(ikse);
 238                 }
 239             } else {
 240                 throw new InvalidKeyException("RSAPublicKey required");
 241             }
 242         }
 243         init(false, newKey, newSigLength);
 244     }
 245 
 246     // deprecated but abstract
 247     @SuppressWarnings("deprecation")
 248     protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
 249         throw new UnsupportedOperationException("setParameter() not supported");
 250     }
 251 
 252     @Override
 253     protected synchronized byte[] engineSign() throws SignatureException {
 254         byte[] sig = new byte[sigLength];
 255         int rv = doFinal(sig, 0, sigLength);
 256         if (rv < 0) {
 257             throw new SignatureException(new UcryptoException(-rv));
 258         }
 259         return sig;
 260     }
 261 
 262     @Override
 263     protected synchronized int engineSign(byte[] outbuf, int offset, int len)
 264         throws SignatureException {
 265         if (outbuf == null || (offset < 0) || (outbuf.length < (offset + sigLength))
 266             || (len < sigLength)) {
 267             throw new SignatureException("Invalid output buffer");
 268         }
 269         int rv = doFinal(outbuf, offset, sigLength);
 270         if (rv < 0) {
 271             throw new SignatureException(new UcryptoException(-rv));
 272         }
 273         return sigLength;
 274     }
 275 
 276     @Override
 277     protected synchronized void engineUpdate(byte b) throws SignatureException {
 278         byte[] in = { b };
 279         int rv = update(in, 0, 1);
 280         if (rv < 0) {
 281             throw new SignatureException(new UcryptoException(-rv));
 282         }
 283     }
 284 
 285     @Override
 286     protected synchronized void engineUpdate(byte[] in, int inOfs, int inLen)
 287             throws SignatureException {
 288         if (in == null || inOfs < 0 || inLen == 0) return;
 289 
 290         int rv = update(in, inOfs, inLen);
 291         if (rv < 0) {
 292             throw new SignatureException(new UcryptoException(-rv));
 293         }
 294     }
 295 
 296     @Override
 297     protected synchronized void engineUpdate(ByteBuffer in) {
 298         if (in == null || in.remaining() == 0) return;
 299 
 300         if (in instanceof DirectBuffer == false) {
 301             // cannot do better than default impl
 302             super.engineUpdate(in);
 303             return;
 304         }
 305         long inAddr = ((DirectBuffer)in).address();
 306         int inOfs = in.position();
 307         int inLen = in.remaining();
 308 
 309         int rv = update((inAddr + inOfs), inLen);
 310         if (rv < 0) {
 311             throw new UcryptoException(-rv);
 312         }
 313         in.position(inOfs + inLen);
 314     }
 315 
 316     @Override
 317     protected synchronized boolean engineVerify(byte[] sigBytes) throws SignatureException {
 318         return engineVerify(sigBytes, 0, sigBytes.length);
 319     }
 320 
 321     @Override
 322     protected synchronized boolean engineVerify(byte[] sigBytes, int sigOfs, int sigLen)
 323         throws SignatureException {
 324         if (sigBytes == null || (sigOfs < 0) || (sigBytes.length < (sigOfs + this.sigLength))
 325             || (sigLen < this.sigLength)) {
 326             throw new SignatureException("Invalid signature buffer");
 327         }
 328 
 329         int rv = doFinal(sigBytes, sigOfs, sigLen);
 330         if (rv == 0) {
 331             return true;
 332         } else {
 333             UcryptoProvider.debug("Signature: " + mech + " verification error " +
 334                              new UcryptoException(-rv).getMessage());
 335             return false;
 336         }
 337     }
 338 
 339     void reset(boolean doCancel) {
 340         initialized = false;
 341         if (pCtxt != null) {
 342             pCtxt.dispose(doCancel);
 343             pCtxt = null;
 344         }
 345     }
 346 
 347     /**
 348      * calls ucrypto_sign_init(...) or ucrypto_verify_init(...)
 349      * @return pointer to the context
 350      */
 351     private native static long nativeInit(int mech, boolean sign,
 352                                           long keyValue, int keyLength);
 353 
 354     /**
 355      * calls ucrypto_sign_update(...) or ucrypto_verify_update(...)
 356      * @returns an error status code (0 means SUCCESS)
 357      */
 358     private native static int nativeUpdate(long pContext, boolean sign,
 359                                            byte[] in, int inOfs, int inLen);
 360     /**
 361      * calls ucrypto_sign_update(...) or ucrypto_verify_update(...)
 362      * @returns an error status code (0 means SUCCESS)
 363      */
 364     private native static int nativeUpdate(long pContext, boolean sign,
 365                                            long pIn, int inLen);
 366 
 367     /**
 368      * calls ucrypto_sign_final(...) or ucrypto_verify_final(...)
 369      * @returns the length of signature bytes or verification status.
 370      * If negative, it indicates an error status code
 371      */
 372     private native static int nativeFinal(long pContext, boolean sign,
 373                                           byte[] sig, int sigOfs, int sigLen);
 374 
 375     // actual init() implementation - caller should clone key if needed
 376     private void init(boolean sign, NativeKey key, int sigLength) {
 377         reset(true);
 378         this.sign = sign;
 379         this.sigLength = sigLength;
 380         this.key = key;
 381         long pCtxtVal = nativeInit(mech.value(), sign, key.value(),
 382                                    key.length());
 383         initialized = (pCtxtVal != 0L);
 384         if (initialized) {
 385             pCtxt = new SignatureContextRef(this, pCtxtVal, sign);
 386         } else {
 387             throw new UcryptoException("Cannot initialize Signature");
 388         }
 389     }
 390 
 391     private void ensureInitialized() {
 392         if (!initialized) {
 393             init(sign, key, sigLength);
 394             if (!initialized) {
 395                 throw new UcryptoException("Cannot initialize Signature");
 396             }
 397         }
 398     }
 399 
 400     // returns 0 (success) or negative (ucrypto error occurred)
 401     private int update(byte[] in, int inOfs, int inLen) {
 402         if (inOfs < 0 || inOfs + inLen > in.length) {
 403             throw new ArrayIndexOutOfBoundsException();
 404         }
 405         ensureInitialized();
 406         int k = nativeUpdate(pCtxt.id, sign, in, inOfs, inLen);
 407         if (k < 0) {
 408             reset(false);
 409         }
 410         return k;
 411     }
 412 
 413     // returns 0 (success) or negative (ucrypto error occurred)
 414     private int update(long pIn, int inLen) {
 415         ensureInitialized();
 416         int k = nativeUpdate(pCtxt.id, sign, pIn, 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 doFinal(byte[] sigBytes, int sigOfs, int sigLen) {
 425         try {
 426             ensureInitialized();
 427             int k = nativeFinal(pCtxt.id, sign, sigBytes, sigOfs, sigLen);
 428             return k;
 429         } finally {
 430             reset(false);
 431         }
 432     }
 433 
 434     // check and return RSA key size in number of bytes
 435     private int checkRSAKeyLength(BigInteger mod) throws InvalidKeyException {
 436         int keySize = (mod.bitLength() + 7) >> 3;
 437         int maxDataSize = keySize - PKCS1PADDING_LEN;
 438         if (maxDataSize < encodedLen) {
 439             throw new InvalidKeyException
 440                 ("Key is too short for this signature algorithm");
 441         }
 442         return keySize;
 443     }
 444 }