• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.bouncycastle.crypto.modes;
2 
3 import org.bouncycastle.crypto.BlockCipher;
4 import org.bouncycastle.crypto.CipherParameters;
5 import org.bouncycastle.crypto.DataLengthException;
6 import org.bouncycastle.crypto.SkippingStreamCipher;
7 import org.bouncycastle.crypto.StreamBlockCipher;
8 import org.bouncycastle.crypto.params.ParametersWithIV;
9 import org.bouncycastle.util.Arrays;
10 import org.bouncycastle.util.Pack;
11 
12 /**
13  * Implements the Segmented Integer Counter (SIC) mode on top of a simple
14  * block cipher. This mode is also known as CTR mode.
15  */
16 public class SICBlockCipher
17     extends StreamBlockCipher
18     implements SkippingStreamCipher
19 {
20     private final BlockCipher     cipher;
21     private final int             blockSize;
22 
23     private byte[]          IV;
24     private byte[]          counter;
25     private byte[]          counterOut;
26     private int             byteCount;
27 
28     /**
29      * Basic constructor.
30      *
31      * @param c the block cipher to be used.
32      */
SICBlockCipher(BlockCipher c)33     public SICBlockCipher(BlockCipher c)
34     {
35         super(c);
36 
37         this.cipher = c;
38         this.blockSize = cipher.getBlockSize();
39         this.IV = new byte[blockSize];
40         this.counter = new byte[blockSize];
41         this.counterOut = new byte[blockSize];
42         this.byteCount = 0;
43     }
44 
init( boolean forEncryption, CipherParameters params)45     public void init(
46         boolean             forEncryption, //ignored by this CTR mode
47         CipherParameters    params)
48         throws IllegalArgumentException
49     {
50         if (params instanceof ParametersWithIV)
51         {
52             ParametersWithIV ivParam = (ParametersWithIV)params;
53             this.IV = Arrays.clone(ivParam.getIV());
54 
55             if (blockSize < IV.length)
56             {
57                 throw new IllegalArgumentException("CTR/SIC mode requires IV no greater than: " + blockSize + " bytes.");
58             }
59 
60             int maxCounterSize = (8 > blockSize / 2) ? blockSize / 2 : 8;
61 
62             if (blockSize - IV.length > maxCounterSize)
63             {
64                 throw new IllegalArgumentException("CTR/SIC mode requires IV of at least: " + (blockSize - maxCounterSize) + " bytes.");
65             }
66 
67             // if null it's an IV changed only.
68             if (ivParam.getParameters() != null)
69             {
70                 cipher.init(true, ivParam.getParameters());
71             }
72 
73             reset();
74         }
75         else
76         {
77             throw new IllegalArgumentException("CTR/SIC mode requires ParametersWithIV");
78         }
79     }
80 
getAlgorithmName()81     public String getAlgorithmName()
82     {
83         return cipher.getAlgorithmName() + "/SIC";
84     }
85 
getBlockSize()86     public int getBlockSize()
87     {
88         return cipher.getBlockSize();
89     }
90 
processBlock(byte[] in, int inOff, byte[] out, int outOff)91     public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
92           throws DataLengthException, IllegalStateException
93     {
94         processBytes(in, inOff, blockSize, out, outOff);
95 
96         return blockSize;
97     }
98 
calculateByte(byte in)99     protected byte calculateByte(byte in)
100           throws DataLengthException, IllegalStateException
101     {
102         if (byteCount == 0)
103         {
104             cipher.processBlock(counter, 0, counterOut, 0);
105 
106             return (byte)(counterOut[byteCount++] ^ in);
107         }
108 
109         byte rv = (byte)(counterOut[byteCount++] ^ in);
110 
111         if (byteCount == counter.length)
112         {
113             byteCount = 0;
114 
115             incrementCounterAt(0);
116 
117             checkCounter();
118         }
119 
120         return rv;
121     }
122 
checkCounter()123     private void checkCounter()
124     {
125         // if the IV is the same as the blocksize we assume the user knows what they are doing
126         if (IV.length < blockSize)
127         {
128             for (int i = 0; i != IV.length; i++)
129             {
130                 if (counter[i] != IV[i])
131                 {
132                     throw new IllegalStateException("Counter in CTR/SIC mode out of range.");
133                 }
134             }
135         }
136     }
137 
incrementCounterAt(int pos)138     private void incrementCounterAt(int pos)
139     {
140         int i = counter.length - pos;
141         while (--i >= 0)
142         {
143             if (++counter[i] != 0)
144             {
145                 break;
146             }
147         }
148     }
149 
incrementCounter(int offSet)150     private void incrementCounter(int offSet)
151     {
152         byte old = counter[counter.length - 1];
153 
154         counter[counter.length - 1] += offSet;
155 
156         if (old != 0 && counter[counter.length - 1] < old)
157         {
158             incrementCounterAt(1);
159         }
160     }
161 
decrementCounterAt(int pos)162     private void decrementCounterAt(int pos)
163     {
164         int i = counter.length - pos;
165         while (--i >= 0)
166         {
167             if (--counter[i] != -1)
168             {
169                 return;
170             }
171         }
172     }
173 
adjustCounter(long n)174     private void adjustCounter(long n)
175     {
176         if (n >= 0)
177         {
178             long numBlocks = (n + byteCount) / blockSize;
179 
180             long rem = numBlocks;
181             if (rem > 255)
182             {
183                 for (int i = 5; i >= 1; i--)
184                 {
185                     long diff = 1L << (8 * i);
186                     while (rem >= diff)
187                     {
188                         incrementCounterAt(i);
189                         rem -= diff;
190                     }
191                 }
192             }
193 
194             incrementCounter((int)rem);
195 
196             byteCount = (int)((n + byteCount) - (blockSize * numBlocks));
197         }
198         else
199         {
200             long numBlocks = (-n - byteCount) / blockSize;
201 
202             long rem = numBlocks;
203             if (rem > 255)
204             {
205                 for (int i = 5; i >= 1; i--)
206                 {
207                     long diff = 1L << (8 * i);
208                     while (rem > diff)
209                     {
210                         decrementCounterAt(i);
211                         rem -= diff;
212                     }
213                 }
214             }
215 
216             for (long i = 0; i != rem; i++)
217             {
218                 decrementCounterAt(0);
219             }
220 
221             int gap = (int)(byteCount + n + (blockSize * numBlocks));
222 
223             if (gap >= 0)
224             {
225                 byteCount = 0;
226             }
227             else
228             {
229                 decrementCounterAt(0);
230                 byteCount =  blockSize + gap;
231             }
232         }
233     }
234 
reset()235     public void reset()
236     {
237         Arrays.fill(counter, (byte)0);
238         System.arraycopy(IV, 0, counter, 0, IV.length);
239         cipher.reset();
240         this.byteCount = 0;
241     }
242 
skip(long numberOfBytes)243     public long skip(long numberOfBytes)
244     {
245         adjustCounter(numberOfBytes);
246 
247         checkCounter();
248 
249         cipher.processBlock(counter, 0, counterOut, 0);
250 
251         return numberOfBytes;
252     }
253 
seekTo(long position)254     public long seekTo(long position)
255     {
256         reset();
257 
258         return skip(position);
259     }
260 
getPosition()261     public long getPosition()
262     {
263         byte[] res = new byte[counter.length];
264 
265         System.arraycopy(counter, 0, res, 0, res.length);
266 
267         for (int i = res.length - 1; i >= 1; i--)
268         {
269             int v;
270             if (i < IV.length)
271             {
272                 v = (res[i] & 0xff) - (IV[i] & 0xff);
273             }
274             else
275             {
276                 v = (res[i] & 0xff);
277             }
278 
279             if (v < 0)
280             {
281                res[i - 1]--;
282                v += 256;
283             }
284 
285             res[i] = (byte)v;
286         }
287 
288         return Pack.bigEndianToLong(res, res.length - 8) * blockSize + byteCount;
289     }
290 }
291