ICMGenerator.java 13 KB

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