1 /*
   2  * Copyright (c) 1999, 2016, 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 javax.crypto;
  27 
  28 import java.security.AlgorithmParameters;
  29 import java.security.Provider;
  30 import java.security.Key;
  31 import java.security.Security;
  32 import java.security.NoSuchAlgorithmException;
  33 import java.security.NoSuchProviderException;
  34 import java.security.InvalidKeyException;
  35 import java.security.InvalidAlgorithmParameterException;
  36 import java.security.spec.AlgorithmParameterSpec;
  37 import java.util.Objects;
  38 
  39 import sun.security.jca.GetInstance.Instance;
  40 
  41 /**
  42  * This class provides the functionality of an exemption mechanism, examples
  43  * of which are <i>key recovery</i>, <i>key weakening</i>, and
  44  * <i>key escrow</i>.
  45  *
  46  * <p>Applications or applets that use an exemption mechanism may be granted
  47  * stronger encryption capabilities than those which don't.
  48  *
  49  * @since 1.4
  50  */
  51 
  52 public class ExemptionMechanism {
  53 
  54     // The provider
  55     private Provider provider;
  56 
  57     // The provider implementation (delegate)
  58     private ExemptionMechanismSpi exmechSpi;
  59 
  60     // The name of the exemption mechanism.
  61     private String mechanism;
  62 
  63     // Flag which indicates whether this ExemptionMechanism
  64     // result is generated successfully.
  65     private boolean done = false;
  66 
  67     // State information
  68     private boolean initialized = false;
  69 
  70     // Store away the key at init() time for later comparison.
  71     private Key keyStored = null;
  72 
  73     /**
  74      * Creates a ExemptionMechanism object.
  75      *
  76      * @param exmechSpi the delegate
  77      * @param provider the provider
  78      * @param mechanism the exemption mechanism
  79      */
  80     protected ExemptionMechanism(ExemptionMechanismSpi exmechSpi,
  81                                  Provider provider,
  82                                  String mechanism) {
  83         this.exmechSpi = exmechSpi;
  84         this.provider = provider;
  85         this.mechanism = mechanism;
  86     }
  87 
  88     /**
  89      * Returns the exemption mechanism name of this
  90      * <code>ExemptionMechanism</code> object.
  91      *
  92      * <p>This is the same name that was specified in one of the
  93      * <code>getInstance</code> calls that created this
  94      * <code>ExemptionMechanism</code> object.
  95      *
  96      * @return the exemption mechanism name of this
  97      * <code>ExemptionMechanism</code> object.
  98      */
  99     public final String getName() {
 100         return this.mechanism;
 101     }
 102 
 103     /**
 104      * Returns an <code>ExemptionMechanism</code> object that implements the
 105      * specified exemption mechanism algorithm.
 106      *
 107      * <p> This method traverses the list of registered security Providers,
 108      * starting with the most preferred Provider.
 109      * A new ExemptionMechanism object encapsulating the
 110      * ExemptionMechanismSpi implementation from the first
 111      * Provider that supports the specified algorithm is returned.
 112      *
 113      * <p> Note that the list of registered providers may be retrieved via
 114      * the {@link Security#getProviders() Security.getProviders()} method.
 115      *
 116      * @implNote
 117      * The JDK Reference Implementation additionally uses the
 118      * {@code jdk.security.provider.preferred}
 119      * {@link Security#getProperty(String) Security} property to determine
 120      * the preferred provider order for the specified algorithm. This
 121      * may be different than the order of providers returned by
 122      * {@link Security#getProviders() Security.getProviders()}.
 123      *
 124      * @param algorithm the standard name of the requested exemption
 125      * mechanism.
 126      * See the ExemptionMechanism section in the
 127      * <a href=
 128      *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
 129      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
 130      * for information about standard exemption mechanism names.
 131      *
 132      * @return the new {@code ExemptionMechanism} object
 133      *
 134      * @throws NoSuchAlgorithmException if no {@code Provider} supports an
 135      *         {@code ExemptionMechanismSpi} implementation for the
 136      *         specified algorithm
 137      *
 138      * @throws NullPointerException if {@code algorithm} is {@code null}
 139      *
 140      * @see java.security.Provider
 141      */
 142     public static final ExemptionMechanism getInstance(String algorithm)
 143             throws NoSuchAlgorithmException {
 144         Objects.requireNonNull(algorithm, "null algorithm name");
 145         Instance instance = JceSecurity.getInstance("ExemptionMechanism",
 146                 ExemptionMechanismSpi.class, algorithm);
 147         return new ExemptionMechanism((ExemptionMechanismSpi)instance.impl,
 148                 instance.provider, algorithm);
 149     }
 150 
 151 
 152     /**
 153      * Returns an <code>ExemptionMechanism</code> object that implements the
 154      * specified exemption mechanism algorithm.
 155      *
 156      * <p> A new ExemptionMechanism object encapsulating the
 157      * ExemptionMechanismSpi implementation from the specified provider
 158      * is returned.  The specified provider must be registered
 159      * in the security provider list.
 160      *
 161      * <p> Note that the list of registered providers may be retrieved via
 162      * the {@link Security#getProviders() Security.getProviders()} method.
 163 
 164      * @param algorithm the standard name of the requested exemption mechanism.
 165      * See the ExemptionMechanism section in the
 166      * <a href=
 167      *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
 168      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
 169      * for information about standard exemption mechanism names.
 170      *
 171      * @param provider the name of the provider.
 172      *
 173      * @return the new {@code ExemptionMechanism} object
 174      *
 175      * @throws IllegalArgumentException if the {@code provider}
 176      *         is {@code null} or empty
 177      *
 178      * @throws NoSuchAlgorithmException if an {@code ExemptionMechanismSpi}
 179      *         implementation for the specified algorithm is not
 180      *         available from the specified provider
 181      *
 182      * @throws NoSuchProviderException if the specified provider is not
 183      *         registered in the security provider list
 184      *
 185      * @throws NullPointerException if {@code algorithm} is {@code null}
 186      *
 187      * @see java.security.Provider
 188      */
 189     public static final ExemptionMechanism getInstance(String algorithm,
 190             String provider) throws NoSuchAlgorithmException,
 191             NoSuchProviderException {
 192         Objects.requireNonNull(algorithm, "null algorithm name");
 193         Instance instance = JceSecurity.getInstance("ExemptionMechanism",
 194                 ExemptionMechanismSpi.class, algorithm, provider);
 195         return new ExemptionMechanism((ExemptionMechanismSpi)instance.impl,
 196                 instance.provider, algorithm);
 197     }
 198 
 199     /**
 200      * Returns an <code>ExemptionMechanism</code> object that implements the
 201      * specified exemption mechanism algorithm.
 202      *
 203      * <p> A new ExemptionMechanism object encapsulating the
 204      * ExemptionMechanismSpi implementation from the specified Provider
 205      * object is returned.  Note that the specified Provider object
 206      * does not have to be registered in the provider list.
 207      *
 208      * @param algorithm the standard name of the requested exemption mechanism.
 209      * See the ExemptionMechanism section in the
 210      * <a href=
 211      *   "{@docRoot}/../technotes/guides/security/StandardNames.html#Exemption">
 212      * Java Cryptography Architecture Standard Algorithm Name Documentation</a>
 213      * for information about standard exemption mechanism names.
 214      *
 215      * @param provider the provider.
 216      *
 217      * @return the new {@code ExemptionMechanism} object
 218      *
 219      * @throws IllegalArgumentException if the {@code provider}
 220      *         is null
 221      *
 222      * @throws NoSuchAlgorithmException if an {@code ExemptionMechanismSpi}
 223      *         implementation for the specified algorithm is not available
 224      *         from the specified {@code Provider object}
 225      *
 226      * @exception NullPointerException if {@code algorithm} is {@code null}
 227      *
 228      * @see java.security.Provider
 229      */
 230     public static final ExemptionMechanism getInstance(String algorithm,
 231             Provider provider) throws NoSuchAlgorithmException {
 232         Objects.requireNonNull(algorithm, "null algorithm name");
 233         Instance instance = JceSecurity.getInstance("ExemptionMechanism",
 234                 ExemptionMechanismSpi.class, algorithm, provider);
 235         return new ExemptionMechanism((ExemptionMechanismSpi)instance.impl,
 236                 instance.provider, algorithm);
 237     }
 238 
 239     /**
 240      * Returns the provider of this <code>ExemptionMechanism</code> object.
 241      *
 242      * @return the provider of this <code>ExemptionMechanism</code> object.
 243      */
 244     public final Provider getProvider() {
 245         return this.provider;
 246     }
 247 
 248     /**
 249      * Returns whether the result blob has been generated successfully by this
 250      * exemption mechanism.
 251      *
 252      * <p>The method also makes sure that the key passed in is the same as
 253      * the one this exemption mechanism used in initializing and generating
 254      * phases.
 255      *
 256      * @param key the key the crypto is going to use.
 257      *
 258      * @return whether the result blob of the same key has been generated
 259      * successfully by this exemption mechanism; false if <code>key</code>
 260      * is null.
 261      *
 262      * @exception ExemptionMechanismException if problem(s) encountered
 263      * while determining whether the result blob has been generated successfully
 264      * by this exemption mechanism object.
 265      */
 266     public final boolean isCryptoAllowed(Key key)
 267             throws ExemptionMechanismException {
 268         boolean ret = false;
 269         if (done && (key != null)) {
 270             // Check if the key passed in is the same as the one
 271             // this exemption mechanism used.
 272             ret = keyStored.equals(key);
 273         }
 274         return ret;
 275      }
 276 
 277     /**
 278      * Returns the length in bytes that an output buffer would need to be in
 279      * order to hold the result of the next
 280      * {@link #genExemptionBlob(byte[]) genExemptionBlob}
 281      * operation, given the input length <code>inputLen</code> (in bytes).
 282      *
 283      * <p>The actual output length of the next
 284      * {@link #genExemptionBlob(byte[]) genExemptionBlob}
 285      * call may be smaller than the length returned by this method.
 286      *
 287      * @param inputLen the input length (in bytes)
 288      *
 289      * @return the required output buffer size (in bytes)
 290      *
 291      * @exception IllegalStateException if this exemption mechanism is in a
 292      * wrong state (e.g., has not yet been initialized)
 293      */
 294     public final int getOutputSize(int inputLen) throws IllegalStateException {
 295         if (!initialized) {
 296             throw new IllegalStateException(
 297                 "ExemptionMechanism not initialized");
 298         }
 299         if (inputLen < 0) {
 300             throw new IllegalArgumentException(
 301                 "Input size must be equal to " + "or greater than zero");
 302         }
 303         return exmechSpi.engineGetOutputSize(inputLen);
 304     }
 305 
 306     /**
 307      * Initializes this exemption mechanism with a key.
 308      *
 309      * <p>If this exemption mechanism requires any algorithm parameters
 310      * that cannot be derived from the given <code>key</code>, the
 311      * underlying exemption mechanism implementation is supposed to
 312      * generate the required parameters itself (using provider-specific
 313      * default values); in the case that algorithm parameters must be
 314      * specified by the caller, an <code>InvalidKeyException</code> is raised.
 315      *
 316      * @param key the key for this exemption mechanism
 317      *
 318      * @exception InvalidKeyException if the given key is inappropriate for
 319      * this exemption mechanism.
 320      * @exception ExemptionMechanismException if problem(s) encountered in the
 321      * process of initializing.
 322      */
 323     public final void init(Key key)
 324             throws InvalidKeyException, ExemptionMechanismException {
 325         done = false;
 326         initialized = false;
 327 
 328         keyStored = key;
 329         exmechSpi.engineInit(key);
 330         initialized = true;
 331     }
 332 
 333     /**
 334      * Initializes this exemption mechanism with a key and a set of algorithm
 335      * parameters.
 336      *
 337      * <p>If this exemption mechanism requires any algorithm parameters
 338      * and <code>params</code> is null, the underlying exemption
 339      * mechanism implementation is supposed to generate the required
 340      * parameters itself (using provider-specific default values); in the case
 341      * that algorithm parameters must be specified by the caller, an
 342      * <code>InvalidAlgorithmParameterException</code> is raised.
 343      *
 344      * @param key the key for this exemption mechanism
 345      * @param params the algorithm parameters
 346      *
 347      * @exception InvalidKeyException if the given key is inappropriate for
 348      * this exemption mechanism.
 349      * @exception InvalidAlgorithmParameterException if the given algorithm
 350      * parameters are inappropriate for this exemption mechanism.
 351      * @exception ExemptionMechanismException if problem(s) encountered in the
 352      * process of initializing.
 353      */
 354     public final void init(Key key, AlgorithmParameterSpec params)
 355             throws InvalidKeyException, InvalidAlgorithmParameterException,
 356             ExemptionMechanismException {
 357         done = false;
 358         initialized = false;
 359 
 360         keyStored = key;
 361         exmechSpi.engineInit(key, params);
 362         initialized = true;
 363     }
 364 
 365     /**
 366      * Initializes this exemption mechanism with a key and a set of algorithm
 367      * parameters.
 368      *
 369      * <p>If this exemption mechanism requires any algorithm parameters
 370      * and <code>params</code> is null, the underlying exemption mechanism
 371      * implementation is supposed to generate the required parameters itself
 372      * (using provider-specific default values); in the case that algorithm
 373      * parameters must be specified by the caller, an
 374      * <code>InvalidAlgorithmParameterException</code> is raised.
 375      *
 376      * @param key the key for this exemption mechanism
 377      * @param params the algorithm parameters
 378      *
 379      * @exception InvalidKeyException if the given key is inappropriate for
 380      * this exemption mechanism.
 381      * @exception InvalidAlgorithmParameterException if the given algorithm
 382      * parameters are inappropriate for this exemption mechanism.
 383      * @exception ExemptionMechanismException if problem(s) encountered in the
 384      * process of initializing.
 385      */
 386     public final void init(Key key, AlgorithmParameters params)
 387             throws InvalidKeyException, InvalidAlgorithmParameterException,
 388             ExemptionMechanismException {
 389         done = false;
 390         initialized = false;
 391 
 392         keyStored = key;
 393         exmechSpi.engineInit(key, params);
 394         initialized = true;
 395     }
 396 
 397     /**
 398      * Generates the exemption mechanism key blob.
 399      *
 400      * @return the new buffer with the result key blob.
 401      *
 402      * @exception IllegalStateException if this exemption mechanism is in
 403      * a wrong state (e.g., has not been initialized).
 404      * @exception ExemptionMechanismException if problem(s) encountered in the
 405      * process of generating.
 406      */
 407     public final byte[] genExemptionBlob() throws IllegalStateException,
 408             ExemptionMechanismException {
 409         if (!initialized) {
 410             throw new IllegalStateException(
 411                 "ExemptionMechanism not initialized");
 412         }
 413         byte[] blob = exmechSpi.engineGenExemptionBlob();
 414         done = true;
 415         return blob;
 416     }
 417 
 418     /**
 419      * Generates the exemption mechanism key blob, and stores the result in
 420      * the <code>output</code> buffer.
 421      *
 422      * <p>If the <code>output</code> buffer is too small to hold the result,
 423      * a <code>ShortBufferException</code> is thrown. In this case, repeat this
 424      * call with a larger output buffer. Use
 425      * {@link #getOutputSize(int) getOutputSize} to determine how big
 426      * the output buffer should be.
 427      *
 428      * @param output the buffer for the result
 429      *
 430      * @return the number of bytes stored in <code>output</code>
 431      *
 432      * @exception IllegalStateException if this exemption mechanism is in
 433      * a wrong state (e.g., has not been initialized).
 434      * @exception ShortBufferException if the given output buffer is too small
 435      * to hold the result.
 436      * @exception ExemptionMechanismException if problem(s) encountered in the
 437      * process of generating.
 438      */
 439     public final int genExemptionBlob(byte[] output)
 440             throws IllegalStateException, ShortBufferException,
 441             ExemptionMechanismException {
 442         if (!initialized) {
 443             throw new IllegalStateException
 444             ("ExemptionMechanism not initialized");
 445         }
 446         int n = exmechSpi.engineGenExemptionBlob(output, 0);
 447         done = true;
 448         return n;
 449     }
 450 
 451     /**
 452      * Generates the exemption mechanism key blob, and stores the result in
 453      * the <code>output</code> buffer, starting at <code>outputOffset</code>
 454      * inclusive.
 455      *
 456      * <p>If the <code>output</code> buffer is too small to hold the result,
 457      * a <code>ShortBufferException</code> is thrown. In this case, repeat this
 458      * call with a larger output buffer. Use
 459      * {@link #getOutputSize(int) getOutputSize} to determine how big
 460      * the output buffer should be.
 461      *
 462      * @param output the buffer for the result
 463      * @param outputOffset the offset in <code>output</code> where the result
 464      * is stored
 465      *
 466      * @return the number of bytes stored in <code>output</code>
 467      *
 468      * @exception IllegalStateException if this exemption mechanism is in
 469      * a wrong state (e.g., has not been initialized).
 470      * @exception ShortBufferException if the given output buffer is too small
 471      * to hold the result.
 472      * @exception ExemptionMechanismException if problem(s) encountered in the
 473      * process of generating.
 474      */
 475     public final int genExemptionBlob(byte[] output, int outputOffset)
 476             throws IllegalStateException, ShortBufferException,
 477             ExemptionMechanismException {
 478         if (!initialized) {
 479             throw new IllegalStateException
 480             ("ExemptionMechanism not initialized");
 481         }
 482         int n = exmechSpi.engineGenExemptionBlob(output, outputOffset);
 483         done = true;
 484         return n;
 485     }
 486 }