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
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java index 3e8f0b2..644a4bb 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -131,6 +131,7 @@ 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);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java index 3306592..cdaf20e 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
@@ -45,6 +45,7 @@ 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.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 @@ MinaServiceFactory.class.getName()); if (SecurityUtils.isBouncyCastleRegistered()) { - initProviderBouncyCastle(); + initProviderBouncyCastle(cfg); } else { initProviderJce(); } @@ -366,11 +369,42 @@ 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() {