Hack SshDaemon to not use SecureRandom during tests

We previously needed 8 bytes of data from a SecureRandom in order to
start an SSH daemon. Unfortunately, sometimes machines run out of
entropy, for example when running the Gerrit test suite several times
in a row.

Add an undocumented configuration option to use a different Random
factory for Apache SSHD that uses a hard-coded seed instead of
depending on SecureRandom. Unfortunately, because SshDaemon is
constructed using Daemon's injector stack, we can't easily modify its
modules to swap out the provider, so a configuration option is the
easiest solution.

Change-Id: I539b8e3d39d2da9908962fdb8d9633adf935fb4c
This commit is contained in:
Dave Borowitz 2015-02-20 15:28:33 -08:00
parent d6a48f0201
commit a5f3a69707
2 changed files with 39 additions and 4 deletions

View File

@ -131,6 +131,7 @@ public class GerritServer {
cfg.setString("gerrit", null, "canonicalWebUrl", url);
cfg.setString("httpd", null, "listenUrl", url);
cfg.setString("sshd", null, "listenAddress", forceEphemeralPort);
cfg.setBoolean("sshd", null, "testUseInsecureRandom", true);
cfg.setString("cache", null, "directory", null);
cfg.setString("gerrit", null, "basePath", "git");
cfg.setBoolean("sendemail", null, "enable", false);

View File

@ -45,6 +45,7 @@ import org.apache.sshd.common.ForwardingFilter;
import org.apache.sshd.common.KeyExchange;
import org.apache.sshd.common.KeyPairProvider;
import org.apache.sshd.common.NamedFactory;
import org.apache.sshd.common.Random;
import org.apache.sshd.common.Session;
import org.apache.sshd.common.Signature;
import org.apache.sshd.common.SshdSocketAddress;
@ -94,6 +95,8 @@ import org.apache.sshd.server.channel.ChannelSession;
import org.apache.sshd.server.kex.DHG1;
import org.apache.sshd.server.kex.DHG14;
import org.apache.sshd.server.session.SessionFactory;
import org.bouncycastle.crypto.prng.RandomGenerator;
import org.bouncycastle.crypto.prng.VMPCRandomGenerator;
import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -197,7 +200,7 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
MinaServiceFactory.class.getName());
if (SecurityUtils.isBouncyCastleRegistered()) {
initProviderBouncyCastle();
initProviderBouncyCastle(cfg);
} else {
initProviderJce();
}
@ -366,11 +369,42 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
return r.toString();
}
private void initProviderBouncyCastle() {
private void initProviderBouncyCastle(Config cfg) {
setKeyExchangeFactories(Arrays.<NamedFactory<KeyExchange>> asList(
new DHG14.Factory(), new DHG1.Factory()));
setRandomFactory(new SingletonRandomFactory(
new BouncyCastleRandom.Factory()));
NamedFactory<Random> factory;
if (cfg.getBoolean("sshd", null, "testUseInsecureRandom", false)) {
factory = new InsecureBouncyCastleRandom.Factory();
} else {
factory = new BouncyCastleRandom.Factory();
}
setRandomFactory(new SingletonRandomFactory(factory));
}
private static class InsecureBouncyCastleRandom implements Random {
private static class Factory implements NamedFactory<Random> {
@Override
public String getName() {
return "INSECURE_bouncycastle";
}
@Override
public Random create() {
return new InsecureBouncyCastleRandom();
}
}
private final RandomGenerator random;
private InsecureBouncyCastleRandom() {
random = new VMPCRandomGenerator();
random.addSeedMaterial(1234);
}
@Override
public void fill(byte[] bytes, int start, int len) {
random.nextBytes(bytes, start, len);
}
}
private void initProviderJce() {