package io.vertx.ext.auth;
import io.vertx.core.Vertx;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.concurrent.atomic.AtomicBoolean;
public class PRNG implements VertxContextPRNG {
private static final int DEFAULT_SEED_INTERVAL_MILLIS = 300000;
private static final int DEFAULT_SEED_BITS = 64;
private static final Base64.Encoder base64url = Base64.getUrlEncoder().withoutPadding();
private final SecureRandom random;
private final long seedID;
private final Vertx vertx;
private volatile boolean dirty = false;
public PRNG(Vertx vertx) {
this.vertx = vertx;
final String algorithm = System.getProperty("io.vertx.ext.auth.prng.algorithm");
final int seedInterval = Integer.getInteger("io.vertx.ext.auth.prng.seed.interval", DEFAULT_SEED_INTERVAL_MILLIS);
final int seedBits = Integer.getInteger("io.vertx.ext.auth.prng.seed.bits", DEFAULT_SEED_BITS);
if (algorithm != null) {
try {
random = SecureRandom.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
} else {
random = new SecureRandom();
}
random.nextBytes(new byte[1]);
if (seedInterval > 0 && seedBits > 0) {
final AtomicBoolean seeding = new AtomicBoolean(false);
seedID = vertx.setPeriodic(
seedInterval,
id -> {
if (dirty && seeding.compareAndSet(false, true)) {
vertx.<byte[]>executeBlocking(
future -> future.complete(random.generateSeed(seedBits / 8)),
false,
generateSeed -> {
seeding.set(false);
dirty = false;
random.setSeed(generateSeed.result());
});
}
});
} else {
seedID = -1;
}
}
@Override
public void close() {
if (seedID != -1) {
vertx.cancelTimer(seedID);
}
}
@Override
public void nextBytes(byte[] bytes) {
if (bytes != null) {
random.nextBytes(bytes);
dirty = true;
}
}
@Override
public int nextInt() {
try {
return random.nextInt();
} finally {
dirty = true;
}
}
@Override
public int nextInt(final int bound) {
try {
return random.nextInt(bound);
} finally {
dirty = true;
}
}
@Override
public boolean nextBoolean() {
try {
return random.nextBoolean();
} finally {
dirty = true;
}
}
@Override
public long nextLong() {
try {
return random.nextLong();
} finally {
dirty = true;
}
}
@Override
public float nextFloat() {
try {
return random.nextFloat();
} finally {
dirty = true;
}
}
@Override
public double nextDouble() {
try {
return random.nextDouble();
} finally {
dirty = true;
}
}
@Override
public double nextGaussian() {
try {
return random.nextGaussian();
} finally {
dirty = true;
}
}
@Override
public String nextString(int length) {
final byte[] data = new byte[length];
nextBytes(data);
return base64url.encodeToString(data);
}
}