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