package com.wesabe.grendel.modules;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Provider;
/**
* A Guice {@link Provider} which manages a {@link SecureRandom} instance.
*
* @author coda
*/
public class SecureRandomProvider implements Provider<SecureRandom> {
private static final int ENTROPY_UPDATE_SIZE = 64;
/**
* A scheduled, asynchronous task which generates additional entropy and
* adds it to the PRNG's entropy pool.
*/
private static class UpdateTask implements Runnable {
private final SecureRandom random;
private final Logger logger;
public UpdateTask(SecureRandom random, Logger logger) {
this.random = random;
this.logger = logger;
}
@Override
public void run() {
logger.info("Generating new PRNG seed");
final byte[] seed = SecureRandom.getSeed(ENTROPY_UPDATE_SIZE);
logger.info("Updating PRNG seed");
random.setSeed(seed);
}
}
private static final Logger LOGGER = LoggerFactory.getLogger(SecureRandomProvider.class);
private static final String PRNG_ALGORITHM = "SHA1PRNG";
private static final String PRNG_PROVIDER = "SUN";
private final SecureRandom random;
private final ScheduledExecutorService pool;
public SecureRandomProvider() {
this.pool = Executors.newSingleThreadScheduledExecutor();
LOGGER.info("Creating PRNG");
try {
this.random = SecureRandom.getInstance(PRNG_ALGORITHM, PRNG_PROVIDER);
} catch (GeneralSecurityException e) {
throw new IllegalStateException(e);
}
LOGGER.info("Seeding PRNG");
random.nextInt(); // force seeding
// update the PRNG every hour, starting in an hour
pool.scheduleAtFixedRate(new UpdateTask(random, LOGGER), 1, 1, TimeUnit.HOURS);
}
@Override
public SecureRandom get() {
return random;
}
}