From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- .../classpath/gnu/javax/crypto/prng/ARCFour.java | 137 +++ .../classpath/gnu/javax/crypto/prng/CSPRNG.java | 985 +++++++++++++++++++++ .../classpath/gnu/javax/crypto/prng/Fortuna.java | 349 ++++++++ .../gnu/javax/crypto/prng/ICMGenerator.java | 306 +++++++ libjava/classpath/gnu/javax/crypto/prng/IPBE.java | 81 ++ .../classpath/gnu/javax/crypto/prng/PBKDF2.java | 184 ++++ .../gnu/javax/crypto/prng/PRNGFactory.java | 115 +++ .../gnu/javax/crypto/prng/UMacGenerator.java | 186 ++++ 8 files changed, 2343 insertions(+) create mode 100644 libjava/classpath/gnu/javax/crypto/prng/ARCFour.java create mode 100644 libjava/classpath/gnu/javax/crypto/prng/CSPRNG.java create mode 100644 libjava/classpath/gnu/javax/crypto/prng/Fortuna.java create mode 100644 libjava/classpath/gnu/javax/crypto/prng/ICMGenerator.java create mode 100644 libjava/classpath/gnu/javax/crypto/prng/IPBE.java create mode 100644 libjava/classpath/gnu/javax/crypto/prng/PBKDF2.java create mode 100644 libjava/classpath/gnu/javax/crypto/prng/PRNGFactory.java create mode 100644 libjava/classpath/gnu/javax/crypto/prng/UMacGenerator.java (limited to 'libjava/classpath/gnu/javax/crypto/prng') diff --git a/libjava/classpath/gnu/javax/crypto/prng/ARCFour.java b/libjava/classpath/gnu/javax/crypto/prng/ARCFour.java new file mode 100644 index 000000000..60464d5ba --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/prng/ARCFour.java @@ -0,0 +1,137 @@ +/* ARCFour.java -- + Copyright (C) 2002, 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.prng; + +import gnu.java.security.Registry; +import gnu.java.security.prng.BasePRNG; +import gnu.java.security.prng.LimitReachedException; + +import java.util.Map; + +/** + * RC4 is a stream cipher developed by Ron Rivest. Until 1994 RC4 was a trade + * secret of RSA Data Security, Inc., when it was released anonymously to a + * mailing list. This version is a descendent of that code, and since there is + * no proof that the leaked version was in fact RC4 and because "RC4" is a + * trademark, it is called "ARCFOUR", short for "Allegedly RC4". + *

+ * This class only implements the keystream of ARCFOUR. To use this as a + * stream cipher, one would say: + *

+ * out = in ˆ arcfour.nextByte();
+ * 
+ *

+ * This operation works for encryption and decryption. + *

+ * References: + *

    + *
  1. Schneier, Bruce: Applied Cryptography: Protocols, Algorithms, and + * Source Code in C, Second Edition. (1996 John Wiley and Sons), pp. + * 397--398. ISBN 0-471-11709-9
  2. + *
  3. K. Kaukonen and R. Thayer, "A Stream Cipher Encryption Algorithm + * 'Arcfour'", Internet Draft (expired), draft-kaukonen-cipher-arcfour-03.txt
  4. + *
+ */ +public class ARCFour + extends BasePRNG + implements Cloneable +{ + /** The attributes property name for the key bytes. */ + public static final String ARCFOUR_KEY_MATERIAL = "gnu.crypto.prng.arcfour.key-material"; + /** The size of the internal S-box. */ + public static final int ARCFOUR_SBOX_SIZE = 256; + /** The S-box. */ + private byte[] s; + private byte m, n; + + /** Default 0-arguments constructor. */ + public ARCFour() + { + super(Registry.ARCFOUR_PRNG); + } + + public void setup(Map attributes) + { + byte[] kb = (byte[]) attributes.get(ARCFOUR_KEY_MATERIAL); + if (kb == null) + throw new IllegalArgumentException("ARCFOUR needs a key"); + s = new byte[ARCFOUR_SBOX_SIZE]; + m = n = 0; + byte[] k = new byte[ARCFOUR_SBOX_SIZE]; + for (int i = 0; i < ARCFOUR_SBOX_SIZE; i++) + s[i] = (byte) i; + if (kb.length > 0) + for (int i = 0, j = 0; i < ARCFOUR_SBOX_SIZE; i++) + { + k[i] = kb[j++]; + if (j >= kb.length) + j = 0; + } + for (int i = 0, j = 0; i < ARCFOUR_SBOX_SIZE; i++) + { + j = j + s[i] + k[i]; + byte temp = s[i]; + s[i] = s[j & 0xff]; + s[j & 0xff] = temp; + } + buffer = new byte[ARCFOUR_SBOX_SIZE]; + try + { + fillBlock(); + } + catch (LimitReachedException wontHappen) + { + } + } + + public void fillBlock() throws LimitReachedException + { + for (int i = 0; i < buffer.length; i++) + { + m++; + n = (byte)(n + s[m & 0xff]); + byte temp = s[m & 0xff]; + s[m & 0xff] = s[n & 0xff]; + s[n & 0xff] = temp; + temp = (byte)(s[m & 0xff] + s[n & 0xff]); + buffer[i] = s[temp & 0xff]; + } + } +} diff --git a/libjava/classpath/gnu/javax/crypto/prng/CSPRNG.java b/libjava/classpath/gnu/javax/crypto/prng/CSPRNG.java new file mode 100644 index 000000000..ecea2f469 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/prng/CSPRNG.java @@ -0,0 +1,985 @@ +/* CSPRNG.java -- continuously-seeded pseudo-random number generator. + Copyright (C) 2004, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.prng; + +import gnu.java.security.Configuration; +import gnu.java.security.Properties; +import gnu.java.security.Registry; +import gnu.java.security.hash.HashFactory; +import gnu.java.security.hash.IMessageDigest; +import gnu.java.security.prng.BasePRNG; +import gnu.java.security.prng.EntropySource; +import gnu.java.security.prng.IRandom; +import gnu.java.security.prng.LimitReachedException; +import gnu.java.security.util.SimpleList; +import gnu.java.security.util.Util; +import gnu.javax.crypto.cipher.CipherFactory; +import gnu.javax.crypto.cipher.IBlockCipher; + +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.AccessController; +import java.security.InvalidKeyException; +import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * An entropy pool-based pseudo-random number generator based on the PRNG in + * Peter Gutmann's cryptlib (http://www.cs.auckland.ac.nz/~pgut001/cryptlib/). + *

+ * The basic properties of this generator are: + *

    + *
  1. The internal state cannot be determined by knowledge of the input.
  2. + *
  3. It is resistant to bias introduced by specific inputs.
  4. + *
  5. The output does not reveal the state of the generator.
  6. + *
+ */ +public class CSPRNG + extends BasePRNG +{ + private static final Logger log = Logger.getLogger(CSPRNG.class.getName()); + /** + * Property name for the list of files to read for random values. The mapped + * value is a list with the following values: + *
    + *
  1. A {@link Double}, indicating the suggested quality of this + * source. This value must be between 0 and 100.
  2. + *
  3. An {@link Integer}, indicating the number of bytes to skip in the + * file before reading bytes. This can be any nonnegative value.
  4. + *
  5. An {@link Integer}, indicating the number of bytes to read.
  6. + *
  7. A {@link String}, indicating the path to the file.
  8. + *
+ * + * @see gnu.java.security.util.SimpleList + */ + public static final String FILE_SOURCES = "gnu.crypto.prng.pool.files"; + /** + * Property name for the list of URLs to poll for random values. The mapped + * value is a list formatted similarly as in {@link #FILE_SOURCES}, but the + * fourth member is a {@link URL}. + */ + public static final String URL_SOURCES = "gnu.crypto.prng.pool.urls"; + /** + * Property name for the list of programs to execute, and use the output as + * new random bytes. The mapped property is formatted similarly an in + * {@link #FILE_SOURCES} and {@link #URL_SOURCES}, except the fourth member + * is a {@link String} of the program to execute. + */ + public static final String PROGRAM_SOURCES = "gnu.crypto.prng.pool.programs"; + /** + * Property name for a list of other sources of entropy. The mapped value must + * be a list of {@link EntropySource} objects. + */ + public static final String OTHER_SOURCES = "gnu.crypto.prng.pool.other"; + /** + * Property name for whether or not to wait for the slow poll to complete, + * passed as a {@link Boolean}. The default value is true. + */ + public static final String BLOCKING = "gnu.crypto.prng.pool.blocking"; + private static final String FILES = "gnu.crypto.csprng.file."; + private static final String URLS = "gnu.crypto.csprng.url."; + private static final String PROGS = "gnu.crypto.csprng.program."; + private static final String OTHER = "gnu.crypto.csprng.other."; + private static final String BLOCK = "gnu.crypto.csprng.blocking"; + private static final int POOL_SIZE = 256; + private static final int ALLOC_SIZE = 260; + private static final int OUTPUT_SIZE = POOL_SIZE / 2; + private static final int X917_POOL_SIZE = 16; + private static final String HASH_FUNCTION = Registry.SHA160_HASH; + private static final String CIPHER = Registry.AES_CIPHER; + private static final int MIX_COUNT = 10; + private static final int X917_LIFETIME = 8192; + // FIXME this should be configurable. + private static final int SPINNER_COUNT = 8; + /** + * The spinner group singleton. We use this to add a small amount of + * randomness (in addition to the current time and the amount of free memory) + * based on the randomness (if any) present due to system load and thread + * scheduling. + */ + private static final Spinner[] SPINNERS = new Spinner[SPINNER_COUNT]; + private static final Thread[] SPINNER_THREADS = new Thread[SPINNER_COUNT]; + static + { + for (int i = 0; i < SPINNER_COUNT; i++) + { + SPINNER_THREADS[i] = new Thread(SPINNERS[i] = new Spinner(), + "spinner-" + i); + SPINNER_THREADS[i].setDaemon(true); + SPINNER_THREADS[i].setPriority(Thread.MIN_PRIORITY); + SPINNER_THREADS[i].start(); + } + } + /** The message digest (SHA-1) used in the mixing function. */ + private final IMessageDigest hash; + /** The cipher (AES) used in the output masking function. */ + private final IBlockCipher cipher; + /** The number of times the pool has been mixed. */ + private int mixCount; + /** The entropy pool. */ + private final byte[] pool; + /** The quality of the random pool (percentage). */ + private double quality; + /** The index of the next byte in the entropy pool. */ + private int index; + /** The pool for the X9.17-like generator. */ + private byte[] x917pool; + /** The number of iterations of the X9.17-like generators. */ + private int x917count; + /** Whether or not the X9.17-like generator is initialized. */ + private boolean x917init; + /** The list of file soures. */ + private final List files; + /** The list of URL sources. */ + private final List urls; + /** The list of program sources. */ + private final List progs; + /** The list of other sources. */ + private final List other; + /** Whether or not to wait for the slow poll to complete. */ + private boolean blocking; + /** The thread that polls for random data. */ + private Poller poller; + private Thread pollerThread; + + public CSPRNG() + { + super("CSPRNG"); + pool = new byte[ALLOC_SIZE]; + x917pool = new byte[X917_POOL_SIZE]; + x917count = 0; + x917init = false; + quality = 0.0; + hash = HashFactory.getInstance(HASH_FUNCTION); + cipher = CipherFactory.getInstance(CIPHER); + buffer = new byte[OUTPUT_SIZE]; + ndx = 0; + initialised = false; + files = new LinkedList(); + urls = new LinkedList(); + progs = new LinkedList(); + other = new LinkedList(); + } + + /** + * Create and initialize a CSPRNG instance with the "system" parameters; the + * files, URLs, programs, and {@link EntropySource} sources used by the + * instance are derived from properties set in the system {@link Properties}. + *

+ * All properties are of the from name.N, where name + * is the name of the source, and N is an integer (staring at 1) that + * indicates the preference number for that source. + *

+ * The following vales for name are used here: + *

+ *
gnu.crypto.csprng.file
+ *
+ *

+ * These properties are file sources, passed as the {@link #FILE_SOURCES} + * parameter of the instance. The property value is a 4-tuple formatted as: + *

+ *
quality ; offset ; count ; path
+ *

+ * The parameters are mapped to the parameters defined for {@link + * #FILE_SOURCES}. Leading or trailing spaces on any item are trimmed off. + *

+ *
+ *
gnu.crypto.csprng.url
+ *
+ *

+ * These properties are URL sources, passed as the {@link #URL_SOURCES} + * parameter of the instance. The property is formatted the same way as file + * sources, but the path argument must be a valid URL. + *

+ *
+ *
gnu.crypto.csprng.program
+ *
+ *

+ * These properties are program sources, passed as the {@link + * #PROGRAM_SOURCES} parameter of the instance. This property is formatted the + * same way as file and URL sources, but the last argument is a program and + * its arguments. + *

+ *
+ *
gnu.crypto.cspring.other
+ *
+ *

+ * These properties are other sources, passed as the {@link #OTHER_SOURCES} + * parameter of the instance. The property value must be the full name of a + * class that implements the {@link EntropySource} interface and has a public + * no-argument constructor. + *

+ *
+ *
+ *

+ * Finally, a boolean property "gnu.crypto.csprng.blocking" can be set to the + * desired value of {@link #BLOCKING}. + *

+ * An example of valid properties would be: + *

+   *  gnu.crypto.csprng.blocking=true
+   *
+   *  gnu.crypto.csprng.file.1=75.0;0;256;/dev/random
+   *  gnu.crypto.csprng.file.2=10.0;0;100;/home/user/file
+   *
+   *  gnu.crypto.csprng.url.1=5.0;0;256;http://www.random.org/cgi-bin/randbyte?nbytes=256
+   *  gnu.crypto.csprng.url.2=0;256;256;http://slashdot.org/
+   *
+   *  gnu.crypto.csprng.program.1=0.5;0;10;last -n 50
+   *  gnu.crypto.csprng.program.2=0.5;0;10;tcpdump -c 5
+   *
+   *  gnu.crypto.csprng.other.1=foo.bar.MyEntropySource
+   *  gnu.crypto.csprng.other.2=com.company.OtherEntropySource
+   * 
+ */ + public static IRandom getSystemInstance() throws ClassNotFoundException, + MalformedURLException, NumberFormatException + { + CSPRNG instance = new CSPRNG(); + HashMap attrib = new HashMap(); + attrib.put(BLOCKING, Boolean.valueOf(getProperty(BLOCK))); + String s = null; + // Get each file source "gnu.crypto.csprng.file.N". + List l = new LinkedList(); + for (int i = 0; (s = getProperty(FILES + i)) != null; i++) + try + { + l.add(parseString(s.trim())); + } + catch (NumberFormatException nfe) + { + } + attrib.put(FILE_SOURCES, l); + l = new LinkedList(); + for (int i = 0; (s = getProperty(URLS + i)) != null; i++) + try + { + l.add(parseURL(s.trim())); + } + catch (NumberFormatException nfe) + { + } + catch (MalformedURLException mue) + { + } + attrib.put(URL_SOURCES, l); + l = new LinkedList(); + for (int i = 0; (s = getProperty(PROGS + i)) != null; i++) + try + { + l.add(parseString(s.trim())); + } + catch (NumberFormatException nfe) + { + } + attrib.put(PROGRAM_SOURCES, l); + l = new LinkedList(); + for (int i = 0; (s = getProperty(OTHER + i)) != null; i++) + try + { + Class c = Class.forName(s.trim()); + l.add(c.newInstance()); + } + catch (ClassNotFoundException cnfe) + { + } + catch (InstantiationException ie) + { + } + catch (IllegalAccessException iae) + { + } + attrib.put(OTHER_SOURCES, l); + instance.init(attrib); + return instance; + } + + private static String getProperty(final String name) + { + return (String) AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + return Properties.getProperty(name); + } + }); + } + + private static List parseString(String s) throws NumberFormatException + { + StringTokenizer tok = new StringTokenizer(s, ";"); + if (tok.countTokens() != 4) + throw new IllegalArgumentException("malformed property"); + Double quality = new Double(tok.nextToken()); + Integer offset = new Integer(tok.nextToken()); + Integer length = new Integer(tok.nextToken()); + String str = tok.nextToken(); + return new SimpleList(quality, offset, length, str); + } + + private static List parseURL(String s) throws MalformedURLException, + NumberFormatException + { + StringTokenizer tok = new StringTokenizer(s, ";"); + if (tok.countTokens() != 4) + throw new IllegalArgumentException("malformed property"); + Double quality = new Double(tok.nextToken()); + Integer offset = new Integer(tok.nextToken()); + Integer length = new Integer(tok.nextToken()); + URL url = new URL(tok.nextToken()); + return new SimpleList(quality, offset, length, url); + } + + public Object clone() + { + return new CSPRNG(); + } + + public void setup(Map attrib) + { + List list = null; + if (Configuration.DEBUG) + log.fine("attrib=" + String.valueOf(attrib)); + try + { + list = (List) attrib.get(FILE_SOURCES); + if (Configuration.DEBUG) + log.fine("list=" + String.valueOf(list)); + if (list != null) + { + files.clear(); + for (Iterator it = list.iterator(); it.hasNext();) + { + List l = (List) it.next(); + if (Configuration.DEBUG) + log.fine("l=" + l); + if (l.size() != 4) + { + if (Configuration.DEBUG) + log.fine("file list too small: " + l.size()); + throw new IllegalArgumentException("invalid file list"); + } + Double quality = (Double) l.get(0); + Integer offset = (Integer) l.get(1); + Integer length = (Integer) l.get(2); + String source = (String) l.get(3); + files.add(new SimpleList(quality, offset, length, source)); + } + } + } + catch (ClassCastException cce) + { + if (Configuration.DEBUG) + log.log(Level.FINE, "bad file list", cce); + throw new IllegalArgumentException("invalid file list"); + } + try + { + list = (List) attrib.get(URL_SOURCES); + if (Configuration.DEBUG) + log.fine("list=" + String.valueOf(list)); + if (list != null) + { + urls.clear(); + for (Iterator it = list.iterator(); it.hasNext();) + { + List l = (List) it.next(); + if (Configuration.DEBUG) + log.fine("l=" + l); + if (l.size() != 4) + { + if (Configuration.DEBUG) + log.fine("URL list too small: " + l.size()); + throw new IllegalArgumentException("invalid URL list"); + } + Double quality = (Double) l.get(0); + Integer offset = (Integer) l.get(1); + Integer length = (Integer) l.get(2); + URL source = (URL) l.get(3); + urls.add(new SimpleList(quality, offset, length, source)); + } + } + } + catch (ClassCastException cce) + { + if (Configuration.DEBUG) + log.log(Level.FINE, "bad URL list", cce); + throw new IllegalArgumentException("invalid URL list"); + } + try + { + list = (List) attrib.get(PROGRAM_SOURCES); + if (Configuration.DEBUG) + log.fine("list=" + String.valueOf(list)); + if (list != null) + { + progs.clear(); + for (Iterator it = list.iterator(); it.hasNext();) + { + List l = (List) it.next(); + if (Configuration.DEBUG) + log.fine("l=" + l); + if (l.size() != 4) + { + if (Configuration.DEBUG) + log.fine("program list too small: " + l.size()); + throw new IllegalArgumentException("invalid program list"); + } + Double quality = (Double) l.get(0); + Integer offset = (Integer) l.get(1); + Integer length = (Integer) l.get(2); + String source = (String) l.get(3); + progs.add(new SimpleList(quality, offset, length, source)); + } + } + } + catch (ClassCastException cce) + { + if (Configuration.DEBUG) + log.log(Level.FINE, "bad program list", cce); + throw new IllegalArgumentException("invalid program list"); + } + try + { + list = (List) attrib.get(OTHER_SOURCES); + if (Configuration.DEBUG) + log.fine("list=" + String.valueOf(list)); + if (list != null) + { + other.clear(); + for (Iterator it = list.iterator(); it.hasNext();) + { + EntropySource src = (EntropySource) it.next(); + if (Configuration.DEBUG) + log.fine("src=" + src); + if (src == null) + throw new NullPointerException("null source in source list"); + other.add(src); + } + } + } + catch (ClassCastException cce) + { + throw new IllegalArgumentException("invalid source list"); + } + + try + { + Boolean block = (Boolean) attrib.get(BLOCKING); + if (block != null) + blocking = block.booleanValue(); + else + blocking = true; + } + catch (ClassCastException cce) + { + throw new IllegalArgumentException("invalid blocking parameter"); + } + poller = new Poller(files, urls, progs, other, this); + try + { + fillBlock(); + } + catch (LimitReachedException lre) + { + throw new RuntimeException("bootstrapping CSPRNG failed"); + } + } + + public void fillBlock() throws LimitReachedException + { + if (Configuration.DEBUG) + log.fine("fillBlock"); + if (getQuality() < 100.0) + { + if (Configuration.DEBUG) + log.fine("doing slow poll"); + slowPoll(); + } + do + { + fastPoll(); + mixRandomPool(); + } + while (mixCount < MIX_COUNT); + if (! x917init || x917count >= X917_LIFETIME) + { + mixRandomPool(pool); + Map attr = new HashMap(); + byte[] key = new byte[32]; + System.arraycopy(pool, 0, key, 0, 32); + cipher.reset(); + attr.put(IBlockCipher.KEY_MATERIAL, key); + try + { + cipher.init(attr); + } + catch (InvalidKeyException ike) + { + throw new Error(ike.toString()); + } + mixRandomPool(pool); + generateX917(pool); + mixRandomPool(pool); + generateX917(pool); + if (x917init) + quality = 0.0; + x917init = true; + x917count = 0; + } + byte[] export = new byte[ALLOC_SIZE]; + for (int i = 0; i < ALLOC_SIZE; i++) + export[i] = (byte)(pool[i] ^ 0xFF); + mixRandomPool(); + mixRandomPool(export); + generateX917(export); + for (int i = 0; i < OUTPUT_SIZE; i++) + buffer[i] = (byte)(export[i] ^ export[i + OUTPUT_SIZE]); + Arrays.fill(export, (byte) 0); + } + + /** + * Add an array of bytes into the randomness pool. Note that this method will + * not increment the pool's quality counter (this can only be done via + * a source provided to the setup method). + * + * @param buf The byte array. + * @param off The offset from whence to start reading bytes. + * @param len The number of bytes to add. + * @throws ArrayIndexOutOfBoundsException If off or len are + * out of the range of buf. + */ + public synchronized void addRandomBytes(byte[] buf, int off, int len) + { + if (off < 0 || len < 0 || off + len > buf.length) + throw new ArrayIndexOutOfBoundsException(); + if (Configuration.DEBUG) + { + log.fine("adding random bytes:"); + log.fine(Util.toString(buf, off, len)); + } + final int count = off + len; + for (int i = off; i < count; i++) + { + pool[index++] ^= buf[i]; + if (index == pool.length) + { + mixRandomPool(); + index = 0; + } + } + } + + /** + * Add a single random byte to the randomness pool. Note that this method will + * not increment the pool's quality counter (this can only be done via + * a source provided to the setup method). + * + * @param b The byte to add. + */ + public synchronized void addRandomByte(byte b) + { + if (Configuration.DEBUG) + log.fine("adding byte " + Integer.toHexString(b)); + pool[index++] ^= b; + if (index >= pool.length) + { + mixRandomPool(); + index = 0; + } + } + + synchronized void addQuality(double quality) + { + if (Configuration.DEBUG) + log.fine("adding quality " + quality); + if (this.quality < 100) + this.quality += quality; + if (Configuration.DEBUG) + log.fine("quality now " + this.quality); + } + + synchronized double getQuality() + { + return quality; + } + + /** + * The mix operation. This method will, for every 20-byte block in the random + * pool, hash that block, the previous 20 bytes, and the next 44 bytes with + * SHA-1, writing the result back into that block. + */ + private void mixRandomPool(byte[] buf) + { + int hashSize = hash.hashSize(); + for (int i = 0; i < buf.length; i += hashSize) + { + // First update the bytes [p-19..p-1]. + if (i == 0) + hash.update(buf, buf.length - hashSize, hashSize); + else + hash.update(buf, i - hashSize, hashSize); + // Now the next 64 bytes. + if (i + 64 < buf.length) + hash.update(buf, i, 64); + else + { + hash.update(buf, i, buf.length - i); + hash.update(buf, 0, 64 - (buf.length - i)); + } + byte[] digest = hash.digest(); + System.arraycopy(digest, 0, buf, i, hashSize); + } + } + + private void mixRandomPool() + { + mixRandomPool(pool); + mixCount++; + } + + private void generateX917(byte[] buf) + { + int off = 0; + for (int i = 0; i < buf.length; i += X917_POOL_SIZE) + { + int copy = Math.min(buf.length - i, X917_POOL_SIZE); + for (int j = 0; j < copy; j++) + x917pool[j] ^= pool[off + j]; + cipher.encryptBlock(x917pool, 0, x917pool, 0); + System.arraycopy(x917pool, 0, buf, off, copy); + cipher.encryptBlock(x917pool, 0, x917pool, 0); + off += copy; + x917count++; + } + } + + /** + * Add random data always immediately available into the random pool, such as + * the values of the eight asynchronous counters, the current time, the + * current memory usage, the calling thread name, and the current stack trace. + *

+ * This method does not alter the quality counter, and is provided more to + * maintain randomness, not to seriously improve the current random state. + */ + private void fastPoll() + { + byte b = 0; + for (int i = 0; i < SPINNER_COUNT; i++) + b ^= SPINNERS[i].counter; + addRandomByte(b); + addRandomByte((byte) System.currentTimeMillis()); + addRandomByte((byte) Runtime.getRuntime().freeMemory()); + String s = Thread.currentThread().getName(); + if (s != null) + { + byte[] buf = s.getBytes(); + addRandomBytes(buf, 0, buf.length); + } + ByteArrayOutputStream bout = new ByteArrayOutputStream(1024); + PrintStream pout = new PrintStream(bout); + Throwable t = new Throwable(); + t.printStackTrace(pout); + pout.flush(); + byte[] buf = bout.toByteArray(); + addRandomBytes(buf, 0, buf.length); + } + + private void slowPoll() throws LimitReachedException + { + if (Configuration.DEBUG) + log.fine("poller is alive? " + + (pollerThread == null ? false : pollerThread.isAlive())); + if (pollerThread == null || ! pollerThread.isAlive()) + { + boolean interrupted = false; + pollerThread = new Thread(poller); + pollerThread.setDaemon(true); + pollerThread.setPriority(Thread.NORM_PRIORITY - 1); + pollerThread.start(); + if (blocking) + try + { + pollerThread.join(); + } + catch (InterruptedException ie) + { + interrupted = true; + } + // If the full slow poll has completed after we waited for it, + // and there in insufficient randomness, throw an exception. + if (! interrupted && blocking && quality < 100.0) + { + if (Configuration.DEBUG) + log.fine("insufficient quality: " + quality); + throw new LimitReachedException("insufficient randomness was polled"); + } + } + } + + protected void finalize() throws Throwable + { + if (poller != null && pollerThread != null && pollerThread.isAlive()) + { + pollerThread.interrupt(); + poller.stopUpdating(); + pollerThread.interrupt(); + } + Arrays.fill(pool, (byte) 0); + Arrays.fill(x917pool, (byte) 0); + Arrays.fill(buffer, (byte) 0); + } + + /** + * A simple thread that constantly updates a byte counter. This class is used + * in a group of lowest-priority threads and the values of their counters + * (updated in competition with all other threads) is used as a source of + * entropy bits. + */ + private static class Spinner + implements Runnable + { + protected byte counter; + + private Spinner() + { + } + + public void run() + { + while (true) + { + counter++; + try + { + Thread.sleep(100); + } + catch (InterruptedException ie) + { + } + } + } + } + + private final class Poller + implements Runnable + { + private final List files; + private final List urls; + private final List progs; + private final List other; + private final CSPRNG pool; + private boolean running; + + Poller(List files, List urls, List progs, List other, CSPRNG pool) + { + super(); + this.files = Collections.unmodifiableList(files); + this.urls = Collections.unmodifiableList(urls); + this.progs = Collections.unmodifiableList(progs); + this.other = Collections.unmodifiableList(other); + this.pool = pool; + } + + public void run() + { + running = true; + if (Configuration.DEBUG) + { + log.fine("files: " + files); + log.fine("URLs: " + urls); + log.fine("progs: " + progs); + } + Iterator files_it = files.iterator(); + Iterator urls_it = urls.iterator(); + Iterator prog_it = progs.iterator(); + Iterator other_it = other.iterator(); + + while (files_it.hasNext() || urls_it.hasNext() || prog_it.hasNext() + || other_it.hasNext()) + { + // There is enough random data. Go away. + if (pool.getQuality() >= 100.0 || ! running) + return; + if (files_it.hasNext()) + try + { + List l = (List) files_it.next(); + if (Configuration.DEBUG) + log.fine(l.toString()); + double qual = ((Double) l.get(0)).doubleValue(); + int offset = ((Integer) l.get(1)).intValue(); + int count = ((Integer) l.get(2)).intValue(); + String src = (String) l.get(3); + InputStream in = new FileInputStream(src); + byte[] buf = new byte[count]; + if (offset > 0) + in.skip(offset); + int len = in.read(buf); + if (len >= 0) + { + pool.addRandomBytes(buf, 0, len); + pool.addQuality(qual * ((double) len / (double) count)); + } + if (Configuration.DEBUG) + log.fine("got " + len + " bytes from " + src); + } + catch (Exception x) + { + if (Configuration.DEBUG) + log.throwing(this.getClass().getName(), "run", x); + } + if (pool.getQuality() >= 100.0 || ! running) + return; + if (urls_it.hasNext()) + try + { + List l = (List) urls_it.next(); + if (Configuration.DEBUG) + log.fine(l.toString()); + double qual = ((Double) l.get(0)).doubleValue(); + int offset = ((Integer) l.get(1)).intValue(); + int count = ((Integer) l.get(2)).intValue(); + URL src = (URL) l.get(3); + InputStream in = src.openStream(); + byte[] buf = new byte[count]; + if (offset > 0) + in.skip(offset); + int len = in.read(buf); + if (len >= 0) + { + pool.addRandomBytes(buf, 0, len); + pool.addQuality(qual * ((double) len / (double) count)); + } + if (Configuration.DEBUG) + log.fine("got " + len + " bytes from " + src); + } + catch (Exception x) + { + if (Configuration.DEBUG) + log.throwing(this.getClass().getName(), "run", x); + } + if (pool.getQuality() >= 100.0 || ! running) + return; + Process proc = null; + if (prog_it.hasNext()) + try + { + List l = (List) prog_it.next(); + if (Configuration.DEBUG) + log.finer(l.toString()); + double qual = ((Double) l.get(0)).doubleValue(); + int offset = ((Integer) l.get(1)).intValue(); + int count = ((Integer) l.get(2)).intValue(); + String src = (String) l.get(3); + proc = null; + proc = Runtime.getRuntime().exec(src); + InputStream in = proc.getInputStream(); + byte[] buf = new byte[count]; + if (offset > 0) + in.skip(offset); + int len = in.read(buf); + if (len >= 0) + { + pool.addRandomBytes(buf, 0, len); + pool.addQuality(qual * ((double) len / (double) count)); + } + proc.destroy(); + proc.waitFor(); + if (Configuration.DEBUG) + log.fine("got " + len + " bytes from " + src); + } + catch (Exception x) + { + if (Configuration.DEBUG) + log.throwing(this.getClass().getName(), "run", x); + try + { + if (proc != null) + { + proc.destroy(); + proc.waitFor(); + } + } + catch (Exception ignored) + { + } + } + if (pool.getQuality() >= 100.0 || ! running) + return; + if (other_it.hasNext()) + try + { + EntropySource src = (EntropySource) other_it.next(); + byte[] buf = src.nextBytes(); + if (pool == null) + return; + pool.addRandomBytes(buf, 0, buf.length); + pool.addQuality(src.quality()); + if (Configuration.DEBUG) + log.fine("got " + buf.length + " bytes from " + src); + } + catch (Exception x) + { + if (Configuration.DEBUG) + log.throwing(this.getClass().getName(), "run", x); + } + } + } + + public void stopUpdating() + { + running = false; + } + } +} diff --git a/libjava/classpath/gnu/javax/crypto/prng/Fortuna.java b/libjava/classpath/gnu/javax/crypto/prng/Fortuna.java new file mode 100644 index 000000000..8aec9ab7d --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/prng/Fortuna.java @@ -0,0 +1,349 @@ +/* Fortuna.java -- The Fortuna PRNG. + Copyright (C) 2004, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.prng; + +import gnu.java.security.Registry; +import gnu.java.security.hash.HashFactory; +import gnu.java.security.hash.IMessageDigest; +import gnu.java.security.prng.BasePRNG; +import gnu.java.security.prng.LimitReachedException; +import gnu.java.security.prng.RandomEvent; +import gnu.java.security.prng.RandomEventListener; +import gnu.javax.crypto.cipher.CipherFactory; +import gnu.javax.crypto.cipher.IBlockCipher; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.security.InvalidKeyException; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +/** + * The Fortuna continuously-seeded pseudo-random number generator. This + * generator is composed of two major pieces: the entropy accumulator and the + * generator function. The former takes in random bits and incorporates them + * into the generator's state. The latter takes this base entropy and generates + * pseudo-random bits from it. + *

+ * There are some things users of this class must be aware of: + *

+ *
Adding Random Data
+ *
This class does not do any polling of random sources, but rather + * provides an interface for adding random events. Applications that use this + * code must provide this mechanism. We use this design because an + * application writer who knows the system he is targeting is in a better + * position to judge what random data is available.
+ *
Storing the Seed
+ *
This class implements {@link Serializable} in such a way that it writes + * a 64 byte seed to the stream, and reads it back again when being + * deserialized. This is the extent of seed file management, however, and those + * using this class are encouraged to think deeply about when, how often, and + * where to store the seed.
+ *
+ *

+ * References: + *

+ */ +public class Fortuna + extends BasePRNG + implements Serializable, RandomEventListener +{ + private static final long serialVersionUID = 0xFACADE; + private static final int SEED_FILE_SIZE = 64; + private static final int NUM_POOLS = 32; + private static final int MIN_POOL_SIZE = 64; + private final Generator generator; + private final IMessageDigest[] pools; + private long lastReseed; + private int pool; + private int pool0Count; + private int reseedCount; + public static final String SEED = "gnu.crypto.prng.fortuna.seed"; + + public Fortuna() + { + super(Registry.FORTUNA_PRNG); + generator = new Generator(CipherFactory.getInstance(Registry.RIJNDAEL_CIPHER), + HashFactory.getInstance(Registry.SHA256_HASH)); + pools = new IMessageDigest[NUM_POOLS]; + for (int i = 0; i < NUM_POOLS; i++) + pools[i] = HashFactory.getInstance(Registry.SHA256_HASH); + lastReseed = 0; + pool = 0; + pool0Count = 0; + buffer = new byte[256]; + } + + public void setup(Map attributes) + { + lastReseed = 0; + reseedCount = 0; + pool = 0; + pool0Count = 0; + generator.init(attributes); + try + { + fillBlock(); + } + catch (LimitReachedException shouldNotHappen) + { + throw new RuntimeException(shouldNotHappen); + } + } + + public void fillBlock() throws LimitReachedException + { + if (pool0Count >= MIN_POOL_SIZE + && System.currentTimeMillis() - lastReseed > 100) + { + reseedCount++; + byte[] seed = new byte[0]; + for (int i = 0; i < NUM_POOLS; i++) + if (reseedCount % (1 << i) == 0) + generator.addRandomBytes(pools[i].digest()); + lastReseed = System.currentTimeMillis(); + pool0Count = 0; + } + generator.nextBytes(buffer); + } + + public void addRandomByte(byte b) + { + pools[pool].update(b); + if (pool == 0) + pool0Count++; + pool = (pool + 1) % NUM_POOLS; + } + + public void addRandomBytes(byte[] buf, int offset, int length) + { + pools[pool].update(buf, offset, length); + if (pool == 0) + pool0Count += length; + pool = (pool + 1) % NUM_POOLS; + } + + public void addRandomEvent(RandomEvent event) + { + if (event.getPoolNumber() < 0 || event.getPoolNumber() >= pools.length) + throw new IllegalArgumentException("pool number out of range: " + + event.getPoolNumber()); + pools[event.getPoolNumber()].update(event.getSourceNumber()); + pools[event.getPoolNumber()].update((byte) event.getData().length); + pools[event.getPoolNumber()].update(event.getData()); + if (event.getPoolNumber() == 0) + pool0Count += event.getData().length; + } + + // Reading and writing this object is equivalent to storing and retrieving + // the seed. + + private void writeObject(ObjectOutputStream out) throws IOException + { + byte[] seed = new byte[SEED_FILE_SIZE]; + try + { + generator.nextBytes(seed); + } + catch (LimitReachedException shouldNeverHappen) + { + throw new Error(shouldNeverHappen); + } + out.write(seed); + } + + private void readObject(ObjectInputStream in) throws IOException + { + byte[] seed = new byte[SEED_FILE_SIZE]; + in.readFully(seed); + generator.addRandomBytes(seed); + } + + /** + * The Fortuna generator function. The generator is a PRNG in its own right; + * Fortuna itself is basically a wrapper around this generator that manages + * reseeding in a secure way. + */ + public static class Generator + extends BasePRNG + implements Cloneable + { + private static final int LIMIT = 1 << 20; + private final IBlockCipher cipher; + private final IMessageDigest hash; + private final byte[] counter; + private final byte[] key; + private boolean seeded; + + public Generator(final IBlockCipher cipher, final IMessageDigest hash) + { + super(Registry.FORTUNA_GENERATOR_PRNG); + this.cipher = cipher; + this.hash = hash; + counter = new byte[cipher.defaultBlockSize()]; + buffer = new byte[cipher.defaultBlockSize()]; + int keysize = 0; + for (Iterator it = cipher.keySizes(); it.hasNext();) + { + int ks = ((Integer) it.next()).intValue(); + if (ks > keysize) + keysize = ks; + if (keysize >= 32) + break; + } + key = new byte[keysize]; + } + + public byte nextByte() + { + byte[] b = new byte[1]; + nextBytes(b, 0, 1); + return b[0]; + } + + public void nextBytes(byte[] out, int offset, int length) + { + if (! seeded) + throw new IllegalStateException("generator not seeded"); + int count = 0; + do + { + int amount = Math.min(LIMIT, length - count); + try + { + super.nextBytes(out, offset + count, amount); + } + catch (LimitReachedException shouldNeverHappen) + { + throw new Error(shouldNeverHappen); + } + count += amount; + for (int i = 0; i < key.length; i += counter.length) + { + fillBlock(); + int l = Math.min(key.length - i, cipher.currentBlockSize()); + System.arraycopy(buffer, 0, key, i, l); + } + resetKey(); + } + while (count < length); + fillBlock(); + ndx = 0; + } + + public void addRandomByte(byte b) + { + addRandomBytes(new byte[] { b }); + } + + public void addRandomBytes(byte[] seed, int offset, int length) + { + hash.update(key); + hash.update(seed, offset, length); + byte[] newkey = hash.digest(); + System.arraycopy(newkey, 0, key, 0, Math.min(key.length, newkey.length)); + resetKey(); + incrementCounter(); + seeded = true; + } + + public void fillBlock() + { + if (! seeded) + throw new IllegalStateException("generator not seeded"); + cipher.encryptBlock(counter, 0, buffer, 0); + incrementCounter(); + } + + public void setup(Map attributes) + { + seeded = false; + Arrays.fill(key, (byte) 0); + Arrays.fill(counter, (byte) 0); + byte[] seed = (byte[]) attributes.get(SEED); + if (seed != null) + addRandomBytes(seed); + fillBlock(); + } + + /** + * Resets the cipher's key. This is done after every reseed, which combines + * the old key and the seed, and processes that throigh the hash function. + */ + private void resetKey() + { + try + { + cipher.reset(); + cipher.init(Collections.singletonMap(IBlockCipher.KEY_MATERIAL, key)); + } + // We expect to never get an exception here. + catch (InvalidKeyException ike) + { + throw new Error(ike); + } + catch (IllegalArgumentException iae) + { + throw new Error(iae); + } + } + + /** + * Increment `counter' as a sixteen-byte little-endian unsigned integer by + * one. + */ + private void incrementCounter() + { + for (int i = 0; i < counter.length; i++) + { + counter[i]++; + if (counter[i] != 0) + break; + } + } + } +} diff --git a/libjava/classpath/gnu/javax/crypto/prng/ICMGenerator.java b/libjava/classpath/gnu/javax/crypto/prng/ICMGenerator.java new file mode 100644 index 000000000..a4df5b964 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/prng/ICMGenerator.java @@ -0,0 +1,306 @@ +/* ICMGenerator.java -- + Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.prng; + +import gnu.java.security.Registry; +import gnu.java.security.prng.BasePRNG; +import gnu.java.security.prng.LimitReachedException; +import gnu.javax.crypto.cipher.CipherFactory; +import gnu.javax.crypto.cipher.IBlockCipher; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.util.HashMap; +import java.util.Map; + +/** + * Counter Mode is a way to define a pseudorandom keystream generator using a + * block cipher. The keystream can be used for additive encryption, key + * derivation, or any other application requiring pseudorandom data. + *

+ * In ICM, the keystream is logically broken into segments. Each segment is + * identified with a segment index, and the segments have equal lengths. This + * segmentation makes ICM especially appropriate for securing packet-based + * protocols. + *

+ * This implementation adheres to the definition of the ICM keystream generation + * function that allows for any symetric key block cipher algorithm + * (initialisation parameter gnu.crypto.prng.icm.cipher.name + * taken to be an instance of {@link java.lang.String}) to be used. If such a + * parameter is not defined/included in the initialisation Map, + * then the "Rijndael" algorithm is used. Furthermore, if the initialisation + * parameter gnu.crypto.cipher.block.size (taken to be a instance + * of {@link java.lang.Integer}) is missing or undefined in the initialisation + * Map, then the cipher's default block size is used. + *

+ * The practical limits and constraints of such generator are: + *

+ *

+ * NOTE: Rijndael is used as the default symmetric key block cipher + * algorithm because, with its default block and key sizes, it is the AES. Yet + * being Rijndael, the algorithm offers more versatile block and key sizes which + * may prove to be useful for generating longer key streams. + *

+ * References: + *

    + *
  1. + * Integer Counter Mode, David A. McGrew.
  2. + *
+ */ +public class ICMGenerator + extends BasePRNG + implements Cloneable +{ + /** Property name of underlying block cipher for this ICM generator. */ + public static final String CIPHER = "gnu.crypto.prng.icm.cipher.name"; + /** Property name of ICM's block index length. */ + public static final String BLOCK_INDEX_LENGTH = + "gnu.crypto.prng.icm.block.index.length"; + /** Property name of ICM's segment index length. */ + public static final String SEGMENT_INDEX_LENGTH = + "gnu.crypto.prng.icm.segment.index.length"; + /** Property name of ICM's offset. */ + public static final String OFFSET = "gnu.crypto.prng.icm.offset"; + /** Property name of ICM's segment index. */ + public static final String SEGMENT_INDEX = "gnu.crypto.prng.icm.segment.index"; + /** The integer value 256 as a BigInteger. */ + private static final BigInteger TWO_FIFTY_SIX = new BigInteger("256"); + /** The underlying cipher implementation. */ + private IBlockCipher cipher; + /** This keystream block index length in bytes. */ + private int blockNdxLength = -1; + /** This keystream segment index length in bytes. */ + private int segmentNdxLength = -1; + /** The index of the next block for a given keystream segment. */ + private BigInteger blockNdx = BigInteger.ZERO; + /** The segment index for this keystream. */ + private BigInteger segmentNdx; + /** The initial counter for a given keystream segment. */ + private BigInteger C0; + + /** Trivial 0-arguments constructor. */ + public ICMGenerator() + { + super(Registry.ICM_PRNG); + } + + // Conceptually, ICM is a keystream generator that takes a secret key and a + // segment index as an input and then outputs a keystream segment. The + // segmentation lends itself to packet encryption, as each keystream segment + // can be used to encrypt a distinct packet. + // + // An ICM key consists of the block cipher key and an Offset. The Offset is + // an integer with BLOCK_LENGTH octets... + public void setup(Map attributes) + { + // find out which cipher algorithm to use + boolean newCipher = true; + String underlyingCipher = (String) attributes.get(CIPHER); + if (underlyingCipher == null) + if (cipher == null) // happy birthday + // ensure we have a reliable implementation of this cipher + cipher = CipherFactory.getInstance(Registry.RIJNDAEL_CIPHER); + else + // we already have one. use it as is + newCipher = false; + else // ensure we have a reliable implementation of this cipher + cipher = CipherFactory.getInstance(underlyingCipher); + + // find out what block size we should use it in + int cipherBlockSize = 0; + Integer bs = (Integer) attributes.get(IBlockCipher.CIPHER_BLOCK_SIZE); + if (bs != null) + cipherBlockSize = bs.intValue(); + else + { + if (newCipher) // assume we'll use its default block size + cipherBlockSize = cipher.defaultBlockSize(); + // else use as is + } + // get the key material + byte[] key = (byte[]) attributes.get(IBlockCipher.KEY_MATERIAL); + if (key == null) + throw new IllegalArgumentException(IBlockCipher.KEY_MATERIAL); + // now initialise the cipher + HashMap map = new HashMap(); + if (cipherBlockSize != 0) // only needed if new or changed + map.put(IBlockCipher.CIPHER_BLOCK_SIZE, Integer.valueOf(cipherBlockSize)); + map.put(IBlockCipher.KEY_MATERIAL, key); + try + { + cipher.init(map); + } + catch (InvalidKeyException x) + { + throw new IllegalArgumentException(IBlockCipher.KEY_MATERIAL); + } + // at this point we have an initialised (new or otherwise) cipher + // ensure that remaining params make sense + cipherBlockSize = cipher.currentBlockSize(); + BigInteger counterRange = TWO_FIFTY_SIX.pow(cipherBlockSize); + // offset, like the underlying cipher key is not cloneable + // always look for it and throw an exception if it's not there + Object obj = attributes.get(OFFSET); + // allow either a byte[] or a BigInteger + BigInteger r; + if (obj instanceof BigInteger) + r = (BigInteger) obj; + else // assume byte[]. should be same length as cipher block size + { + byte[] offset = (byte[]) obj; + if (offset.length != cipherBlockSize) + throw new IllegalArgumentException(OFFSET); + r = new BigInteger(1, offset); + } + int wantBlockNdxLength = -1; // number of octets in the block index + Integer i = (Integer) attributes.get(BLOCK_INDEX_LENGTH); + if (i != null) + { + wantBlockNdxLength = i.intValue(); + if (wantBlockNdxLength < 1) + throw new IllegalArgumentException(BLOCK_INDEX_LENGTH); + } + int wantSegmentNdxLength = -1; // number of octets in the segment index + i = (Integer) attributes.get(SEGMENT_INDEX_LENGTH); + if (i != null) + { + wantSegmentNdxLength = i.intValue(); + if (wantSegmentNdxLength < 1) + throw new IllegalArgumentException(SEGMENT_INDEX_LENGTH); + } + // if both are undefined check if it's a reuse + if ((wantBlockNdxLength == -1) && (wantSegmentNdxLength == -1)) + { + if (blockNdxLength == -1) // new instance + throw new IllegalArgumentException(BLOCK_INDEX_LENGTH + ", " + + SEGMENT_INDEX_LENGTH); + // else reuse old values + } + else // only one is undefined, set it to BLOCK_LENGTH/2 minus the other + { + int limit = cipherBlockSize / 2; + if (wantBlockNdxLength == -1) + wantBlockNdxLength = limit - wantSegmentNdxLength; + else if (wantSegmentNdxLength == -1) + wantSegmentNdxLength = limit - wantBlockNdxLength; + else if ((wantSegmentNdxLength + wantBlockNdxLength) > limit) + throw new IllegalArgumentException(BLOCK_INDEX_LENGTH + ", " + + SEGMENT_INDEX_LENGTH); + // save new values + blockNdxLength = wantBlockNdxLength; + segmentNdxLength = wantSegmentNdxLength; + } + // get the segment index as a BigInteger + BigInteger s = (BigInteger) attributes.get(SEGMENT_INDEX); + if (s == null) + { + if (segmentNdx == null) // segment index was never set + throw new IllegalArgumentException(SEGMENT_INDEX); + // reuse; check if still valid + if (segmentNdx.compareTo(TWO_FIFTY_SIX.pow(segmentNdxLength)) > 0) + throw new IllegalArgumentException(SEGMENT_INDEX); + } + else + { + if (s.compareTo(TWO_FIFTY_SIX.pow(segmentNdxLength)) > 0) + throw new IllegalArgumentException(SEGMENT_INDEX); + segmentNdx = s; + } + // The initial counter of the keystream segment with segment index s is + // defined as follows, where r denotes the Offset: + // + // C[0] = (s * (256^BLOCK_INDEX_LENGTH) + r) modulo (256^BLOCK_LENGTH) + C0 = segmentNdx.multiply(TWO_FIFTY_SIX.pow(blockNdxLength)) + .add(r).modPow(BigInteger.ONE, counterRange); + try + { + fillBlock(); + } + catch (LimitReachedException impossible) + { + throw (InternalError) + new InternalError().initCause(impossible); + } + } + + public void fillBlock() throws LimitReachedException + { + if (C0 == null) + throw new IllegalStateException(); + if (blockNdx.compareTo(TWO_FIFTY_SIX.pow(blockNdxLength)) >= 0) + throw new LimitReachedException(); + int cipherBlockSize = cipher.currentBlockSize(); + BigInteger counterRange = TWO_FIFTY_SIX.pow(cipherBlockSize); + // encrypt the counter for the current blockNdx + // C[i] = (C[0] + i) modulo (256^BLOCK_LENGTH). + BigInteger Ci = C0.add(blockNdx).modPow(BigInteger.ONE, counterRange); + buffer = Ci.toByteArray(); + int limit = buffer.length; + if (limit < cipherBlockSize) + { + byte[] data = new byte[cipherBlockSize]; + System.arraycopy(buffer, 0, data, cipherBlockSize - limit, limit); + buffer = data; + } + else if (limit > cipherBlockSize) + { + byte[] data = new byte[cipherBlockSize]; + System.arraycopy(buffer, limit - cipherBlockSize, data, 0, + cipherBlockSize); + buffer = data; + } + cipher.encryptBlock(buffer, 0, buffer, 0); + blockNdx = blockNdx.add(BigInteger.ONE); // increment blockNdx + } +} diff --git a/libjava/classpath/gnu/javax/crypto/prng/IPBE.java b/libjava/classpath/gnu/javax/crypto/prng/IPBE.java new file mode 100644 index 000000000..8138b7b9a --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/prng/IPBE.java @@ -0,0 +1,81 @@ +/* IPBE.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.prng; + +/** + * Trivial interface to group Password-based encryption property names and + * constants. + */ +public interface IPBE +{ + /** + * Property name for the iteration count in a PBE algorithm. The property + * associated with this is expected to be an {@link Integer}. + */ + String ITERATION_COUNT = "gnu.crypto.pbe.iteration.count"; + + /** + * Property name for the password in a PBE algorithm. The property associated + * with this is expected to be a char array. + */ + String PASSWORD = "gnu.crypto.pbe.password"; + + /** + * Property name for the password character encoding in a PBE algorithm. The + * property associated with this is expected to be a String denoting a valid + * character-encoding name. If this property is not set, and a password is + * used, then {@link #DEFAULT_PASSWORD_ENCODING} will be used when converting + * the password character(s) to bytes. + */ + String PASSWORD_ENCODING = "gnu.crypto.pbe.password.encoding"; + + /** + * Property name for the salt in a PBE algorithm. The property associated + * with this is expected to be a byte array. + */ + String SALT = "gnu.crypto.pbe.salt"; + + /** + * The default character set encoding name to be used if (a) a password is + * to be used as the source for a PBE-based Key Derivation Function (KDF) and + * (b) no character set encoding name was specified among the attributes used + * to initialize the instance. + */ + String DEFAULT_PASSWORD_ENCODING = "UTF-8"; +} diff --git a/libjava/classpath/gnu/javax/crypto/prng/PBKDF2.java b/libjava/classpath/gnu/javax/crypto/prng/PBKDF2.java new file mode 100644 index 000000000..22fcd5504 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/prng/PBKDF2.java @@ -0,0 +1,184 @@ +/* PBKDF2.java -- + Copyright (C) 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.prng; + +import gnu.java.security.prng.BasePRNG; +import gnu.java.security.prng.LimitReachedException; +import gnu.javax.crypto.mac.HMac; +import gnu.javax.crypto.mac.IMac; + +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * An implementation of the key derivation function KDF2 from PKCS #5: + * Password-Based Cryptography (PBE). This KDF is essentially a way to + * transform a password and a salt into a stream of random bytes, which may then + * be used to initialize a cipher or a MAC. + *

+ * This version uses a MAC as its pseudo-random function, and the password is + * used as the key. + *

+ * References: + *

    + *
  1. B. Kaliski, RFC 2898: + * Password-Based Cryptography Specification, Version 2.0
  2. + *
+ */ +public class PBKDF2 + extends BasePRNG + implements Cloneable +{ + /** + * The bytes fed into the MAC. This is initially the concatenation of the salt + * and the block number. + */ + private byte[] in; + /** The iteration count. */ + private int iterationCount; + /** The salt. */ + private byte[] salt; + /** The MAC (the pseudo-random function we use). */ + private IMac mac; + /** The number of hLen-sized blocks generated. */ + private long count; + + /** + * Creates a new PBKDF2 object. The argument is the MAC that will serve as the + * pseudo-random function. The MAC does not need to be initialized. + * + * @param mac The pseudo-random function. + */ + public PBKDF2(IMac mac) + { + super("PBKDF2-" + mac.name()); + this.mac = mac; + iterationCount = -1; + } + + public void setup(Map attributes) + { + Map macAttrib = new HashMap(); + macAttrib.put(HMac.USE_WITH_PKCS5_V2, Boolean.TRUE); + byte[] s = (byte[]) attributes.get(IPBE.SALT); + if (s == null) + { + if (salt == null) + throw new IllegalArgumentException("no salt specified"); + // Otherwise re-use. + } + else + salt = s; + byte[] macKeyMaterial; + char[] password = (char[]) attributes.get(IPBE.PASSWORD); + if (password != null) + { + String encoding = (String) attributes.get(IPBE.PASSWORD_ENCODING); + if (encoding == null || encoding.trim().length() == 0) + encoding = IPBE.DEFAULT_PASSWORD_ENCODING; + else + encoding = encoding.trim(); + try + { + macKeyMaterial = new String(password).getBytes(encoding); + } + catch (UnsupportedEncodingException uee) + { + throw new IllegalArgumentException("Unknown or unsupported encoding: " + + encoding, uee); + } + } + else + macKeyMaterial = (byte[]) attributes.get(IMac.MAC_KEY_MATERIAL); + + if (macKeyMaterial != null) + macAttrib.put(IMac.MAC_KEY_MATERIAL, macKeyMaterial); + else if (! initialised) + throw new IllegalArgumentException( + "Neither password nor key-material were specified"); + // otherwise re-use previous password/key-material + try + { + mac.init(macAttrib); + } + catch (Exception x) + { + throw new IllegalArgumentException(x.getMessage()); + } + Integer ic = (Integer) attributes.get(IPBE.ITERATION_COUNT); + if (ic != null) + iterationCount = ic.intValue(); + if (iterationCount <= 0) + throw new IllegalArgumentException("bad iteration count"); + count = 0L; + buffer = new byte[mac.macSize()]; + try + { + fillBlock(); + } + catch (LimitReachedException x) + { + throw new Error(x.getMessage()); + } + } + + public void fillBlock() throws LimitReachedException + { + if (++count > ((1L << 32) - 1)) + throw new LimitReachedException(); + Arrays.fill(buffer, (byte) 0x00); + int limit = salt.length; + in = new byte[limit + 4]; + System.arraycopy(salt, 0, in, 0, salt.length); + in[limit++] = (byte)(count >>> 24); + in[limit++] = (byte)(count >>> 16); + in[limit++] = (byte)(count >>> 8); + in[limit ] = (byte) count; + for (int i = 0; i < iterationCount; i++) + { + mac.reset(); + mac.update(in, 0, in.length); + in = mac.digest(); + for (int j = 0; j < buffer.length; j++) + buffer[j] ^= in[j]; + } + } +} diff --git a/libjava/classpath/gnu/javax/crypto/prng/PRNGFactory.java b/libjava/classpath/gnu/javax/crypto/prng/PRNGFactory.java new file mode 100644 index 000000000..0a7c5e377 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/prng/PRNGFactory.java @@ -0,0 +1,115 @@ +/* PRNGFactory.java -- + Copyright (C) 2001, 2002, 2003, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.prng; + +import gnu.java.security.Registry; +import gnu.java.security.prng.IRandom; +import gnu.javax.crypto.mac.HMacFactory; +import gnu.javax.crypto.mac.IMac; +import gnu.javax.crypto.mac.MacFactory; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/** + * A Factory to instantiate pseudo random number generators. + */ +public class PRNGFactory + implements Registry +{ + /** Trivial constructor to enforce Singleton pattern. */ + private PRNGFactory() + { + } + + /** + * Returns an instance of a padding algorithm given its name. + * + * @param prng the case-insensitive name of the PRNG. + * @return an instance of the pseudo-random number generator. + * @exception InternalError if the implementation does not pass its self- + * test. + */ + public static IRandom getInstance(String prng) + { + if (prng == null) + return null; + prng = prng.trim(); + IRandom result = null; + if (prng.equalsIgnoreCase(ARCFOUR_PRNG) || prng.equalsIgnoreCase(RC4_PRNG)) + result = new ARCFour(); + else if (prng.equalsIgnoreCase(ICM_PRNG)) + result = new ICMGenerator(); + else if (prng.equalsIgnoreCase(UMAC_PRNG)) + result = new UMacGenerator(); + else if (prng.toLowerCase().startsWith(PBKDF2_PRNG_PREFIX)) + { + String macName = prng.substring(PBKDF2_PRNG_PREFIX.length()); + IMac mac = MacFactory.getInstance(macName); + if (mac == null) + return null; + result = new PBKDF2(mac); + } + + if (result != null) + return result; + + return gnu.java.security.prng.PRNGFactory.getInstance(prng); + } + + /** + * Returns a {@link Set} of names of padding algorithms supported by this + * Factory. + * + * @return a {@link Set} of pseudo-random number generator algorithm names + * (Strings). + */ + public static Set getNames() + { + HashSet hs = new HashSet(gnu.java.security.prng.PRNGFactory.getNames()); + hs.add(ICM_PRNG); + hs.add(UMAC_PRNG); + // add all hmac implementations as candidate PBKDF2 ones too + for (Iterator it = HMacFactory.getNames().iterator(); it.hasNext();) + hs.add(PBKDF2_PRNG_PREFIX + ((String) it.next())); + return Collections.unmodifiableSet(hs); + } +} diff --git a/libjava/classpath/gnu/javax/crypto/prng/UMacGenerator.java b/libjava/classpath/gnu/javax/crypto/prng/UMacGenerator.java new file mode 100644 index 000000000..1ee449223 --- /dev/null +++ b/libjava/classpath/gnu/javax/crypto/prng/UMacGenerator.java @@ -0,0 +1,186 @@ +/* UMacGenerator.java -- + Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.crypto.prng; + +import gnu.java.security.Registry; +import gnu.java.security.prng.BasePRNG; +import gnu.java.security.prng.LimitReachedException; +import gnu.javax.crypto.cipher.CipherFactory; +import gnu.javax.crypto.cipher.IBlockCipher; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.security.InvalidKeyException; + +/** + * KDFs (Key Derivation Functions) are used to stretch user-supplied key + * material to specific size(s) required by high level cryptographic primitives. + * Described in the UMAC + * paper, this function basically operates an underlying symmetric key block + * cipher instance in output feedback mode (OFB), as a strong + * pseudo-random number generator. + *

+ * UMacGenerator requires an index parameter + * (initialisation parameter gnu.crypto.prng.umac.kdf.index taken + * to be an instance of {@link Integer} with a value between 0 and + * 255). Using the same key, but different indices, generates + * different pseudorandom outputs. + *

+ * This implementation generalises the definition of the + * UmacGenerator algorithm to allow for other than the AES + * symetric key block cipher algorithm (initialisation parameter + * gnu.crypto.prng.umac.cipher.name taken to be an instance of + * {@link String}). If such a parameter is not defined/included in the + * initialisation Map, then the "Rijndael" algorithm is used. + * Furthermore, if the initialisation parameter + * gnu.crypto.cipher.block.size (taken to be a instance of + * {@link Integer}) is missing or undefined in the initialisation + * Map, then the cipher's default block size is used. + *

+ * NOTE: Rijndael is used as the default symmetric key block cipher + * algorithm because, with its default block and key sizes, it is the AES. Yet + * being Rijndael, the algorithm offers more versatile block and key sizes which + * may prove to be useful for generating "longer" key streams. + *

+ * References: + *

    + *
  1. + * UMAC: Message Authentication Code using Universal Hashing.
    + * T. Krovetz, J. Black, S. Halevi, A. Hevia, H. Krawczyk, and P. Rogaway.
  2. + *
+ */ +public class UMacGenerator + extends BasePRNG + implements Cloneable +{ + /** + * Property name of the KDF index value to use in this + * instance. The value is taken to be an {@link Integer} less than + * 256. + */ + public static final String INDEX = "gnu.crypto.prng.umac.index"; + /** The name of the underlying symmetric key block cipher algorithm. */ + public static final String CIPHER = "gnu.crypto.prng.umac.cipher.name"; + /** The generator's underlying block cipher. */ + private IBlockCipher cipher; + + /** Trivial 0-arguments constructor. */ + public UMacGenerator() + { + super(Registry.UMAC_PRNG); + } + + public void setup(Map attributes) + { + boolean newCipher = true; + String cipherName = (String) attributes.get(CIPHER); + if (cipherName == null) + if (cipher == null) // happy birthday + cipher = CipherFactory.getInstance(Registry.RIJNDAEL_CIPHER); + else // we already have one. use it as is + newCipher = false; + else + cipher = CipherFactory.getInstance(cipherName); + // find out what block size we should use it in + int cipherBlockSize = 0; + Integer bs = (Integer) attributes.get(IBlockCipher.CIPHER_BLOCK_SIZE); + if (bs != null) + cipherBlockSize = bs.intValue(); + else + { + if (newCipher) // assume we'll use its default block size + cipherBlockSize = cipher.defaultBlockSize(); + // else use as is + } + // get the key material + byte[] key = (byte[]) attributes.get(IBlockCipher.KEY_MATERIAL); + if (key == null) + throw new IllegalArgumentException(IBlockCipher.KEY_MATERIAL); + + int keyLength = key.length; + // ensure that keyLength is valid for the chosen underlying cipher + boolean ok = false; + for (Iterator it = cipher.keySizes(); it.hasNext();) + { + ok = (keyLength == ((Integer) it.next()).intValue()); + if (ok) + break; + } + if (! ok) + throw new IllegalArgumentException("key length"); + // ensure that remaining params make sense + int index = -1; + Integer i = (Integer) attributes.get(INDEX); + if (i != null) + { + index = i.intValue(); + if (index < 0 || index > 255) + throw new IllegalArgumentException(INDEX); + } + // now initialise the underlying cipher + Map map = new HashMap(); + if (cipherBlockSize != 0) // only needed if new or changed + map.put(IBlockCipher.CIPHER_BLOCK_SIZE, Integer.valueOf(cipherBlockSize)); + map.put(IBlockCipher.KEY_MATERIAL, key); + try + { + cipher.init(map); + } + catch (InvalidKeyException x) + { + throw new IllegalArgumentException(IBlockCipher.KEY_MATERIAL); + } + buffer = new byte[cipher.currentBlockSize()]; + buffer[cipher.currentBlockSize() - 1] = (byte) index; + try + { + fillBlock(); + } + catch (LimitReachedException impossible) + { + } + } + + public void fillBlock() throws LimitReachedException + { + cipher.encryptBlock(buffer, 0, buffer, 0); + } +} -- cgit v1.2.3