001: package org.bouncycastle.crypto.modes;
002:
003: import org.bouncycastle.crypto.BlockCipher;
004: import org.bouncycastle.crypto.CipherParameters;
005: import org.bouncycastle.crypto.DataLengthException;
006: import org.bouncycastle.crypto.params.ParametersWithIV;
007:
008: /**
009: * implements the GOST 28147 OFB counter mode (GCTR).
010: */
011: public class GOFBBlockCipher implements BlockCipher {
012: private byte[] IV;
013: private byte[] ofbV;
014: private byte[] ofbOutV;
015:
016: private final int blockSize;
017: private final BlockCipher cipher;
018:
019: boolean firstStep = true;
020: int N3;
021: int N4;
022: static final int C1 = 16843012; //00000001000000010000000100000100
023: static final int C2 = 16843009; //00000001000000010000000100000001
024:
025: /**
026: * Basic constructor.
027: *
028: * @param cipher the block cipher to be used as the basis of the
029: * counter mode (must have a 64 bit block size).
030: */
031: public GOFBBlockCipher(BlockCipher cipher) {
032: this .cipher = cipher;
033: this .blockSize = cipher.getBlockSize();
034:
035: if (blockSize != 8) {
036: throw new IllegalArgumentException(
037: "GTCR only for 64 bit block ciphers");
038: }
039:
040: this .IV = new byte[cipher.getBlockSize()];
041: this .ofbV = new byte[cipher.getBlockSize()];
042: this .ofbOutV = new byte[cipher.getBlockSize()];
043: }
044:
045: /**
046: * return the underlying block cipher that we are wrapping.
047: *
048: * @return the underlying block cipher that we are wrapping.
049: */
050: public BlockCipher getUnderlyingCipher() {
051: return cipher;
052: }
053:
054: /**
055: * Initialise the cipher and, possibly, the initialisation vector (IV).
056: * If an IV isn't passed as part of the parameter, the IV will be all zeros.
057: * An IV which is too short is handled in FIPS compliant fashion.
058: *
059: * @param encrypting if true the cipher is initialised for
060: * encryption, if false for decryption.
061: * @param params the key and other data required by the cipher.
062: * @exception IllegalArgumentException if the params argument is
063: * inappropriate.
064: */
065: public void init(boolean encrypting, //ignored by this CTR mode
066: CipherParameters params) throws IllegalArgumentException {
067: firstStep = true;
068: N3 = 0;
069: N4 = 0;
070:
071: if (params instanceof ParametersWithIV) {
072: ParametersWithIV ivParam = (ParametersWithIV) params;
073: byte[] iv = ivParam.getIV();
074:
075: if (iv.length < IV.length) {
076: // prepend the supplied IV with zeros (per FIPS PUB 81)
077: System.arraycopy(iv, 0, IV, IV.length - iv.length,
078: iv.length);
079: for (int i = 0; i < IV.length - iv.length; i++) {
080: IV[i] = 0;
081: }
082: } else {
083: System.arraycopy(iv, 0, IV, 0, IV.length);
084: }
085:
086: reset();
087:
088: cipher.init(true, ivParam.getParameters());
089: } else {
090: reset();
091:
092: cipher.init(true, params);
093: }
094: }
095:
096: /**
097: * return the algorithm name and mode.
098: *
099: * @return the name of the underlying algorithm followed by "/GCTR"
100: * and the block size in bits
101: */
102: public String getAlgorithmName() {
103: return cipher.getAlgorithmName() + "/GCTR";
104: }
105:
106: /**
107: * return the block size we are operating at (in bytes).
108: *
109: * @return the block size we are operating at (in bytes).
110: */
111: public int getBlockSize() {
112: return blockSize;
113: }
114:
115: /**
116: * Process one block of input from the array in and write it to
117: * the out array.
118: *
119: * @param in the array containing the input data.
120: * @param inOff offset into the in array the data starts at.
121: * @param out the array the output data will be copied into.
122: * @param outOff the offset into the out array the output will start at.
123: * @exception DataLengthException if there isn't enough data in in, or
124: * space in out.
125: * @exception IllegalStateException if the cipher isn't initialised.
126: * @return the number of bytes processed and produced.
127: */
128: public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
129: throws DataLengthException, IllegalStateException {
130: if ((inOff + blockSize) > in.length) {
131: throw new DataLengthException("input buffer too short");
132: }
133:
134: if ((outOff + blockSize) > out.length) {
135: throw new DataLengthException("output buffer too short");
136: }
137:
138: if (firstStep) {
139: firstStep = false;
140: cipher.processBlock(ofbV, 0, ofbOutV, 0);
141: N3 = bytesToint(ofbOutV, 0);
142: N4 = bytesToint(ofbOutV, 4);
143: }
144: N3 += C2;
145: N4 += C1;
146: intTobytes(N3, ofbV, 0);
147: intTobytes(N4, ofbV, 4);
148:
149: cipher.processBlock(ofbV, 0, ofbOutV, 0);
150:
151: //
152: // XOR the ofbV with the plaintext producing the cipher text (and
153: // the next input block).
154: //
155: for (int i = 0; i < blockSize; i++) {
156: out[outOff + i] = (byte) (ofbOutV[i] ^ in[inOff + i]);
157: }
158:
159: //
160: // change over the input block.
161: //
162: System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length
163: - blockSize);
164: System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize,
165: blockSize);
166:
167: return blockSize;
168: }
169:
170: /**
171: * reset the feedback vector back to the IV and reset the underlying
172: * cipher.
173: */
174: public void reset() {
175: System.arraycopy(IV, 0, ofbV, 0, IV.length);
176:
177: cipher.reset();
178: }
179:
180: //array of bytes to type int
181: private int bytesToint(byte[] in, int inOff) {
182: return ((in[inOff + 3] << 24) & 0xff000000)
183: + ((in[inOff + 2] << 16) & 0xff0000)
184: + ((in[inOff + 1] << 8) & 0xff00) + (in[inOff] & 0xff);
185: }
186:
187: //int to array of bytes
188: private void intTobytes(int num, byte[] out, int outOff) {
189: out[outOff + 3] = (byte) (num >>> 24);
190: out[outOff + 2] = (byte) (num >>> 16);
191: out[outOff + 1] = (byte) (num >>> 8);
192: out[outOff] = (byte) num;
193: }
194: }
|