1package org.bouncycastle.crypto.modes; 2 3import org.bouncycastle.crypto.BlockCipher; 4import org.bouncycastle.crypto.CipherParameters; 5import org.bouncycastle.crypto.DataLengthException; 6import org.bouncycastle.crypto.params.ParametersWithIV; 7 8/** 9 * implements the GOST 28147 OFB counter mode (GCTR). 10 */ 11public class GOFBBlockCipher 12 implements BlockCipher 13{ 14 private byte[] IV; 15 private byte[] ofbV; 16 private byte[] ofbOutV; 17 18 private final int blockSize; 19 private final BlockCipher cipher; 20 21 boolean firstStep = true; 22 int N3; 23 int N4; 24 static final int C1 = 16843012; //00000001000000010000000100000100 25 static final int C2 = 16843009; //00000001000000010000000100000001 26 27 28 /** 29 * Basic constructor. 30 * 31 * @param cipher the block cipher to be used as the basis of the 32 * counter mode (must have a 64 bit block size). 33 */ 34 public GOFBBlockCipher( 35 BlockCipher cipher) 36 { 37 this.cipher = cipher; 38 this.blockSize = cipher.getBlockSize(); 39 40 if (blockSize != 8) 41 { 42 throw new IllegalArgumentException("GCTR only for 64 bit block ciphers"); 43 } 44 45 this.IV = new byte[cipher.getBlockSize()]; 46 this.ofbV = new byte[cipher.getBlockSize()]; 47 this.ofbOutV = new byte[cipher.getBlockSize()]; 48 } 49 50 /** 51 * return the underlying block cipher that we are wrapping. 52 * 53 * @return the underlying block cipher that we are wrapping. 54 */ 55 public BlockCipher getUnderlyingCipher() 56 { 57 return cipher; 58 } 59 60 /** 61 * Initialise the cipher and, possibly, the initialisation vector (IV). 62 * If an IV isn't passed as part of the parameter, the IV will be all zeros. 63 * An IV which is too short is handled in FIPS compliant fashion. 64 * 65 * @param encrypting if true the cipher is initialised for 66 * encryption, if false for decryption. 67 * @param params the key and other data required by the cipher. 68 * @exception IllegalArgumentException if the params argument is 69 * inappropriate. 70 */ 71 public void init( 72 boolean encrypting, //ignored by this CTR mode 73 CipherParameters params) 74 throws IllegalArgumentException 75 { 76 firstStep = true; 77 N3 = 0; 78 N4 = 0; 79 80 if (params instanceof ParametersWithIV) 81 { 82 ParametersWithIV ivParam = (ParametersWithIV)params; 83 byte[] iv = ivParam.getIV(); 84 85 if (iv.length < IV.length) 86 { 87 // prepend the supplied IV with zeros (per FIPS PUB 81) 88 System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length); 89 for (int i = 0; i < IV.length - iv.length; i++) 90 { 91 IV[i] = 0; 92 } 93 } 94 else 95 { 96 System.arraycopy(iv, 0, IV, 0, IV.length); 97 } 98 99 reset(); 100 101 cipher.init(true, ivParam.getParameters()); 102 } 103 else 104 { 105 reset(); 106 107 cipher.init(true, params); 108 } 109 } 110 111 /** 112 * return the algorithm name and mode. 113 * 114 * @return the name of the underlying algorithm followed by "/GCTR" 115 * and the block size in bits 116 */ 117 public String getAlgorithmName() 118 { 119 return cipher.getAlgorithmName() + "/GCTR"; 120 } 121 122 123 /** 124 * return the block size we are operating at (in bytes). 125 * 126 * @return the block size we are operating at (in bytes). 127 */ 128 public int getBlockSize() 129 { 130 return blockSize; 131 } 132 133 /** 134 * Process one block of input from the array in and write it to 135 * the out array. 136 * 137 * @param in the array containing the input data. 138 * @param inOff offset into the in array the data starts at. 139 * @param out the array the output data will be copied into. 140 * @param outOff the offset into the out array the output will start at. 141 * @exception DataLengthException if there isn't enough data in in, or 142 * space in out. 143 * @exception IllegalStateException if the cipher isn't initialised. 144 * @return the number of bytes processed and produced. 145 */ 146 public int processBlock( 147 byte[] in, 148 int inOff, 149 byte[] out, 150 int outOff) 151 throws DataLengthException, IllegalStateException 152 { 153 if ((inOff + blockSize) > in.length) 154 { 155 throw new DataLengthException("input buffer too short"); 156 } 157 158 if ((outOff + blockSize) > out.length) 159 { 160 throw new DataLengthException("output buffer too short"); 161 } 162 163 if (firstStep) 164 { 165 firstStep = false; 166 cipher.processBlock(ofbV, 0, ofbOutV, 0); 167 N3 = bytesToint(ofbOutV, 0); 168 N4 = bytesToint(ofbOutV, 4); 169 } 170 N3 += C2; 171 N4 += C1; 172 intTobytes(N3, ofbV, 0); 173 intTobytes(N4, ofbV, 4); 174 175 cipher.processBlock(ofbV, 0, ofbOutV, 0); 176 177 // 178 // XOR the ofbV with the plaintext producing the cipher text (and 179 // the next input block). 180 // 181 for (int i = 0; i < blockSize; i++) 182 { 183 out[outOff + i] = (byte)(ofbOutV[i] ^ in[inOff + i]); 184 } 185 186 // 187 // change over the input block. 188 // 189 System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize); 190 System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize); 191 192 return blockSize; 193 } 194 195 /** 196 * reset the feedback vector back to the IV and reset the underlying 197 * cipher. 198 */ 199 public void reset() 200 { 201 System.arraycopy(IV, 0, ofbV, 0, IV.length); 202 203 cipher.reset(); 204 } 205 206 //array of bytes to type int 207 private int bytesToint( 208 byte[] in, 209 int inOff) 210 { 211 return ((in[inOff + 3] << 24) & 0xff000000) + ((in[inOff + 2] << 16) & 0xff0000) + 212 ((in[inOff + 1] << 8) & 0xff00) + (in[inOff] & 0xff); 213 } 214 215 //int to array of bytes 216 private void intTobytes( 217 int num, 218 byte[] out, 219 int outOff) 220 { 221 out[outOff + 3] = (byte)(num >>> 24); 222 out[outOff + 2] = (byte)(num >>> 16); 223 out[outOff + 1] = (byte)(num >>> 8); 224 out[outOff] = (byte)num; 225 } 226} 227