1 /*
   2  * Copyright (c) 1997, 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.sun.crypto.provider;
  27 
  28 import java.security.InvalidKeyException;
  29 import java.security.ProviderException;
  30 import java.util.Objects;
  31 
  32 import jdk.internal.HotSpotIntrinsicCandidate;
  33 
  34 
  35 /**
  36  * This class represents ciphers in cipher block chaining (CBC) mode.
  37  *
  38  * <p>This mode is implemented independently of a particular cipher.
  39  * Ciphers to which this mode should apply (e.g., DES) must be
  40  * <i>plugged-in</i> using the constructor.
  41  *
  42  * <p>NOTE: This class does not deal with buffering or padding.
  43  *
  44  * @author Gigi Ankeny
  45  */
  46 
  47 class CipherBlockChaining extends FeedbackCipher  {
  48 
  49     /*
  50      * random bytes that are initialized with iv
  51      */
  52     protected byte[] r;
  53 
  54     /*
  55      * output buffer
  56      */
  57     private byte[] k;
  58 
  59     // variables for save/restore calls
  60     private byte[] rSave = null;
  61 
  62     CipherBlockChaining(SymmetricCipher embeddedCipher) {
  63         super(embeddedCipher);
  64         k = new byte[blockSize];
  65         r = new byte[blockSize];
  66     }
  67 
  68     /**
  69      * Gets the name of this feedback mode.
  70      *
  71      * @return the string <code>CBC</code>
  72      */
  73     String getFeedback() {
  74         return "CBC";
  75     }
  76 
  77     /**
  78      * Initializes the cipher in the specified mode with the given key
  79      * and iv.
  80      *
  81      * @param decrypting flag indicating encryption or decryption
  82      * @param algorithm the algorithm name
  83      * @param key the key
  84      * @param iv the iv
  85      *
  86      * @exception InvalidKeyException if the given key is inappropriate for
  87      * initializing this cipher
  88      */
  89     void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
  90             throws InvalidKeyException {
  91         if ((key == null) || (iv == null) || (iv.length != blockSize)) {
  92             throw new InvalidKeyException("Internal error");
  93         }
  94         this.iv = iv;
  95         reset();
  96         embeddedCipher.init(decrypting, algorithm, key);
  97     }
  98 
  99     /**
 100      * Resets the iv to its original value.
 101      * This is used when doFinal is called in the Cipher class, so that the
 102      * cipher can be reused (with its original iv).
 103      */
 104     void reset() {
 105         System.arraycopy(iv, 0, r, 0, blockSize);
 106     }
 107 
 108     /**
 109      * Save the current content of this cipher.
 110      */
 111     void save() {
 112         if (rSave == null) {
 113             rSave = new byte[blockSize];
 114         }
 115         System.arraycopy(r, 0, rSave, 0, blockSize);
 116     }
 117 
 118     /**
 119      * Restores the content of this cipher to the previous saved one.
 120      */
 121     void restore() {
 122         System.arraycopy(rSave, 0, r, 0, blockSize);
 123     }
 124 
 125     /**
 126      * Performs encryption operation.
 127      *
 128      * <p>The input plain text <code>plain</code>, starting at
 129      * <code>plainOffset</code> and ending at
 130      * <code>(plainOffset + plainLen - 1)</code>, is encrypted.
 131      * The result is stored in <code>cipher</code>, starting at
 132      * <code>cipherOffset</code>.
 133      *
 134      * @param plain the buffer with the input data to be encrypted
 135      * @param plainOffset the offset in <code>plain</code>
 136      * @param plainLen the length of the input data
 137      * @param cipher the buffer for the result
 138      * @param cipherOffset the offset in <code>cipher</code>
 139      * @exception ProviderException if <code>len</code> is not
 140      * a multiple of the block size
 141      * @return the length of the encrypted data
 142      */
 143     int encrypt(byte[] plain, int plainOffset, int plainLen,
 144                 byte[] cipher, int cipherOffset) {
 145         cryptBlockSizeCheck(plainLen);
 146         cryptNullAndBoundsCheck(plain, plainOffset, plainLen);
 147         cryptNullAndBoundsCheck(cipher, cipherOffset, plainLen);
 148         return implEncrypt(plain, plainOffset, plainLen,
 149                            cipher, cipherOffset);
 150     }
 151 
 152     @HotSpotIntrinsicCandidate
 153     private int implEncrypt(byte[] plain, int plainOffset, int plainLen,
 154                             byte[] cipher, int cipherOffset)
 155     {
 156         int endIndex = plainOffset + plainLen;
 157 
 158         for (; plainOffset < endIndex;
 159              plainOffset += blockSize, cipherOffset += blockSize) {
 160             for (int i = 0; i < blockSize; i++) {
 161                 k[i] = (byte)(plain[i + plainOffset] ^ r[i]);
 162             }
 163             embeddedCipher.encryptBlock(k, 0, cipher, cipherOffset);
 164             System.arraycopy(cipher, cipherOffset, r, 0, blockSize);
 165         }
 166         return plainLen;
 167     }
 168 
 169     /**
 170      * Performs decryption operation.
 171      *
 172      * <p>The input cipher text <code>cipher</code>, starting at
 173      * <code>cipherOffset</code> and ending at
 174      * <code>(cipherOffset + cipherLen - 1)</code>, is decrypted.
 175      * The result is stored in <code>plain</code>, starting at
 176      * <code>plainOffset</code>.
 177      *
 178      * <p>It is also the application's responsibility to make sure that
 179      * <code>init</code> has been called before this method is called.
 180      * (This check is omitted here, to avoid double checking.)
 181      *
 182      * @param cipher the buffer with the input data to be decrypted
 183      * @param cipherOffset the offset in <code>cipherOffset</code>
 184      * @param cipherLen the length of the input data
 185      * @param plain the buffer for the result
 186      * @param plainOffset the offset in <code>plain</code>
 187      * @exception ProviderException if <code>len</code> is not
 188      * a multiple of the block size
 189      * @return the length of the decrypted data
 190      */
 191     int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
 192                 byte[] plain, int plainOffset) {
 193         cryptBlockSizeCheck(cipherLen);
 194         cryptNullAndBoundsCheck(cipher, cipherOffset, cipherLen);
 195         cryptNullAndBoundsCheck(plain, plainOffset, cipherLen);
 196         return implDecrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
 197     }
 198 
 199     @HotSpotIntrinsicCandidate
 200     private int implDecrypt(byte[] cipher, int cipherOffset, int cipherLen,
 201                             byte[] plain, int plainOffset)
 202     {
 203         int endIndex = cipherOffset + cipherLen;
 204 
 205         for (; cipherOffset < endIndex;
 206              cipherOffset += blockSize, plainOffset += blockSize) {
 207             embeddedCipher.decryptBlock(cipher, cipherOffset, k, 0);
 208             for (int i = 0; i < blockSize; i++) {
 209                 plain[i + plainOffset] = (byte)(k[i] ^ r[i]);
 210             }
 211             System.arraycopy(cipher, cipherOffset, r, 0, blockSize);
 212         }
 213         return cipherLen;
 214     }
 215 
 216     private void cryptBlockSizeCheck(int len) {
 217         if ((len % blockSize) != 0) {
 218             throw new ProviderException("Internal error in input buffering");
 219         }
 220     }
 221 
 222     private static void cryptNullAndBoundsCheck(byte[] array, int offset, int len) {
 223         if (len <= 0) {
 224             return; // not an error because cryptImpl/decryptImpl won't execute if len <= 0
 225         }
 226 
 227         Objects.requireNonNull(array);
 228 
 229         if (offset < 0 || offset >= array.length) {
 230             throw new ArrayIndexOutOfBoundsException(offset);
 231         }
 232 
 233         int endIndex = offset + len - 1;
 234         if (endIndex < 0 || endIndex >= array.length) {
 235             throw new ArrayIndexOutOfBoundsException(endIndex);
 236         }
 237     }
 238 }