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