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 output-feedback (OFB) 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 final class OutputFeedback extends FeedbackCipher {
44 
45     /*
46      * output buffer
47      */
48     private byte[] k = null;
49 
50     /*
51      * register buffer
52      */
53     private byte[] register = null;
54 
55     /*
56      * number of bytes for each stream unit, defaults to the blocksize
57      * of the embedded cipher
58      */
59     private int numBytes;
60 
61     // variables for save/restore calls
62     private byte[] registerSave = null;
63 
OutputFeedback(SymmetricCipher embeddedCipher, int numBytes)64     OutputFeedback(SymmetricCipher embeddedCipher, int numBytes) {
65         super(embeddedCipher);
66         if (numBytes > blockSize) {
67             numBytes = blockSize;
68         }
69         this.numBytes = numBytes;
70         k = new byte[blockSize];
71         register = new byte[blockSize];
72     }
73 
74     /**
75      * Gets the name of this feedback mode.
76      *
77      * @return the string <code>OFB</code>
78      */
getFeedback()79     String getFeedback() {
80         return "OFB";
81     }
82 
83     /**
84      * Initializes the cipher in the specified mode with the given key
85      * and iv.
86      *
87      * @param decrypting flag indicating encryption or decryption
88      * @param algorithm the algorithm name
89      * @param key the key
90      * @param iv the iv
91      *
92      * @exception InvalidKeyException if the given key is inappropriate for
93      * initializing this cipher
94      */
init(boolean decrypting, String algorithm, byte[] key, byte[] iv)95     void init(boolean decrypting, String algorithm, byte[] key, byte[] iv)
96             throws InvalidKeyException {
97         if ((key == null) || (iv == null) || (iv.length != blockSize)) {
98             throw new InvalidKeyException("Internal error");
99         }
100         this.iv = iv;
101         reset();
102         // always encrypt mode for embedded cipher
103         embeddedCipher.init(false, algorithm, key);
104     }
105 
106     /**
107      * Resets the iv to its original value.
108      * This is used when doFinal is called in the Cipher class, so that the
109      * cipher can be reused (with its original iv).
110      */
reset()111     void reset() {
112         System.arraycopy(iv, 0, register, 0, blockSize);
113     }
114 
115     /**
116      * Save the current content of this cipher.
117      */
save()118     void save() {
119         if (registerSave == null) {
120             registerSave = new byte[blockSize];
121         }
122         System.arraycopy(register, 0, registerSave, 0, blockSize);
123     }
124 
125     /**
126      * Restores the content of this cipher to the previous saved one.
127      */
restore()128     void restore() {
129         System.arraycopy(registerSave, 0, register, 0, blockSize);
130     }
131 
132     /**
133      * Performs encryption operation.
134      *
135      * <p>The input plain text <code>plain</code>, starting at
136      * <code>plainOffset</code> and ending at
137      * <code>(plainOffset + plainLen - 1)</code>, is encrypted.
138      * The result is stored in <code>cipher</code>, starting at
139      * <code>cipherOffset</code>.
140      *
141      * @param plain the buffer with the input data to be encrypted
142      * @param plainOffset the offset in <code>plain</code>
143      * @param plainLen the length of the input data
144      * @param cipher the buffer for the result
145      * @param cipherOffset the offset in <code>cipher</code>
146      * @exception ProviderException if <code>plainLen</code> is not
147      * a multiple of the <code>numBytes</code>
148      * @return the length of the encrypted data
149      */
encrypt(byte[] plain, int plainOffset, int plainLen, byte[] cipher, int cipherOffset)150     int encrypt(byte[] plain, int plainOffset, int plainLen,
151                 byte[] cipher, int cipherOffset) {
152         ArrayUtil.blockSizeCheck(plainLen, numBytes);
153         ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
154         ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
155 
156         int nShift = blockSize - numBytes;
157         int loopCount = plainLen / numBytes;
158 
159         for (; loopCount > 0;
160              plainOffset += numBytes, cipherOffset += numBytes,
161              loopCount--) {
162             embeddedCipher.encryptBlock(register, 0, k, 0);
163             for (int i = 0; i < numBytes; i++) {
164                 cipher[i + cipherOffset] =
165                     (byte)(k[i] ^ plain[i + plainOffset]);
166             }
167             if (nShift != 0) {
168                 System.arraycopy(register, numBytes, register, 0, nShift);
169             }
170             System.arraycopy(k, 0, register, nShift, numBytes);
171         }
172         return plainLen;
173     }
174 
175     /**
176      * Performs 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 length of the encrypted data
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         ArrayUtil.nullAndBoundsCheck(plain, plainOffset, plainLen);
194         ArrayUtil.nullAndBoundsCheck(cipher, cipherOffset, plainLen);
195 
196         int oddBytes = plainLen % numBytes;
197         int len = encrypt(plain, plainOffset, (plainLen - oddBytes),
198                           cipher, cipherOffset);
199         plainOffset += len;
200         cipherOffset += len;
201 
202         if (oddBytes != 0) {
203             embeddedCipher.encryptBlock(register, 0, k, 0);
204             for (int i = 0; i < oddBytes; i++) {
205                 cipher[i + cipherOffset] =
206                     (byte)(k[i] ^ plain[ i + plainOffset]);
207             }
208         }
209         return plainLen;
210     }
211 
212     // OFB encrypt and decrypt are identical
decrypt(byte[] cipher, int cipherOffset, int cipherLen, byte[] plain, int plainOffset)213     int decrypt(byte[] cipher, int cipherOffset, int cipherLen,
214                 byte[] plain, int plainOffset) {
215         return encrypt(cipher, cipherOffset, cipherLen, plain, plainOffset);
216     }
217 
218     // OFB encrypt and decrypt are identical
decryptFinal(byte[] cipher, int cipherOffset, int cipherLen, byte[] plain, int plainOffset)219     int decryptFinal(byte[] cipher, int cipherOffset, int cipherLen,
220                      byte[] plain, int plainOffset) {
221         // OFB encrypt and decrypt are identical
222         return encryptFinal(cipher, cipherOffset, cipherLen, plain, plainOffset);
223     }
224 }
225