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
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
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
;
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
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>
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>
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>
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>
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
;
145 // -------------------------------------------------------------------------
147 /** Trivial 0-arguments constructor. */
148 public ICMGenerator()
150 super(Registry
.ICM_PRNG
);
154 // -------------------------------------------------------------------------
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)
178 // ensure we have a reliable implementation of this cipher
179 cipher
= CipherFactory
.getInstance(Registry
.RIJNDAEL_CIPHER
);
182 { // we already have one. use it as is
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
);
196 cipherBlockSize
= bs
.intValue();
201 { // assume we'll use its default block size
202 cipherBlockSize
= cipher
.defaultBlockSize();
206 // get the key material
207 byte[] key
= (byte[]) attributes
.get(IBlockCipher
.KEY_MATERIAL
);
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
);
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
240 if (obj
instanceof BigInteger
)
242 r
= (BigInteger
) obj
;
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
);
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
);
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)
282 throw new IllegalArgumentException(BLOCK_INDEX_LENGTH
+ ", "
283 + SEGMENT_INDEX_LENGTH
);
284 } // else reuse old values
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
);
303 blockNdxLength
= wantBlockNdxLength
;
304 segmentNdxLength
= wantSegmentNdxLength
;
307 // get the segment index as a BigInteger
308 BigInteger s
= (BigInteger
) attributes
.get(SEGMENT_INDEX
);
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
);
323 if (s
.compareTo(TWO_FIFTY_SIX
.pow(segmentNdxLength
)) > 0)
325 throw new IllegalArgumentException(SEGMENT_INDEX
);
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(
340 public void fillBlock() throws LimitReachedException
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
);
366 else if (limit
> cipherBlockSize
)
368 byte[] data
= new byte[cipherBlockSize
];
369 System
.arraycopy(buffer
, limit
- cipherBlockSize
, data
, 0,
374 cipher
.encryptBlock(buffer
, 0, buffer
, 0);
375 blockNdx
= blockNdx
.add(BigInteger
.ONE
); // increment blockNdx