Imported GNU Classpath 0.90
[official-gcc.git] / libjava / classpath / gnu / javax / crypto / prng / ICMGenerator.java
blob7d4f4c9a32cf1a2d5a24b15d23096e81d5b48fee
1 /* ICMGenerator.java --
2 Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
4 This file is a part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 USA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.javax.crypto.prng;
41 import gnu.java.security.Registry;
42 import gnu.java.security.prng.BasePRNG;
43 import gnu.java.security.prng.LimitReachedException;
45 import gnu.javax.crypto.cipher.IBlockCipher;
46 import gnu.javax.crypto.cipher.CipherFactory;
48 import java.math.BigInteger;
49 import java.security.InvalidKeyException;
50 import java.util.HashMap;
51 import java.util.Map;
53 /**
54 * <p>Counter Mode is a way to define a pseudorandom keystream generator using
55 * a block cipher. The keystream can be used for additive encryption, key
56 * derivation, or any other application requiring pseudorandom data.</p>
58 * <p>In ICM, the keystream is logically broken into segments. Each segment is
59 * identified with a segment index, and the segments have equal lengths. This
60 * segmentation makes ICM especially appropriate for securing packet-based
61 * protocols.</p>
63 * <p>This implementation adheres to the definition of the ICM keystream
64 * generation function that allows for any symetric key block cipher algorithm
65 * (initialisation parameter <code>gnu.crypto.prng.icm.cipher.name</code> taken
66 * to be an instance of {@link java.lang.String}) to be used. If such a
67 * parameter is not defined/included in the initialisation <code>Map</code>,
68 * then the "Rijndael" algorithm is used. Furthermore, if the initialisation
69 * parameter <code>gnu.crypto.cipher.block.size</code> (taken to be a instance
70 * of {@link java.lang.Integer}) is missing or undefined in the initialisation
71 * <code>Map</code>, then the cipher's <em>default</em> block size is used.</p>
73 * <p>The practical limits and constraints of such generator are:</p>
74 * <ul>
75 * <li>The number of blocks in any segment <b>MUST NOT</b> exceed <code>
76 * 256 ** BLOCK_INDEX_LENGTH</code>. The number of segments <b>MUST NOT</b>
77 * exceed <code>256 ** SEGMENT_INDEX_LENGTH</code>. These restrictions ensure
78 * the uniqueness of each block cipher input.</li>
80 * <li>Each segment contains <code>SEGMENT_LENGTH</code> octets; this value
81 * <b>MUST NOT</b> exceed the value <code>(256 ** BLOCK_INDEX_LENGTH) *
82 * BLOCK_LENGTH</code>.</li>
84 * <li>The sum of <code>SEGMENT_INDEX_LENGTH</code> and
85 * <code>BLOCK_INDEX_LENGTH</code> <b>MUST NOT</b> exceed <code>BLOCK_LENGTH
86 * / 2</code>. This requirement protects the ICM keystream generator from
87 * potentially failing to be pseudorandom.</li>
88 * </ul>
90 * <p><b>NOTE</b>: Rijndael is used as the default symmetric key block cipher
91 * algorithm because, with its default block and key sizes, it is the AES. Yet
92 * being Rijndael, the algorithm offers more versatile block and key sizes which
93 * may prove to be useful for generating <em>longer</em> key streams.</p>
95 * <p>References:</p>
97 * <ol>
98 * <li><a href="http://www.ietf.org/internet-drafts/draft-mcgrew-saag-icm-00.txt">
99 * Integer Counter Mode</a>, David A. McGrew.</li>
100 * </ol>
102 public class ICMGenerator extends BasePRNG implements Cloneable
105 // Constants and variables
106 // -------------------------------------------------------------------------
108 /** Property name of underlying block cipher for this ICM generator. */
109 public static final String CIPHER = "gnu.crypto.prng.icm.cipher.name";
111 /** Property name of ICM's block index length. */
112 public static final String BLOCK_INDEX_LENGTH = "gnu.crypto.prng.icm.block.index.length";
114 /** Property name of ICM's segment index length. */
115 public static final String SEGMENT_INDEX_LENGTH = "gnu.crypto.prng.icm.segment.index.length";
117 /** Property name of ICM's offset. */
118 public static final String OFFSET = "gnu.crypto.prng.icm.offset";
120 /** Property name of ICM's segment index. */
121 public static final String SEGMENT_INDEX = "gnu.crypto.prng.icm.segment.index";
123 /** The integer value 256 as a BigInteger. */
124 private static final BigInteger TWO_FIFTY_SIX = new BigInteger("256");
126 /** The underlying cipher implementation. */
127 private IBlockCipher cipher;
129 /** This keystream block index length in bytes. */
130 private int blockNdxLength = -1;
132 /** This keystream segment index length in bytes. */
133 private int segmentNdxLength = -1;
135 /** The index of the next block for a given keystream segment. */
136 private BigInteger blockNdx = BigInteger.ZERO;
138 /** The segment index for this keystream. */
139 private BigInteger segmentNdx;
141 /** The initial counter for a given keystream segment. */
142 private BigInteger C0;
144 // Constructor(s)
145 // -------------------------------------------------------------------------
147 /** Trivial 0-arguments constructor. */
148 public ICMGenerator()
150 super(Registry.ICM_PRNG);
153 // Class methods
154 // -------------------------------------------------------------------------
156 // Instance methods
157 // -------------------------------------------------------------------------
159 // Implementation of abstract methods in BasePRNG --------------------------
161 // Conceptually, ICM is a keystream generator that takes a secret key
162 // and a segment index as an input and then outputs a keystream
163 // segment. The segmentation lends itself to packet encryption, as
164 // each keystream segment can be used to encrypt a distinct packet.
166 // An ICM key consists of the block cipher key and an Offset. The
167 // Offset is an integer with BLOCK_LENGTH octets...
169 public void setup(Map attributes)
171 // find out which cipher algorithm to use
172 boolean newCipher = true;
173 String underlyingCipher = (String) attributes.get(CIPHER);
174 if (underlyingCipher == null)
176 if (cipher == null)
177 { // happy birthday
178 // ensure we have a reliable implementation of this cipher
179 cipher = CipherFactory.getInstance(Registry.RIJNDAEL_CIPHER);
181 else
182 { // we already have one. use it as is
183 newCipher = false;
186 else
187 { // ensure we have a reliable implementation of this cipher
188 cipher = CipherFactory.getInstance(underlyingCipher);
191 // find out what block size we should use it in
192 int cipherBlockSize = 0;
193 Integer bs = (Integer) attributes.get(IBlockCipher.CIPHER_BLOCK_SIZE);
194 if (bs != null)
196 cipherBlockSize = bs.intValue();
198 else
200 if (newCipher)
201 { // assume we'll use its default block size
202 cipherBlockSize = cipher.defaultBlockSize();
203 } // else use as is
206 // get the key material
207 byte[] key = (byte[]) attributes.get(IBlockCipher.KEY_MATERIAL);
208 if (key == null)
210 throw new IllegalArgumentException(IBlockCipher.KEY_MATERIAL);
213 // now initialise the cipher
214 HashMap map = new HashMap();
215 if (cipherBlockSize != 0)
216 { // only needed if new or changed
217 map.put(IBlockCipher.CIPHER_BLOCK_SIZE, new Integer(cipherBlockSize));
219 map.put(IBlockCipher.KEY_MATERIAL, key);
222 cipher.init(map);
224 catch (InvalidKeyException x)
226 throw new IllegalArgumentException(IBlockCipher.KEY_MATERIAL);
229 // at this point we have an initialised (new or otherwise) cipher
230 // ensure that remaining params make sense
232 cipherBlockSize = cipher.currentBlockSize();
233 BigInteger counterRange = TWO_FIFTY_SIX.pow(cipherBlockSize);
235 // offset, like the underlying cipher key is not cloneable
236 // always look for it and throw an exception if it's not there
237 Object obj = attributes.get(OFFSET);
238 // allow either a byte[] or a BigInteger
239 BigInteger r;
240 if (obj instanceof BigInteger)
242 r = (BigInteger) obj;
244 else
245 { // assume byte[]. should be same length as cipher block size
246 byte[] offset = (byte[]) obj;
247 if (offset.length != cipherBlockSize)
249 throw new IllegalArgumentException(OFFSET);
252 r = new BigInteger(1, offset);
255 int wantBlockNdxLength = -1; // number of octets in the block index
256 Integer i = (Integer) attributes.get(BLOCK_INDEX_LENGTH);
257 if (i != null)
259 wantBlockNdxLength = i.intValue();
260 if (wantBlockNdxLength < 1)
262 throw new IllegalArgumentException(BLOCK_INDEX_LENGTH);
266 int wantSegmentNdxLength = -1; // number of octets in the segment index
267 i = (Integer) attributes.get(SEGMENT_INDEX_LENGTH);
268 if (i != null)
270 wantSegmentNdxLength = i.intValue();
271 if (wantSegmentNdxLength < 1)
273 throw new IllegalArgumentException(SEGMENT_INDEX_LENGTH);
277 // if both are undefined check if it's a reuse
278 if ((wantBlockNdxLength == -1) && (wantSegmentNdxLength == -1))
280 if (blockNdxLength == -1)
281 { // new instance
282 throw new IllegalArgumentException(BLOCK_INDEX_LENGTH + ", "
283 + SEGMENT_INDEX_LENGTH);
284 } // else reuse old values
286 else
287 { // only one is undefined, set it to BLOCK_LENGTH/2 minus the other
288 int limit = cipherBlockSize / 2;
289 if (wantBlockNdxLength == -1)
291 wantBlockNdxLength = limit - wantSegmentNdxLength;
293 else if (wantSegmentNdxLength == -1)
295 wantSegmentNdxLength = limit - wantBlockNdxLength;
297 else if ((wantSegmentNdxLength + wantBlockNdxLength) > limit)
299 throw new IllegalArgumentException(BLOCK_INDEX_LENGTH + ", "
300 + SEGMENT_INDEX_LENGTH);
302 // save new values
303 blockNdxLength = wantBlockNdxLength;
304 segmentNdxLength = wantSegmentNdxLength;
307 // get the segment index as a BigInteger
308 BigInteger s = (BigInteger) attributes.get(SEGMENT_INDEX);
309 if (s == null)
311 if (segmentNdx == null)
312 { // segment index was never set
313 throw new IllegalArgumentException(SEGMENT_INDEX);
315 // reuse; check if still valid
316 if (segmentNdx.compareTo(TWO_FIFTY_SIX.pow(segmentNdxLength)) > 0)
318 throw new IllegalArgumentException(SEGMENT_INDEX);
321 else
323 if (s.compareTo(TWO_FIFTY_SIX.pow(segmentNdxLength)) > 0)
325 throw new IllegalArgumentException(SEGMENT_INDEX);
327 segmentNdx = s;
330 // The initial counter of the keystream segment with segment index s is
331 // defined as follows, where r denotes the Offset:
333 // C[0] = (s * (256^BLOCK_INDEX_LENGTH) + r) modulo (256^BLOCK_LENGTH)
335 C0 = segmentNdx.multiply(TWO_FIFTY_SIX.pow(blockNdxLength)).add(r).modPow(
336 BigInteger.ONE,
337 counterRange);
340 public void fillBlock() throws LimitReachedException
342 if (C0 == null)
344 throw new IllegalStateException();
346 if (blockNdx.compareTo(TWO_FIFTY_SIX.pow(blockNdxLength)) >= 0)
348 throw new LimitReachedException();
351 int cipherBlockSize = cipher.currentBlockSize();
352 BigInteger counterRange = TWO_FIFTY_SIX.pow(cipherBlockSize);
354 // encrypt the counter for the current blockNdx
355 // C[i] = (C[0] + i) modulo (256^BLOCK_LENGTH).
357 BigInteger Ci = C0.add(blockNdx).modPow(BigInteger.ONE, counterRange);
358 buffer = Ci.toByteArray();
359 int limit = buffer.length;
360 if (limit < cipherBlockSize)
362 byte[] data = new byte[cipherBlockSize];
363 System.arraycopy(buffer, 0, data, cipherBlockSize - limit, limit);
364 buffer = data;
366 else if (limit > cipherBlockSize)
368 byte[] data = new byte[cipherBlockSize];
369 System.arraycopy(buffer, limit - cipherBlockSize, data, 0,
370 cipherBlockSize);
371 buffer = data;
374 cipher.encryptBlock(buffer, 0, buffer, 0);
375 blockNdx = blockNdx.add(BigInteger.ONE); // increment blockNdx