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 sun.security.util.ArrayUtil;
31 
32 /**
33  * This class represents ciphers in cipher-feedback (CFB) mode.
34  *
35  * <p>This mode is implemented independently of a particular cipher.
36  * Ciphers to which this mode should apply (e.g., DES) must be
37  * <i>plugged-in</i> using the constructor.
38  *
39  * <p>NOTE: This class does not deal with buffering or padding.
40  *
41  * @author Gigi Ankeny
42  */
43 
44 final class CipherFeedback extends FeedbackCipher {
45 
46     /*
47      * encrypt/decrypt output buffer
48      */
49     private final byte[] k;
50 
51     /*
52      * register value, initialized with iv
53      */
54     private final byte[] register;
55 
56     /*
57      * number of bytes for each stream unit, defaults to the blocksize
58      * of the embedded cipher
59      */
60     private int numBytes;
61 
62     // variables for save/restore calls
63     private byte[] registerSave = null;
64 
CipherFeedback(SymmetricCipher embeddedCipher, int numBytes)65     CipherFeedback(SymmetricCipher embeddedCipher, int numBytes) {
66         super(embeddedCipher);
67         if (numBytes > blockSize) {
68             numBytes = blockSize;
69         }
70         this.numBytes = numBytes;
71         k = new byte[blockSize];
72         register = new byte[blockSize];
73     }
74 
75     /**
76      * Gets the name of this feedback mode.
77      *
78      * @return the string <code>CFB</code>
79      */
getFeedback()80     String getFeedback() {
81         return "CFB";
82     }
83 
84     /**
85      * Initializes the cipher in the specified mode with the given key
86      * and iv.
87      *
88      * @param decrypting flag indicating encryption or decryption
89      * @param algorithm the algorithm name
90      * @param key the key
91      * @param iv the iv
92      *
93      * @exception InvalidKeyException if the given key is inappropriate for
94      * initializing this cipher
95      */
init(boolean decrypting, String algorithm, byte[] key, byte[] iv)96     void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
97             throws InvalidKeyException {
98         if ((key == null) || (iv == null) || (iv.length != blockSize)) {
99             throw new InvalidKeyException("Internal error");
100         }
101         this.iv = iv;
102         reset();
103         // always encrypt mode for embedded cipher
104         embeddedCipher.init(false, algorithm, key);
105     }
106 
107     /**
108      * Resets the iv to its original value.
109      * This is used when doFinal is called in the Cipher class, so that the
110      * cipher can be reused (with its original iv).
111      */
reset()112     void reset() {
113         System.arraycopy(iv, 0, register, 0, blockSize);
114     }
115 
116     /**
117      * Save the current content of this cipher.
118      */
save()119     void save() {
120         if (registerSave == null) {
121             registerSave = new byte[blockSize];
122         }
123         System.arraycopy(register, 0, registerSave, 0, blockSize);
124     }
125 
126     /**
127      * Restores the content of this cipher to the previous saved one.
128      */
restore()129     void restore() {
130         System.arraycopy(registerSave, 0, register, 0, blockSize);
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 + plainLen - 1)</code>, is encrypted.
139      * The result is stored in <code>cipher</code>, starting at
140      * <code>cipherOffset</code>.
141      *
142      * @param plain the buffer with the input data to be encrypted
143      * @param plainOffset the offset in <code>plain</code>
144      * @param plainLen the length of the input data
145      * @param cipher the buffer for the result
146      * @param cipherOffset the offset in <code>cipher</code>
147      * @exception ProviderException if <code>plainLen</code> is not
148      * a multiple of the <code>numBytes</code>
149      * @return the length of the encrypted data
150      */
encrypt(byte[] plain, int plainOffset, int plainLen, byte[] cipher, int cipherOffset)151     int encrypt(byte[] plain, int plainOffset, int plainLen,
152                 byte[] cipher, int cipherOffset) {
153         ArrayUtil.blockSizeCheck(plainLen, numBytes);
154         ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
155         ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
156 
157         int nShift = blockSize - numBytes;
158         int loopCount = plainLen / numBytes;
159 
160         for (; loopCount > 0 ;
161              plainOffset += numBytes, cipherOffset += numBytes,
162              loopCount--) {
163             embeddedCipher.encryptBlock(register, 0, k, 0);
164             if (nShift != 0) {
165                 System.arraycopy(register, numBytes, register, 0, nShift);
166             }
167             for (int i = 0; i < numBytes; i++) {
168                 register[nShift + i] = cipher[i + cipherOffset] =
169                         (byte)(k[i] ^ plain[i + plainOffset]);
170             }
171         }
172         return plainLen;
173     }
174 
175     /**
176      * Performs the last encryption operation.
177      *
178      * <p>The input plain text <code>plain</code>, starting at
179      * <code>plainOffset</code> and ending at
180      * <code>(plainOffset + plainLen - 1)</code>, is encrypted.
181      * The result is stored in <code>cipher</code>, starting at
182      * <code>cipherOffset</code>.
183      *
184      * @param plain the buffer with the input data to be encrypted
185      * @param plainOffset the offset in <code>plain</code>
186      * @param plainLen the length of the input data
187      * @param cipher the buffer for the result
188      * @param cipherOffset the offset in <code>cipher</code>
189      * @return the number of bytes placed into <code>cipher</code>
190      */
encryptFinal(byte[] plain, int plainOffset, int plainLen, byte[] cipher, int cipherOffset)191     int encryptFinal(byte[] plain, int plainOffset, int plainLen,
192                      byte[] cipher, int cipherOffset) {
193 
194         int oddBytes = plainLen % numBytes;
195         int len = encrypt(plain, plainOffset, (plainLen - oddBytes),
196                           cipher, cipherOffset);
197         plainOffset += len;
198         cipherOffset += len;
199         if (oddBytes != 0) {
200             embeddedCipher.encryptBlock(register, 0, k, 0);
201             for (int i = 0; i < oddBytes; i++) {
202                  cipher[i + cipherOffset] =
203                     (byte)(k[i] ^ plain[i + plainOffset]);
204             }
205         }
206         return plainLen;
207     }
208 
209     /**
210      * Performs decryption operation.
211      *
212      * <p>The input cipher text <code>cipher</code>, starting at
213      * <code>cipherOffset</code> and ending at
214      * <code>(cipherOffset + cipherLen - 1)</code>, is decrypted.
215      * The result is stored in <code>plain</code>, starting at
216      * <code>plainOffset</code>.
217      *
218      * @param cipher the buffer with the input data to be decrypted
219      * @param cipherOffset the offset in <code>cipherOffset</code>
220      * @param cipherLen the length of the input data
221      * @param plain the buffer for the result
222      * @param plainOffset the offset in <code>plain</code>
223      * @exception ProviderException if <code>cipherLen</code> is not
224      * a multiple of the <code>numBytes</code>
225      * @return the length of the decrypted data
226      */
decrypt(byte[] cipher, int cipherOffset, int cipherLen, byte[] plain, int plainOffset)227     int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
228                 byte[] plain, int plainOffset) {
229 
230         ArrayUtil.blockSizeCheck(cipherLen, numBytes);
231         ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, cipherLen);
232         ArrayUtil.nullAndBoundsCheck(plain, plainOffset, cipherLen);
233 
234         int nShift = blockSize - numBytes;
235         int loopCount = cipherLen / numBytes;
236 
237         for (; loopCount > 0;
238              plainOffset += numBytes, cipherOffset += numBytes,
239              loopCount--) {
240             embeddedCipher.encryptBlock(register, 0, k, 0);
241             if (nShift != 0) {
242                 System.arraycopy(register, numBytes, register, 0, nShift);
243             }
244             for (int i = 0; i < numBytes; i++) {
245                 register[i + nShift] = cipher[i + cipherOffset];
246                 plain[i + plainOffset]
247                     = (byte)(cipher[i + cipherOffset] ^ k[i]);
248             }
249         }
250         return cipherLen;
251     }
252 
253     /**
254      * Performs the last decryption operation.
255      *
256      * <p>The input cipher text <code>cipher</code>, starting at
257      * <code>cipherOffset</code> and ending at
258      * <code>(cipherOffset + cipherLen - 1)</code>, is decrypted.
259      * The result is stored in <code>plain</code>, starting at
260      * <code>plainOffset</code>.
261      *
262      * @param cipher the buffer with the input data to be decrypted
263      * @param cipherOffset the offset in <code>cipherOffset</code>
264      * @param cipherLen the length of the input data
265      * @param plain the buffer for the result
266      * @param plainOffset the offset in <code>plain</code>
267      * @return the length of the decrypted data
268      */
decryptFinal(byte[] cipher, int cipherOffset, int cipherLen, byte[] plain, int plainOffset)269     int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
270                 byte[] plain, int plainOffset) {
271 
272         int oddBytes = cipherLen % numBytes;
273         int len = decrypt(cipher, cipherOffset, (cipherLen - oddBytes),
274                           plain, plainOffset);
275         cipherOffset += len;
276         plainOffset += len;
277         if (oddBytes != 0) {
278             embeddedCipher.encryptBlock(register, 0, k, 0);
279             for (int i = 0; i < oddBytes; i++) {
280                 plain[i + plainOffset]
281                     = (byte)(cipher[i + cipherOffset] ^ k[i]);
282             }
283         }
284         return cipherLen;
285     }
286 }
287