diff --git a/akka-remote/src/main/resources/reference.conf b/akka-remote/src/main/resources/reference.conf index f365d5ce19..64c2e1a840 100644 --- a/akka-remote/src/main/resources/reference.conf +++ b/akka-remote/src/main/resources/reference.conf @@ -209,6 +209,7 @@ akka { # "" or SecureRandom => (default) # "SHA1PRNG" => Can be slow because of blocking issues on Linux # "AES128CounterSecureRNG" => fastest startup and based on AES encryption algorithm + # "AES256CounterSecureRNG" # The following use one of 3 possible seed sources, depending on availability: /dev/random, random.org and SecureRandom (provided by Java) # "AES128CounterInetRNG" # "AES256CounterInetRNG" (Install JCE Unlimited Strength Jurisdiction Policy Files first) diff --git a/akka-remote/src/main/scala/akka/remote/netty/NettySSLSupport.scala b/akka-remote/src/main/scala/akka/remote/netty/NettySSLSupport.scala index 690b4522ec..83fdb781a7 100644 --- a/akka-remote/src/main/scala/akka/remote/netty/NettySSLSupport.scala +++ b/akka-remote/src/main/scala/akka/remote/netty/NettySSLSupport.scala @@ -38,7 +38,7 @@ private[akka] object NettySSLSupport { } val rng = rngName match { - case Some(r @ ("AES128CounterSecureRNG" | "AES128CounterInetRNG" | "AES256CounterInetRNG")) ⇒ + case Some(r @ ("AES128CounterSecureRNG" | "AES256CounterSecureRNG" | "AES128CounterInetRNG" | "AES256CounterInetRNG")) ⇒ log.debug("SSL random number generator set to: {}", r) SecureRandom.getInstance(r, AkkaProvider) case Some(s @ ("SHA1PRNG" | "NativePRNG")) ⇒ diff --git a/akka-remote/src/main/scala/akka/security/provider/AES128CounterInetRNG.scala b/akka-remote/src/main/scala/akka/security/provider/AES128CounterInetRNG.scala index 41d12b275f..284178773c 100644 --- a/akka-remote/src/main/scala/akka/security/provider/AES128CounterInetRNG.scala +++ b/akka-remote/src/main/scala/akka/security/provider/AES128CounterInetRNG.scala @@ -3,17 +3,17 @@ */ package akka.security.provider -import org.uncommons.maths.random.{ AESCounterRNG, DefaultSeedGenerator } +import org.uncommons.maths.random.{ AESCounterRNG } /** * Internal API * This class is a wrapper around the 128-bit AESCounterRNG algorithm provided by http://maths.uncommons.org/ * It uses the default seed generator which uses one of the following 3 random seed sources: - * Depending on availability: /dev/random, random.org and SecureRandom (provided by Java) + * Depending on availability: random.org, /dev/random, and SecureRandom (provided by Java) * The only method used by netty ssl is engineNextBytes(bytes) */ class AES128CounterInetRNG extends java.security.SecureRandomSpi { - private val rng = new AESCounterRNG() + private val rng = new AESCounterRNG(InternetSeedGenerator.getInstance.generateSeed(Seed128.size)) /** * This is managed internally by AESCounterRNG @@ -35,6 +35,6 @@ class AES128CounterInetRNG extends java.security.SecureRandomSpi { * @param numBytes the number of seed bytes to generate. * @return the seed bytes. */ - override protected def engineGenerateSeed(numBytes: Int): Array[Byte] = DefaultSeedGenerator.getInstance.generateSeed(numBytes) + override protected def engineGenerateSeed(numBytes: Int): Array[Byte] = InternetSeedGenerator.getInstance.generateSeed(numBytes) } diff --git a/akka-remote/src/main/scala/akka/security/provider/AES128CounterSecureRNG.scala b/akka-remote/src/main/scala/akka/security/provider/AES128CounterSecureRNG.scala index cda59ee03b..f891a29dc7 100644 --- a/akka-remote/src/main/scala/akka/security/provider/AES128CounterSecureRNG.scala +++ b/akka-remote/src/main/scala/akka/security/provider/AES128CounterSecureRNG.scala @@ -7,12 +7,18 @@ import org.uncommons.maths.random.{ AESCounterRNG, SecureRandomSeedGenerator } /** * Internal API - * This class is a wrapper around the AESCounterRNG algorithm provided by http://maths.uncommons.org/ * + * This class is a wrapper around the 128-bit AESCounterRNG algorithm provided by http://maths.uncommons.org/ * The only method used by netty ssl is engineNextBytes(bytes) * This RNG is good to use to prevent startup delay when you don't have Internet access to random.org */ class AES128CounterSecureRNG extends java.security.SecureRandomSpi { - private val rng = new AESCounterRNG(new SecureRandomSeedGenerator()) + /**Singleton instance. */ + private final lazy val INSTANCE: SecureRandomSeedGenerator = new SecureRandomSeedGenerator + + /** + * Make sure the seed generator is provided by a SecureRandom singleton and not default 'Random' + */ + private val rng = new AESCounterRNG(INSTANCE.generateSeed(Seed128.size)) /** * This is managed internally by AESCounterRNG @@ -34,6 +40,6 @@ class AES128CounterSecureRNG extends java.security.SecureRandomSpi { * @param numBytes the number of seed bytes to generate. * @return the seed bytes. */ - override protected def engineGenerateSeed(numBytes: Int): Array[Byte] = (new SecureRandomSeedGenerator()).generateSeed(numBytes) + override protected def engineGenerateSeed(numBytes: Int): Array[Byte] = INSTANCE.generateSeed(numBytes) } diff --git a/akka-remote/src/main/scala/akka/security/provider/AES256CounterInetRNG.scala b/akka-remote/src/main/scala/akka/security/provider/AES256CounterInetRNG.scala index 076d4fcd7f..8a6f68b49d 100644 --- a/akka-remote/src/main/scala/akka/security/provider/AES256CounterInetRNG.scala +++ b/akka-remote/src/main/scala/akka/security/provider/AES256CounterInetRNG.scala @@ -3,23 +3,17 @@ */ package akka.security.provider -import org.uncommons.maths.random.{ AESCounterRNG, DefaultSeedGenerator } +import org.uncommons.maths.random.{ AESCounterRNG } /** * Internal API * This class is a wrapper around the 256-bit AESCounterRNG algorithm provided by http://maths.uncommons.org/ * It uses the default seed generator which uses one of the following 3 random seed sources: - * Depending on availability: /dev/random, random.org and SecureRandom (provided by Java) + * Depending on availability: random.org, /dev/random, and SecureRandom (provided by Java) * The only method used by netty ssl is engineNextBytes(bytes) */ class AES256CounterInetRNG extends java.security.SecureRandomSpi { - /** - * From AESCounterRNG API docs: - * Valid values are 16 (128 bits), 24 (192 bits) and 32 (256 bits). - * Any other values will result in an exception from the AES implementation. - */ - private val AES_256_BIT = 32 // Magic number is magic - private val rng = new AESCounterRNG(AES_256_BIT) + private val rng = new AESCounterRNG(InternetSeedGenerator.getInstance.generateSeed(Seed256.size)) /** * This is managed internally by AESCounterRNG @@ -41,6 +35,6 @@ class AES256CounterInetRNG extends java.security.SecureRandomSpi { * @param numBytes the number of seed bytes to generate. * @return the seed bytes. */ - override protected def engineGenerateSeed(numBytes: Int): Array[Byte] = DefaultSeedGenerator.getInstance.generateSeed(numBytes) + override protected def engineGenerateSeed(numBytes: Int): Array[Byte] = InternetSeedGenerator.getInstance.generateSeed(numBytes) } diff --git a/akka-remote/src/main/scala/akka/security/provider/AES256CounterSecureRNG.scala b/akka-remote/src/main/scala/akka/security/provider/AES256CounterSecureRNG.scala new file mode 100644 index 0000000000..1a69e5e625 --- /dev/null +++ b/akka-remote/src/main/scala/akka/security/provider/AES256CounterSecureRNG.scala @@ -0,0 +1,42 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.security.provider + +import org.uncommons.maths.random.{ AESCounterRNG, SecureRandomSeedGenerator } + +/** + * Internal API + * This class is a wrapper around the 256-bit AESCounterRNG algorithm provided by http://maths.uncommons.org/ + * The only method used by netty ssl is engineNextBytes(bytes) + * This RNG is good to use to prevent startup delay when you don't have Internet access to random.org + */ +class AES256CounterSecureRNG extends java.security.SecureRandomSpi { + /**Singleton instance. */ + private final lazy val INSTANCE: SecureRandomSeedGenerator = new SecureRandomSeedGenerator + + private val rng = new AESCounterRNG(INSTANCE.generateSeed(Seed256.size)) + + /** + * This is managed internally by AESCounterRNG + */ + override protected def engineSetSeed(seed: Array[Byte]): Unit = () + + /** + * Generates a user-specified number of random bytes. + * + * @param bytes the array to be filled in with random bytes. + */ + override protected def engineNextBytes(bytes: Array[Byte]): Unit = rng.nextBytes(bytes) + + /** + * Unused method + * Returns the given number of seed bytes. This call may be used to + * seed other random number generators. + * + * @param numBytes the number of seed bytes to generate. + * @return the seed bytes. + */ + override protected def engineGenerateSeed(numBytes: Int): Array[Byte] = INSTANCE.generateSeed(numBytes) +} + diff --git a/akka-remote/src/main/scala/akka/security/provider/AkkaProvider.scala b/akka-remote/src/main/scala/akka/security/provider/AkkaProvider.scala index 707ad0c399..a78ca57015 100644 --- a/akka-remote/src/main/scala/akka/security/provider/AkkaProvider.scala +++ b/akka-remote/src/main/scala/akka/security/provider/AkkaProvider.scala @@ -13,11 +13,13 @@ object AkkaProvider extends Provider("Akka", 1.0, "Akka provider 1.0 that implem def run = { //SecureRandom put("SecureRandom.AES128CounterSecureRNG", classOf[AES128CounterSecureRNG].getName) + put("SecureRandom.AES256CounterSecureRNG", classOf[AES128CounterSecureRNG].getName) put("SecureRandom.AES128CounterInetRNG", classOf[AES128CounterInetRNG].getName) put("SecureRandom.AES256CounterInetRNG", classOf[AES256CounterInetRNG].getName) //Implementation type: software or hardware put("SecureRandom.AES128CounterSecureRNG ImplementedIn", "Software") + put("SecureRandom.AES256CounterSecureRNG ImplementedIn", "Software") put("SecureRandom.AES128CounterInetRNG ImplementedIn", "Software") put("SecureRandom.AES256CounterInetRNG ImplementedIn", "Software") null //Magic null is magic diff --git a/akka-remote/src/main/scala/akka/security/provider/InternetSeedGenerator.scala b/akka-remote/src/main/scala/akka/security/provider/InternetSeedGenerator.scala new file mode 100644 index 0000000000..a4dee9da8d --- /dev/null +++ b/akka-remote/src/main/scala/akka/security/provider/InternetSeedGenerator.scala @@ -0,0 +1,55 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + * modified and converted to Scala from Daniel W. Dyer's DefaultSeedGenerator + */ +package akka.security.provider + +import org.uncommons.maths.random.{ SeedGenerator, SeedException, SecureRandomSeedGenerator, RandomDotOrgSeedGenerator, DevRandomSeedGenerator } + +/** + * Internal API + * Seed generator that maintains multiple strategies for seed + * generation and will delegate to the best one available for the + * current operating environment. + * @author Daniel Dyer + */ +object InternetSeedGenerator { + /** + * @return The singleton instance of this class. + */ + def getInstance: InternetSeedGenerator = { + INSTANCE + } + + /**Singleton instance. */ + private final val INSTANCE: InternetSeedGenerator = new InternetSeedGenerator + /**Delegate generators. */ + private final val GENERATORS: Seq[SeedGenerator] = + new RandomDotOrgSeedGenerator :: // first try the Internet seed generator + new DevRandomSeedGenerator :: // try the local /dev/random + new SecureRandomSeedGenerator :: // this is last because it always works + Nil + +} + +final class InternetSeedGenerator extends SeedGenerator { + /** + * Generates a seed by trying each of the available strategies in + * turn until one succeeds. Tries the most suitable strategy first + * and eventually degrades to the least suitable (but guaranteed to + * work) strategy. + * @param length The length (in bytes) of the seed. + * @return A random seed of the requested length. + */ + def generateSeed(length: Int): Array[Byte] = { + for (generator ← InternetSeedGenerator.GENERATORS) { + try { + generator.generateSeed(length) + } catch { + case ex: SeedException ⇒ // Ignore and try the next generator... + } + } + throw new IllegalStateException("All available seed generation strategies failed.") + } +} + diff --git a/akka-remote/src/main/scala/akka/security/provider/SeedSize.scala b/akka-remote/src/main/scala/akka/security/provider/SeedSize.scala new file mode 100644 index 0000000000..b92a170c45 --- /dev/null +++ b/akka-remote/src/main/scala/akka/security/provider/SeedSize.scala @@ -0,0 +1,16 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ + +package akka.security.provider + +/** + * Internal API + * From AESCounterRNG API docs: + * Valid values are 16 (128 bits), 24 (192 bits) and 32 (256 bits). + * Any other values will result in an exception from the AES implementation. + */ +sealed trait SeedSize { def size: Int } +case object Seed128 extends SeedSize { val size = 16 } +case object Seed192 extends SeedSize { val size = 24 } +case object Seed256 extends SeedSize { val size = 32 } \ No newline at end of file diff --git a/akka-remote/src/test/scala/akka/remote/Ticket1978CommunicationSpec.scala b/akka-remote/src/test/scala/akka/remote/Ticket1978CommunicationSpec.scala index 64408f15b1..b4bf6fa56b 100644 --- a/akka-remote/src/test/scala/akka/remote/Ticket1978CommunicationSpec.scala +++ b/akka-remote/src/test/scala/akka/remote/Ticket1978CommunicationSpec.scala @@ -83,6 +83,9 @@ class Ticket1978SHA1PRNGSpec extends Ticket1978CommunicationSpec(getCipherConfig @org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner]) class Ticket1978AES128CounterSecureRNGSpec extends Ticket1978CommunicationSpec(getCipherConfig("AES128CounterSecureRNG", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA")) +@org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner]) +class Ticket1978AES256CounterSecureRNGSpec extends Ticket1978CommunicationSpec(getCipherConfig("AES256CounterSecureRNG", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA")) + /** * Both of the Inet variants require access to the Internet to access random.org. */