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