1 /*
   2  * Copyright (c) 2002, 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.util.Objects;
  30 
  31 import jdk.internal.HotSpotIntrinsicCandidate;
  32 
  33 /**
  34  * This class represents ciphers in counter (CTR) mode.
  35  *
  36  * <p>This mode is implemented independently of a particular cipher.
  37  * Ciphers to which this mode should apply (e.g., DES) must be
  38  * <i>plugged-in</i> using the constructor.
  39  *
  40  * <p>NOTE: This class does not deal with buffering or padding.
  41  *
  42  * @author Andreas Sterbenz
  43  * @since 1.4.2
  44  */
  45 final class CounterMode extends FeedbackCipher {
  46 
  47     // current counter value
  48     private final byte[] counter;
  49 
  50     // encrypted bytes of the previous counter value
  51     private final byte[] encryptedCounter;
  52 
  53     // number of bytes in encryptedCounter already used up
  54     private int used;
  55 
  56     // variables for save/restore calls
  57     private byte[] counterSave = null;
  58     private byte[] encryptedCounterSave = null;
  59     private int usedSave = 0;
  60 
  61     CounterMode(SymmetricCipher embeddedCipher) {
  62         super(embeddedCipher);
  63         counter = new byte[blockSize];
  64         encryptedCounter = new byte[blockSize];
  65     }
  66 
  67     /**
  68      * Gets the name of the feedback mechanism
  69      *
  70      * @return the name of the feedback mechanism
  71      */
  72     String getFeedback() {
  73         return "CTR";
  74     }
  75 
  76     /**
  77      * Resets the iv to its original value.
  78      * This is used when doFinal is called in the Cipher class, so that the
  79      * cipher can be reused (with its original iv).
  80      */
  81     void reset() {
  82         System.arraycopy(iv, 0, counter, 0, blockSize);
  83         used = blockSize;
  84     }
  85 
  86     /**
  87      * Save the current content of this cipher.
  88      */
  89     void save() {
  90         if (counterSave == null) {
  91             counterSave = new byte[blockSize];
  92             encryptedCounterSave = new byte[blockSize];
  93         }
  94         System.arraycopy(counter, 0, counterSave, 0, blockSize);
  95         System.arraycopy(encryptedCounter, 0, encryptedCounterSave, 0,
  96             blockSize);
  97         usedSave = used;
  98     }
  99 
 100     /**
 101      * Restores the content of this cipher to the previous saved one.
 102      */
 103     void restore() {
 104         System.arraycopy(counterSave, 0, counter, 0, blockSize);
 105         System.arraycopy(encryptedCounterSave, 0, encryptedCounter, 0,
 106             blockSize);
 107         used = usedSave;
 108     }
 109 
 110     /**
 111      * Initializes the cipher in the specified mode with the given key
 112      * and iv.
 113      *
 114      * @param decrypting flag indicating encryption or decryption
 115      * @param algorithm the algorithm name
 116      * @param key the key
 117      * @param iv the iv
 118      *
 119      * @exception InvalidKeyException if the given key is inappropriate for
 120      * initializing this cipher
 121      */
 122     void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
 123             throws InvalidKeyException {
 124         if ((key == null) || (iv == null) || (iv.length != blockSize)) {
 125             throw new InvalidKeyException("Internal error");
 126         }
 127         this.iv = iv;
 128         reset();
 129         // always encrypt mode for embedded cipher
 130         embeddedCipher.init(false, algorithm, key);
 131     }
 132 
 133     /**
 134      * Performs encryption operation.
 135      *
 136      * <p>The input plain text <code>plain</code>, starting at
 137      * <code>plainOffset</code> and ending at
 138      * <code>(plainOffset + len - 1)</code>, is encrypted.
 139      * The result is stored in <code>cipher</code>, starting at
 140      * <code>cipherOffset</code>.
 141      *
 142      * @param in the buffer with the input data to be encrypted
 143      * @param inOff the offset in <code>plain</code>
 144      * @param len the length of the input data
 145      * @param out the buffer for the result
 146      * @param outOff the offset in <code>cipher</code>
 147      * @return the length of the encrypted data
 148      */
 149     int encrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
 150         return crypt(in, inOff, len, out, outOff);
 151     }
 152 
 153     // CTR encrypt and decrypt are identical
 154     int decrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
 155         return crypt(in, inOff, len, out, outOff);
 156     }
 157 
 158     /**
 159      * Increment the counter value.
 160      */
 161     private static void increment(byte[] b) {
 162         int n = b.length - 1;
 163         while ((n >= 0) && (++b[n] == 0)) {
 164             n--;
 165         }
 166     }
 167 
 168     /**
 169      * Do the actual encryption/decryption operation.
 170      * Essentially we XOR the input plaintext/ciphertext stream with a
 171      * keystream generated by encrypting the counter values. Counter values
 172      * are encrypted on demand.
 173      */
 174     private int crypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
 175 
 176         cryptBlockCheck(in, inOff, len);
 177         cryptBlockCheck(out, outOff, len);
 178         return implCrypt(in, inOff, len, out, outOff);
 179     }
 180 
 181     // Implementation of crpyt() method. Possibly replaced with a compiler intrinsic.
 182     @HotSpotIntrinsicCandidate
 183     private int implCrypt(byte[] in, int inOff, int len, byte[] out, int outOff) {
 184         int result = len;
 185         while (len-- > 0) {
 186             if (used >= blockSize) {
 187                 embeddedCipher.encryptBlock(counter, 0, encryptedCounter, 0);
 188                 increment(counter);
 189                 used = 0;
 190             }
 191             out[outOff++] = (byte)(in[inOff++] ^ encryptedCounter[used++]);
 192         }
 193         return result;
 194     }
 195 
 196     // Used to perform all checks required by the Java semantics
 197     // (i.e., null checks and bounds checks) on the input parameters to crypt().
 198     // Normally, the Java Runtime performs these checks, however, as crypt() is
 199     // possibly replaced with compiler intrinsic, the JDK performs the
 200     // required checks instead.
 201     // Does not check accesses to class-internal (private) arrays.
 202     private static void cryptBlockCheck(byte[] array, int offset, int len) {
 203         Objects.requireNonNull(array);
 204 
 205         if (offset < 0 || len < 0 || offset >= array.length) {
 206             throw new ArrayIndexOutOfBoundsException(offset);
 207         }
 208 
 209         int largestIndex = offset + len - 1;
 210         if (largestIndex < 0 || largestIndex >= array.length) {
 211             throw new ArrayIndexOutOfBoundsException(largestIndex);
 212         }
 213     }
 214 }