1 /*
   2  * Copyright (c) 2004, 2017, 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.sun.crypto.provider;
  27 
  28 import java.util.Arrays;
  29 import java.security.*;
  30 import java.security.spec.*;
  31 import javax.crypto.*;
  32 import javax.crypto.spec.*;
  33 
  34 /**
  35  * This class implements the AES KeyWrap algorithm as defined
  36  * in <a href=http://www.w3.org/TR/xmlenc-core/#sec-Alg-SymmetricKeyWrap>
  37  * "XML Encryption Syntax and Processing" section 5.6.3 "AES Key Wrap".
  38  * Note: only <code>ECB</code> mode and <code>NoPadding</code> padding
  39  * can be used for this algorithm.
  40  *
  41  * @author Valerie Peng
  42  *
  43  *
  44  * @see AESCipher
  45  */
  46 abstract class AESWrapCipher extends CipherSpi {
  47     public static final class General extends AESWrapCipher {
  48         public General() {
  49             super(-1);
  50         }
  51     }
  52     public static final class AES128 extends AESWrapCipher {
  53         public AES128() {
  54             super(16);
  55         }
  56     }
  57     public static final class AES192 extends AESWrapCipher {
  58         public AES192() {
  59             super(24);
  60         }
  61     }
  62     public static final class AES256 extends AESWrapCipher {
  63         public AES256() {
  64             super(32);
  65         }
  66     }
  67     private static final byte[] IV = {
  68         (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6,
  69         (byte) 0xA6, (byte) 0xA6, (byte) 0xA6, (byte) 0xA6
  70     };
  71 
  72     private static final int blksize = AESConstants.AES_BLOCK_SIZE;
  73 
  74     /*
  75      * internal cipher object which does the real work.
  76      */
  77     private AESCrypt cipher;
  78 
  79     /*
  80      * are we encrypting or decrypting?
  81      */
  82     private boolean decrypting = false;
  83 
  84     /*
  85      * needed to support AES oids which associates a fixed key size
  86      * to the cipher object.
  87      */
  88     private final int fixedKeySize; // in bytes, -1 if no restriction
  89 
  90     /**
  91      * Creates an instance of AES KeyWrap cipher with default
  92      * mode, i.e. "ECB" and padding scheme, i.e. "NoPadding".
  93      */
  94     public AESWrapCipher(int keySize) {
  95         cipher = new AESCrypt();
  96         fixedKeySize = keySize;
  97 
  98     }
  99 
 100     /**
 101      * Sets the mode of this cipher. Only "ECB" mode is accepted for this
 102      * cipher.
 103      *
 104      * @param mode the cipher mode
 105      *
 106      * @exception NoSuchAlgorithmException if the requested cipher mode
 107      * is not "ECB".
 108      */
 109     protected void engineSetMode(String mode)
 110         throws NoSuchAlgorithmException {
 111         if (!mode.equalsIgnoreCase("ECB")) {
 112             throw new NoSuchAlgorithmException(mode + " cannot be used");
 113         }
 114     }
 115 
 116     /**
 117      * Sets the padding mechanism of this cipher. Only "NoPadding" schmem
 118      * is accepted for this cipher.
 119      *
 120      * @param padding the padding mechanism
 121      *
 122      * @exception NoSuchPaddingException if the requested padding mechanism
 123      * is not "NoPadding".
 124      */
 125     protected void engineSetPadding(String padding)
 126         throws NoSuchPaddingException {
 127         if (!padding.equalsIgnoreCase("NoPadding")) {
 128             throw new NoSuchPaddingException(padding + " cannot be used");
 129         }
 130     }
 131 
 132     /**
 133      * Returns the block size (in bytes). i.e. 16 bytes.
 134      *
 135      * @return the block size (in bytes), i.e. 16 bytes.
 136      */
 137     protected int engineGetBlockSize() {
 138         return blksize;
 139     }
 140 
 141     /**
 142      * Returns the length in bytes that an output buffer would need to be
 143      * given the input length <code>inputLen</code> (in bytes).
 144      *
 145      * <p>The actual output length of the next <code>update</code> or
 146      * <code>doFinal</code> call may be smaller than the length returned
 147      * by this method.
 148      *
 149      * @param inputLen the input length (in bytes)
 150      *
 151      * @return the required output buffer size (in bytes)
 152      */
 153     protected int engineGetOutputSize(int inputLen) {
 154         // can only return an upper-limit if not initialized yet.
 155         int result = 0;
 156         if (decrypting) {
 157             result = inputLen - 8;
 158         } else {
 159             result = Math.addExact(inputLen, 8);
 160         }
 161         return (result < 0? 0:result);
 162     }
 163 
 164     /**
 165      * Returns the initialization vector (IV) which is null for this cipher.
 166      *
 167      * @return null for this cipher.
 168      */
 169     protected byte[] engineGetIV() {
 170         return null;
 171     }
 172 
 173     /**
 174      * Initializes this cipher with a key and a source of randomness.
 175      *
 176      * <p>The cipher only supports the following two operation modes:<b>
 177      * Cipher.WRAP_MODE, and <b>
 178      * Cipher.UNWRAP_MODE.
 179      * <p>For modes other than the above two, UnsupportedOperationException
 180      * will be thrown.
 181      *
 182      * @param opmode the operation mode of this cipher. Only
 183      * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
 184      * @param key the secret key.
 185      * @param random the source of randomness.
 186      *
 187      * @exception InvalidKeyException if the given key is inappropriate for
 188      * initializing this cipher.
 189      */
 190     protected void engineInit(int opmode, Key key, SecureRandom random)
 191         throws InvalidKeyException {
 192         if (opmode == Cipher.WRAP_MODE) {
 193             decrypting = false;
 194         } else if (opmode == Cipher.UNWRAP_MODE) {
 195             decrypting = true;
 196         } else {
 197             throw new UnsupportedOperationException("This cipher can " +
 198                 "only be used for key wrapping and unwrapping");
 199         }
 200         AESCipher.checkKeySize(key, fixedKeySize);
 201         cipher.init(decrypting, key.getAlgorithm(), key.getEncoded());
 202     }
 203 
 204     /**
 205      * Initializes this cipher with a key, a set of algorithm parameters,
 206      * and a source of randomness.
 207      *
 208      * <p>The cipher only supports the following two operation modes:<b>
 209      * Cipher.WRAP_MODE, and <b>
 210      * Cipher.UNWRAP_MODE.
 211      * <p>For modes other than the above two, UnsupportedOperationException
 212      * will be thrown.
 213      *
 214      * @param opmode the operation mode of this cipher. Only
 215      * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
 216      * @param key the secret key.
 217      * @param params the algorithm parameters; must be null for this cipher.
 218      * @param random the source of randomness.
 219      *
 220      * @exception InvalidKeyException if the given key is inappropriate for
 221      * initializing this cipher
 222      * @exception InvalidAlgorithmParameterException if the given algorithm
 223      * parameters is not null.
 224      */
 225     protected void engineInit(int opmode, Key key,
 226                               AlgorithmParameterSpec params,
 227                               SecureRandom random)
 228         throws InvalidKeyException, InvalidAlgorithmParameterException {
 229         if (params != null) {
 230             throw new InvalidAlgorithmParameterException("This cipher " +
 231                 "does not accept any parameters");
 232         }
 233         engineInit(opmode, key, random);
 234     }
 235 
 236     /**
 237      * Initializes this cipher with a key, a set of algorithm parameters,
 238      * and a source of randomness.
 239      *
 240      * <p>The cipher only supports the following two operation modes:<b>
 241      * Cipher.WRAP_MODE, and <b>
 242      * Cipher.UNWRAP_MODE.
 243      * <p>For modes other than the above two, UnsupportedOperationException
 244      * will be thrown.
 245      *
 246      * @param opmode the operation mode of this cipher. Only
 247      * <code>WRAP_MODE</code> or <code>UNWRAP_MODE</code>) are accepted.
 248      * @param key the secret key.
 249      * @param params the algorithm parameters; must be null for this cipher.
 250      * @param random the source of randomness.
 251      *
 252      * @exception InvalidKeyException if the given key is inappropriate.
 253      * @exception InvalidAlgorithmParameterException if the given algorithm
 254      * parameters is not null.
 255      */
 256     protected void engineInit(int opmode, Key key,
 257                               AlgorithmParameters params,
 258                               SecureRandom random)
 259         throws InvalidKeyException, InvalidAlgorithmParameterException {
 260         if (params != null) {
 261             throw new InvalidAlgorithmParameterException("This cipher " +
 262                 "does not accept any parameters");
 263         }
 264         engineInit(opmode, key, random);
 265     }
 266 
 267     /**
 268      * This operation is not supported by this cipher.
 269      * Since it's impossible to initialize this cipher given the
 270      * current Cipher.engineInit(...) implementation,
 271      * IllegalStateException will always be thrown upon invocation.
 272      *
 273      * @param in the input buffer.
 274      * @param inOffset the offset in <code>in</code> where the input
 275      * starts.
 276      * @param inLen the input length.
 277      *
 278      * @return n/a.
 279      *
 280      * @exception IllegalStateException upon invocation of this method.
 281      */
 282     protected byte[] engineUpdate(byte[] in, int inOffset, int inLen) {
 283         throw new IllegalStateException("Cipher has not been initialized");
 284     }
 285 
 286     /**
 287      * This operation is not supported by this cipher.
 288      * Since it's impossible to initialize this cipher given the
 289      * current Cipher.engineInit(...) implementation,
 290      * IllegalStateException will always be thrown upon invocation.
 291      *
 292      * @param in the input buffer.
 293      * @param inOffset the offset in <code>in</code> where the input
 294      * starts.
 295      * @param inLen the input length.
 296      * @param out the buffer for the result.
 297      * @param outOffset the offset in <code>out</code> where the result
 298      * is stored.
 299      *
 300      * @return n/a.
 301      *
 302      * @exception IllegalStateException upon invocation of this method.
 303      */
 304     protected int engineUpdate(byte[] in, int inOffset, int inLen,
 305                                byte[] out, int outOffset)
 306         throws ShortBufferException {
 307         throw new IllegalStateException("Cipher has not been initialized");
 308     }
 309 
 310     /**
 311      * This operation is not supported by this cipher.
 312      * Since it's impossible to initialize this cipher given the
 313      * current Cipher.engineInit(...) implementation,
 314      * IllegalStateException will always be thrown upon invocation.
 315      *
 316      * @param in the input buffer
 317      * @param inOffset the offset in <code>in</code> where the input
 318      * starts
 319      * @param inLen the input length.
 320      *
 321      * @return n/a.
 322      *
 323      * @exception IllegalStateException upon invocation of this method.
 324      */
 325     protected byte[] engineDoFinal(byte[] input, int inputOffset,
 326                                    int inputLen)
 327         throws IllegalBlockSizeException, BadPaddingException {
 328         throw new IllegalStateException("Cipher has not been initialized");
 329     }
 330 
 331     /**
 332      * This operation is not supported by this cipher.
 333      * Since it's impossible to initialize this cipher given the
 334      * current Cipher.engineInit(...) implementation,
 335      * IllegalStateException will always be thrown upon invocation.
 336      *
 337      * @param in the input buffer.
 338      * @param inOffset the offset in <code>in</code> where the input
 339      * starts.
 340      * @param inLen the input length.
 341      * @param out the buffer for the result.
 342      * @param outOffset the ofset in <code>out</code> where the result
 343      * is stored.
 344      *
 345      * @return n/a.
 346      *
 347      * @exception IllegalStateException upon invocation of this method.
 348      */
 349     protected int engineDoFinal(byte[] in, int inOffset, int inLen,
 350                                 byte[] out, int outOffset)
 351         throws IllegalBlockSizeException, ShortBufferException,
 352                BadPaddingException {
 353         throw new IllegalStateException("Cipher has not been initialized");
 354     }
 355 
 356     /**
 357      * Returns the parameters used with this cipher which is always null
 358      * for this cipher.
 359      *
 360      * @return null since this cipher does not use any parameters.
 361      */
 362     protected AlgorithmParameters engineGetParameters() {
 363         return null;
 364     }
 365 
 366     /**
 367      * Returns the key size of the given key object in number of bits.
 368      *
 369      * @param key the key object.
 370      *
 371      * @return the "effective" key size of the given key object.
 372      *
 373      * @exception InvalidKeyException if <code>key</code> is invalid.
 374      */
 375     protected int engineGetKeySize(Key key) throws InvalidKeyException {
 376         byte[] encoded = key.getEncoded();
 377         if (!AESCrypt.isKeySizeValid(encoded.length)) {
 378             throw new InvalidKeyException("Invalid key length: " +
 379                                           encoded.length + " bytes");
 380         }
 381         return Math.multiplyExact(encoded.length, 8);
 382     }
 383 
 384     /**
 385      * Wrap a key.
 386      *
 387      * @param key the key to be wrapped.
 388      *
 389      * @return the wrapped key.
 390      *
 391      * @exception IllegalBlockSizeException if this cipher is a block
 392      * cipher, no padding has been requested, and the length of the
 393      * encoding of the key to be wrapped is not a
 394      * multiple of the block size.
 395      *
 396      * @exception InvalidKeyException if it is impossible or unsafe to
 397      * wrap the key with this cipher (e.g., a hardware protected key is
 398      * being passed to a software only cipher).
 399      */
 400     protected byte[] engineWrap(Key key)
 401         throws IllegalBlockSizeException, InvalidKeyException {
 402         byte[] keyVal = key.getEncoded();
 403         if ((keyVal == null) || (keyVal.length == 0)) {
 404             throw new InvalidKeyException("Cannot get an encoding of " +
 405                                           "the key to be wrapped");
 406         }
 407         byte[] out = new byte[Math.addExact(keyVal.length, 8)];
 408 
 409         if (keyVal.length == 8) {
 410             System.arraycopy(IV, 0, out, 0, IV.length);
 411             System.arraycopy(keyVal, 0, out, IV.length, 8);
 412             cipher.encryptBlock(out, 0, out, 0);
 413         } else {
 414             if (keyVal.length % 8 != 0) {
 415                 throw new IllegalBlockSizeException("length of the " +
 416                     "to be wrapped key should be multiples of 8 bytes");
 417             }
 418             System.arraycopy(IV, 0, out, 0, IV.length);
 419             System.arraycopy(keyVal, 0, out, IV.length, keyVal.length);
 420             int N = keyVal.length/8;
 421             byte[] buffer = new byte[blksize];
 422             for (int j = 0; j < 6; j++) {
 423                 for (int i = 1; i <= N; i++) {
 424                     int T = i + j*N;
 425                     System.arraycopy(out, 0, buffer, 0, IV.length);
 426                     System.arraycopy(out, i*8, buffer, IV.length, 8);
 427                     cipher.encryptBlock(buffer, 0, buffer, 0);
 428                     for (int k = 1; T != 0; k++) {
 429                         byte v = (byte) T;
 430                         buffer[IV.length - k] ^= v;
 431                         T >>>= 8;
 432                     }
 433                     System.arraycopy(buffer, 0, out, 0, IV.length);
 434                     System.arraycopy(buffer, 8, out, 8*i, 8);
 435                 }
 436             }
 437         }
 438         return out;
 439     }
 440 
 441     /**
 442      * Unwrap a previously wrapped key.
 443      *
 444      * @param wrappedKey the key to be unwrapped.
 445      *
 446      * @param wrappedKeyAlgorithm the algorithm the wrapped key is for.
 447      *
 448      * @param wrappedKeyType the type of the wrapped key.
 449      * This is one of <code>Cipher.SECRET_KEY</code>,
 450      * <code>Cipher.PRIVATE_KEY</code>, or <code>Cipher.PUBLIC_KEY</code>.
 451      *
 452      * @return the unwrapped key.
 453      *
 454      * @exception NoSuchAlgorithmException if no installed providers
 455      * can create keys of type <code>wrappedKeyType</code> for the
 456      * <code>wrappedKeyAlgorithm</code>.
 457      *
 458      * @exception InvalidKeyException if <code>wrappedKey</code> does not
 459      * represent a wrapped key of type <code>wrappedKeyType</code> for
 460      * the <code>wrappedKeyAlgorithm</code>.
 461      */
 462     protected Key engineUnwrap(byte[] wrappedKey,
 463                                String wrappedKeyAlgorithm,
 464                                int wrappedKeyType)
 465         throws InvalidKeyException, NoSuchAlgorithmException {
 466         int wrappedKeyLen = wrappedKey.length;
 467         // ensure the wrappedKey length is multiples of 8 bytes and non-zero
 468         if (wrappedKeyLen == 0) {
 469             throw new InvalidKeyException("The wrapped key is empty");
 470         }
 471         if (wrappedKeyLen % 8 != 0) {
 472             throw new InvalidKeyException
 473                 ("The wrapped key has invalid key length");
 474         }
 475         byte[] out = new byte[wrappedKeyLen - 8];
 476         byte[] buffer = new byte[blksize];
 477         if (wrappedKeyLen == 16) {
 478             cipher.decryptBlock(wrappedKey, 0, buffer, 0);
 479             for (int i = 0; i < IV.length; i++) {
 480                 if (IV[i] != buffer[i]) {
 481                     throw new InvalidKeyException("Integrity check failed");
 482                 }
 483             }
 484             System.arraycopy(buffer, IV.length, out, 0, out.length);
 485         } else {
 486             System.arraycopy(wrappedKey, 0, buffer, 0, IV.length);
 487             System.arraycopy(wrappedKey, IV.length, out, 0, out.length);
 488             int N = out.length/8;
 489             for (int j = 5; j >= 0; j--) {
 490                 for (int i = N; i > 0; i--) {
 491                     int T = i + j*N;
 492                     System.arraycopy(out, 8*(i-1), buffer, IV.length, 8);
 493                     for (int k = 1; T != 0; k++) {
 494                         byte v = (byte) T;
 495                         buffer[IV.length - k] ^= v;
 496                         T >>>= 8;
 497                     }
 498                     cipher.decryptBlock(buffer, 0, buffer, 0);
 499                     System.arraycopy(buffer, IV.length, out, 8*(i-1), 8);
 500                 }
 501             }
 502             for (int i = 0; i < IV.length; i++) {
 503                 if (IV[i] != buffer[i]) {
 504                     throw new InvalidKeyException("Integrity check failed");
 505                 }
 506             }
 507         }
 508         return ConstructKeys.constructKey(out, wrappedKeyAlgorithm,
 509                                           wrappedKeyType);
 510     }
 511 }