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 Segmented Integer Counter (SIC) mode on top of a simple
10 * block cipher. This mode is also known as CTR mode.
11 */
12public class SICBlockCipher implements BlockCipher
13{
14    private final BlockCipher     cipher;
15    private final int             blockSize;
16
17    private byte[]          IV;
18    private byte[]          counter;
19    private byte[]          counterOut;
20
21
22    /**
23     * Basic constructor.
24     *
25     * @param c the block cipher to be used.
26     */
27    public SICBlockCipher(BlockCipher c)
28    {
29        this.cipher = c;
30        this.blockSize = cipher.getBlockSize();
31        this.IV = new byte[blockSize];
32        this.counter = new byte[blockSize];
33        this.counterOut = new byte[blockSize];
34    }
35
36
37    /**
38     * return the underlying block cipher that we are wrapping.
39     *
40     * @return the underlying block cipher that we are wrapping.
41     */
42    public BlockCipher getUnderlyingCipher()
43    {
44        return cipher;
45    }
46
47
48    public void init(
49        boolean             forEncryption, //ignored by this CTR mode
50        CipherParameters    params)
51        throws IllegalArgumentException
52    {
53        if (params instanceof ParametersWithIV)
54        {
55          ParametersWithIV ivParam = (ParametersWithIV)params;
56          byte[]           iv      = ivParam.getIV();
57          System.arraycopy(iv, 0, IV, 0, IV.length);
58
59          reset();
60          cipher.init(true, ivParam.getParameters());
61        }
62        else
63        {
64            throw new IllegalArgumentException("SIC mode requires ParametersWithIV");
65        }
66    }
67
68    public String getAlgorithmName()
69    {
70        return cipher.getAlgorithmName() + "/SIC";
71    }
72
73    public int getBlockSize()
74    {
75        return cipher.getBlockSize();
76    }
77
78
79    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
80          throws DataLengthException, IllegalStateException
81    {
82        cipher.processBlock(counter, 0, counterOut, 0);
83
84        //
85        // XOR the counterOut with the plaintext producing the cipher text
86        //
87        for (int i = 0; i < counterOut.length; i++)
88        {
89          out[outOff + i] = (byte)(counterOut[i] ^ in[inOff + i]);
90        }
91
92        int    carry = 1;
93
94        for (int i = counter.length - 1; i >= 0; i--)
95        {
96            int    x = (counter[i] & 0xff) + carry;
97
98            if (x > 0xff)
99            {
100                carry = 1;
101            }
102            else
103            {
104                carry = 0;
105            }
106
107            counter[i] = (byte)x;
108        }
109
110        return counter.length;
111    }
112
113
114    public void reset()
115    {
116        System.arraycopy(IV, 0, counter, 0, counter.length);
117        cipher.reset();
118    }
119}
120