Deprecation and documentation for the AESCounterBuiltinRNG issue

* reproducer test of AESCounterBuiltinRNG issue
* update documentation of random-number-generator
* deprecate AES128CounterSecureRNG AES256CounterSecureRNG
* incorporate feedback from Johannes
This commit is contained in:
Patrik Nordwall 2018-08-27 15:13:40 +02:00
parent 56498e7e58
commit b75bb8fc46
15 changed files with 282 additions and 66 deletions

View file

@ -379,8 +379,6 @@ akka.remote.artery {
protocol = "TLSv1.2"
enabled-algorithms = [TLS_DHE_RSA_WITH_AES_128_GCM_SHA256]
random-number-generator = "AES128CounterSecureRNG"
}
}
```

View file

@ -469,8 +469,6 @@ akka {
protocol = "TLSv1.2"
enabled-algorithms = [TLS_DHE_RSA_WITH_AES_128_GCM_SHA256]
random-number-generator = "AES128CounterSecureRNG"
}
}
}

View file

@ -0,0 +1,93 @@
# Broken random number generators AES128CounterSecureRNG / AES256CounterSecureRNG, Fixed in Akka 2.5.16
### Date
29 August 2018
### Description of Vulnerability
A random number generator is used in Akka Remoting for TLS (both classic and Artery
Remoting). Akka allows to configure custom random number generators. For historical reasons,
Akka included the `AES128CounterSecureRNG` and `AES256CounterSecureRNG` random number
generators. The implementations had a bug that caused the generated numbers to be repeated
after only a few bytes.
The custom RNG implementations were not configured by default but examples in the
documentation showed (and therefore implicitly recommended) using the custom ones.
This can be used by an attacker to compromise the communication if these random number generators
are enabled in configuration. It would be possible to eavesdrop, replay or modify the messages sent with
Akka Remoting/Cluster.
To protect against such attacks the system should be updated to Akka *2.5.16* or later, or the default
configuration of the TLS random number generator should be used:
```
# Set `SecureRandom` RNG explicitly (but it is also the default)
akka.remote.netty.ssl.random-number-generator = "SecureRandom"
akka.remote.artery.ssl.config-ssl-engine.random-number-generator = "SecureRandom"
```
Please subscribe to the [akka-security](https://groups.google.com/forum/#!forum/akka-security) mailing list to be notified promptly about future security issues.
### Severity
The [CVSS](https://en.wikipedia.org/wiki/CVSS) score of this vulnerability is 5.9 (Medium), based on vector [AV:A/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N/E:U/RL:O/RC:C](https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator?vector=AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N/E:U/RL:O/RC:C).
Rationale for the score:
* AV:A - Best practice is that Akka remoting nodes should only be accessible from the adjacent network, so in
good setups, this will be adjacent.
* AC:H - Any one in the adjacent network can launch the attack with non-special access privileges,
but man-in-the-middle attacks are not trivial.
* C:H, I:H - Confidentiality and Integrity are only partially affected because only the networking component
is affected and not the whole Akka cluster. Assessed to be High anyway because access to actor system data would
probably be possible by injecting messages into the remoting communication.
### Affected Versions
* Akka *2.5.0 - 2.5.15* with any of the following configuration properties defined:
```
akka.remote.netty.ssl.random-number-generator = "AES128CounterSecureRNG"
akka.remote.netty.ssl.random-number-generator = "AES256CounterSecureRNG"
akka.remote.artery.ssl.config-ssl-engine.random-number-generator = "AES128CounterSecureRNG"
akka.remote.artery.ssl.config-ssl-engine.random-number-generator = "AES256CounterSecureRNG"
```
Akka *2.4.x* versions are not affected by this particular bug. It has reached
end-of-life since start of 2018. If you still run on Akka 2.4, we still
recommend to use the default `SecureRandom` implementation for the reasons
given below. Please check your configuration files not to configure the
custom RNGs.
### Fixed Versions
We have prepared patches for the affected versions, and have released the following version which resolve the issue:
* Akka *2.5.16* (Scala 2.11, 2.12)
Binary and source compatibility has been maintained for the patched releases so the upgrade procedure is as simple
as changing the library dependency.
The exact historical reasons to include custom RNG implementations could not be reconstructed
but it was likely because RNGs provided by previous versions of the JDK were deemed too slow.
Including custom cryptographic components in your library (or application) should not be done
lightly. We acknowledge that we cannot prove that the custom RNGs that Akka provides or has
been providing are generally correct or just correct enough for the purposes in Akka.
The reporter of this vulnerability, Rafał Sumisławski, kindly provided us with fixes for the
custom RNGs in Akka. However, as we cannot thoroughly verify the correctness of the algorithm
we decided to remove custom RNGs from Akka.
If the "AES128CounterSecureRNG" and "AES256CounterSecureRNG" configuration values are still used with Akka 2.5.16
they will be ignored and the default `SecureRandom` is used and a warning is logged. This is to avoid accidental
use of these unverified and possibly insecure implementations. The deprecated implementations are not recommended,
but they can be enabled by using configuration values "DeprecatedAES128CounterSecureRNG" or "DeprecatedAES256CounterSecureRNG"
during the transition period until they have been removed.
### Acknowledgements
We would like to thank Rafał Sumisławski at NetworkedAssets for bringing this issue to our attention and providing
a patch.

View file

@ -30,5 +30,6 @@ to ensure that a fix can be provided without delay.
* [2017-02-10-java-serialization](2017-02-10-java-serialization.md)
* [2017-08-09-camel](2017-08-09-camel.md)
* [2018-08-29-aes-rng](2018-08-29-aes-rng.md)
@@@

View file

@ -0,0 +1,8 @@
# AES random number generator issue, security was selected over compatibility
ProblemFilters.exclude[MissingClassProblem]("akka.remote.security.provider.AESCounterBuiltinRNG$AESKey")
ProblemFilters.exclude[MissingClassProblem]("akka.remote.security.provider.AESCounterBuiltinRNG")
ProblemFilters.exclude[MissingClassProblem]("akka.remote.security.provider.AESCounterBuiltinRNG$")
ProblemFilters.exclude[MissingClassProblem]("akka.remote.security.provider.AkkaProvider$")
ProblemFilters.exclude[MissingClassProblem]("akka.remote.security.provider.AES256CounterSecureRNG")
ProblemFilters.exclude[MissingClassProblem]("akka.remote.security.provider.AES128CounterSecureRNG")
ProblemFilters.exclude[MissingClassProblem]("akka.remote.security.provider.AkkaProvider")

View file

@ -666,13 +666,15 @@ akka {
# http://docs.oracle.com/javase/7/docs/technotes/guides/security/SunProviders.html#SunJCEProvider
enabled-algorithms = ["TLS_RSA_WITH_AES_128_CBC_SHA"]
# There are three options, in increasing order of security:
# "" or SecureRandom => (default)
# There are two options, and the default SecureRandom is recommended:
# "" or "SecureRandom" => (default)
# "SHA1PRNG" => Can be slow because of blocking issues on Linux
# "AES128CounterSecureRNG" => fastest startup and based on AES encryption
# algorithm
# "AES256CounterSecureRNG" (Install JCE Unlimited Strength Jurisdiction
# Policy Files first)
#
# "AES128CounterSecureRNG" and "AES256CounterSecureRNG" were deprecated in Akka
# 2.5.16 and if these configuration values are used it will use the default
# SecureRandom anyway and log a warning. The deprecated implementations are
# not recommended, but they can be enabled by using configuration values
# "DeprecatedAES128CounterSecureRNG" or "DeprecatedAES256CounterSecureRNG"
#
# Setting a value here may require you to supply the appropriate cipher
# suite (see enabled-algorithms section above)
@ -1158,13 +1160,15 @@ akka {
# http://docs.oracle.com/javase/7/docs/technotes/guides/security/SunProviders.html#SunJCEProvider
enabled-algorithms = ["TLS_RSA_WITH_AES_128_CBC_SHA"]
# There are three options, in increasing order of security:
# "" or SecureRandom => (default)
# There are two options, and the default SecureRandom is recommended:
# "" or "SecureRandom" => (default)
# "SHA1PRNG" => Can be slow because of blocking issues on Linux
# "AES128CounterSecureRNG" => fastest startup and based on AES encryption
# algorithm
# "AES256CounterSecureRNG" (Install JCE Unlimited Strength Jurisdiction
# Policy Files first)
#
# "AES128CounterSecureRNG" and "AES256CounterSecureRNG" were deprecated in Akka
# 2.5.16 and if these configuration values are used it will use the default
# SecureRandom anyway and log a warning. The deprecated implementations are
# not recommended, but they can be enabled by using configuration values
# "DeprecatedAES128CounterSecureRNG" or "DeprecatedAES256CounterSecureRNG"
#
# Setting a value here may require you to supply the appropriate cipher
# suite (see enabled-algorithms section above)

View file

@ -24,7 +24,7 @@ import akka.event.LogMarker
import akka.event.Logging
import akka.event.MarkerLoggingAdapter
import akka.japi.Util.immutableSeq
import akka.remote.security.provider.AkkaProvider
import akka.remote.security.provider.DeprecatedAkkaProvider
import akka.stream.IgnoreComplete
import akka.stream.TLSClosing
import akka.stream.TLSRole
@ -223,21 +223,28 @@ object SSLEngineProviderSetup {
@InternalApi private[akka] object SecureRandomFactory {
def createSecureRandom(randomNumberGenerator: String, log: MarkerLoggingAdapter): SecureRandom = {
val rng = randomNumberGenerator match {
case r @ ("AES128CounterSecureRNG" | "AES256CounterSecureRNG")
log.debug("SSL random number generator set to: {}", r)
SecureRandom.getInstance(r, AkkaProvider)
case s @ ("SHA1PRNG" | "NativePRNG")
log.debug("SSL random number generator set to: {}", s)
// SHA1PRNG needs /dev/urandom to be the source on Linux to prevent problems with /dev/random blocking
// However, this also makes the seed source insecure as the seed is reused to avoid blocking (not a problem on FreeBSD).
SecureRandom.getInstance(s)
case ""
log.debug("SSLRandomNumberGenerator not specified, falling back to SecureRandom")
case "" | "SecureRandom"
log.debug("SSL random number generator set to [SecureRandom]")
new SecureRandom
case r @ ("AES128CounterSecureRNG" | "AES256CounterSecureRNG")
log.warning("SSL random number generator set to deprecated [{}], using [SecureRandom] instead. " +
"The [{}] implementation can be enabled with configuration value [Deprecated{r}], " +
"but that is not recommended.", r, r, r)
new SecureRandom
case r @ ("DeprecatedAES128CounterSecureRNG" | "DeprecatedAES256CounterSecureRNG")
log.warning("SSL random number generator set to deprecated [{}]. Use [SecureRandom] instead.", r)
SecureRandom.getInstance(r, DeprecatedAkkaProvider)
case unknown
log.warning(LogMarker.Security, "Unknown SSLRandomNumberGenerator [{}] falling back to SecureRandom", unknown)
log.warning(LogMarker.Security, "Unknown SSL random number generator [{}] falling back to SecureRandom", unknown)
new SecureRandom
}
rng.nextInt() // prevent stall on first access

View file

@ -1,26 +0,0 @@
/**
* Copyright (C) 2009-2018 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.remote.security.provider
import java.security.{ PrivilegedAction, AccessController, Provider }
/**
* A provider that for AES128CounterRNGFast, a cryptographically secure random number generator through SecureRandom
*/
object AkkaProvider extends Provider("Akka", 1.0, "Akka provider 1.0 that implements a secure AES random number generator") {
AccessController.doPrivileged(new PrivilegedAction[this.type] {
def run = {
//SecureRandom
put("SecureRandom.AES128CounterSecureRNG", classOf[AES128CounterSecureRNG].getName)
put("SecureRandom.AES256CounterSecureRNG", classOf[AES256CounterSecureRNG].getName)
//Implementation type: software or hardware
put("SecureRandom.AES128CounterSecureRNG ImplementedIn", "Software")
put("SecureRandom.AES256CounterSecureRNG ImplementedIn", "Software")
null //Magic null is magic
}
})
}

View file

@ -14,13 +14,15 @@ import scala.concurrent.ExecutionContext
/**
* This class is a wrapper around the 128-bit AESCounterBuiltinRNG AES/CTR PRNG algorithm
* The only method used by netty ssl is engineNextBytes(bytes)
*
*/
class AES128CounterSecureRNG extends java.security.SecureRandomSpi {
@deprecated("Use SecureRandom instead. We cannot prove that this code is correct, see https://doc.akka.io/docs/akka/current/security/2018-08-29-aes-rng.html", "2.5.16")
class DeprecatedAES128CounterSecureRNG extends java.security.SecureRandomSpi {
private val singleThreadPool = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor(new AESCounterBuiltinRNGReSeeder))
private val entropySource = new SecureRandom
private val seed = entropySource.generateSeed(Seed128)
private val rng = new AESCounterBuiltinRNG(seed, singleThreadPool)
private val rng = new DeprecatedAESCounterBuiltinRNG(seed, singleThreadPool)
/**
* This is managed internally by AESCounterBuiltinRNG

View file

@ -15,12 +15,13 @@ import scala.concurrent.ExecutionContext
* This class is a wrapper around the 256-bit AESCounterBuiltinRNG AES/CTR PRNG algorithm
* The only method used by netty ssl is engineNextBytes(bytes)
*/
class AES256CounterSecureRNG extends java.security.SecureRandomSpi {
@deprecated("Use SecureRandom instead. We cannot prove that this code is correct, see https://doc.akka.io/docs/akka/current/security/2018-08-29-aes-rng.html", "2.5.16")
class DeprecatedAES256CounterSecureRNG extends java.security.SecureRandomSpi {
private val singleThreadPool = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor(new AESCounterBuiltinRNGReSeeder))
private val entropySource = new SecureRandom
private val seed = entropySource.generateSeed(Seed256)
private val rng = new AESCounterBuiltinRNG(seed, singleThreadPool)
private val rng = new DeprecatedAESCounterBuiltinRNG(seed, singleThreadPool)
/**
* This is managed internally by AESCounterBuiltinRNG

View file

@ -17,6 +17,11 @@ import scala.concurrent.{ Await, ExecutionContext, Future, duration }
/**
* INTERNAL API
*
* We cannot prove that this code is correct and it will therefore be removed
* with AES128CounterSecureRNG and AES256CounterSecureRNG. See security
* vulnerability https://doc.akka.io/docs/akka/current/security/2018-08-29-aes-rng.html
*
* This class is a Scala implementation of AESCounterRNG algorithm
* patterned after org.uncommons.maths.random by Daniel Dyer (Apache License 2.0)
*
@ -32,10 +37,10 @@ import scala.concurrent.{ Await, ExecutionContext, Future, duration }
* NOTE: this class is not serializable
*/
@InternalApi
private[akka] class AESCounterBuiltinRNG(val seed: Array[Byte], implicit val executionContext: ExecutionContext,
val reseedingThreshold: Long = CounterRNGConstants.ReseedingThreshold,
val reseedingDeadline: Long = CounterRNGConstants.ReseedingDeadline,
val reseedingTimeout: Duration = CounterRNGConstants.ReseedingTimeout) extends Random {
private[akka] class DeprecatedAESCounterBuiltinRNG(val seed: Array[Byte], implicit val executionContext: ExecutionContext,
val reseedingThreshold: Long = CounterRNGConstants.ReseedingThreshold,
val reseedingDeadline: Long = CounterRNGConstants.ReseedingDeadline,
val reseedingTimeout: Duration = CounterRNGConstants.ReseedingTimeout) extends Random {
import CounterRNGConstants._
private val entropySource = new SecureRandom

View file

@ -0,0 +1,28 @@
/**
* Copyright (C) 2009-2018 Lightbend Inc. <https://www.lightbend.com>
*/
package akka.remote.security.provider
import java.security.{ PrivilegedAction, AccessController, Provider }
/**
* A provider that for AES128CounterRNGFast, a cryptographically secure random number generator through SecureRandom
*
*/
@deprecated("Use SecureRandom instead. We cannot prove that this code is correct, see https://doc.akka.io/docs/akka/current/security/2018-08-29-aes-rng.html", "2.5.16")
object DeprecatedAkkaProvider extends Provider("Akka", 1.0, "Akka provider 1.0 that implements a secure AES random number generator") {
AccessController.doPrivileged(new PrivilegedAction[this.type] {
def run = {
//SecureRandom
put("SecureRandom.DeprecatedAES128CounterSecureRNG", classOf[DeprecatedAES128CounterSecureRNG].getName)
put("SecureRandom.DeprecatedAES256CounterSecureRNG", classOf[DeprecatedAES256CounterSecureRNG].getName)
//Implementation type: software or hardware
put("SecureRandom.DeprecatedAES128CounterSecureRNG ImplementedIn", "Software")
put("SecureRandom.DeprecatedAES256CounterSecureRNG ImplementedIn", "Software")
null //Magic null is magic
}
})
}

View file

@ -7,7 +7,7 @@ package akka.remote.transport.netty
import java.security._
import akka.japi.Util._
import akka.remote.security.provider.AkkaProvider
import akka.remote.security.provider.DeprecatedAkkaProvider
import com.typesafe.config.Config
import org.jboss.netty.handler.ssl.SslHandler
@ -46,7 +46,7 @@ private[akka] class SSLSettings(config: Config) {
private[akka] object NettySSLSupport {
// TODO is this needed in Artery TLS?
Security addProvider AkkaProvider
Security addProvider DeprecatedAkkaProvider
/**
* Construct a SSLHandler which can be inserted into a Netty server/client pipeline

View file

@ -4,7 +4,9 @@
package akka.remote
import java.io.ByteArrayOutputStream
import java.security.NoSuchAlgorithmException
import java.util.zip.GZIPOutputStream
import akka.actor._
import akka.event.NoMarkerLogging
@ -54,7 +56,8 @@ object Configuration {
}
"""
final case class CipherConfig(runTest: Boolean, config: Config, cipher: String, localPort: Int, remotePort: Int)
final case class CipherConfig(runTest: Boolean, config: Config, cipher: String, localPort: Int, remotePort: Int,
provider: Option[ConfigSSLEngineProvider])
def getCipherConfig(cipher: String, enabled: String*): CipherConfig = {
val localPort, remotePort = { val s = new java.net.ServerSocket(0); try s.getLocalPort finally s.close() }
@ -69,7 +72,10 @@ object Configuration {
val rng = sslEngineProvider.createSecureRandom()
rng.nextInt() // Has to work
val sRng = settings.SSLRandomNumberGenerator
val sRng = settings.SSLRandomNumberGenerator match {
case "AES128CounterSecureRNG" | "AES256CounterSecureRNG" ""
case other other
}
if (rng.getAlgorithm != sRng && sRng != "")
throw new NoSuchAlgorithmException(sRng)
@ -81,9 +87,10 @@ object Configuration {
engine.getSupportedProtocols.contains(settings.SSLProtocol) ||
(throw new IllegalArgumentException("Protocol not supported: " + settings.SSLProtocol))
CipherConfig(true, config, cipher, localPort, remotePort)
CipherConfig(true, config, cipher, localPort, remotePort, Some(sslEngineProvider))
} catch {
case (_: IllegalArgumentException) | (_: NoSuchAlgorithmException) CipherConfig(false, AkkaSpec.testConf, cipher, localPort, remotePort) // Cannot match against the message since the message might be localized :S
case _: IllegalArgumentException | _: NoSuchAlgorithmException
CipherConfig(false, AkkaSpec.testConf, cipher, localPort, remotePort, None) // Cannot match against the message since the message might be localized :S
}
}
}
@ -92,13 +99,17 @@ class Ticket1978SHA1PRNGSpec extends Ticket1978CommunicationSpec(getCipherConfig
class Ticket1978AES128CounterSecureRNGSpec extends Ticket1978CommunicationSpec(getCipherConfig("AES128CounterSecureRNG", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA"))
class Ticket1978DeprecatedAES128CounterSecureRNGSpec extends Ticket1978CommunicationSpec(getCipherConfig("DeprecatedAES128CounterSecureRNG", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA"))
class Ticket1978AES256CounterSecureRNGSpec extends Ticket1978CommunicationSpec(getCipherConfig("AES256CounterSecureRNG", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA"))
class Ticket1978DeprecatedAES256CounterSecureRNGSpec extends Ticket1978CommunicationSpec(getCipherConfig("DeprecatedAES256CounterSecureRNG", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA"))
class Ticket1978DefaultRNGSecureSpec extends Ticket1978CommunicationSpec(getCipherConfig("", "TLS_RSA_WITH_AES_128_CBC_SHA"))
class Ticket1978CrappyRSAWithMD5OnlyHereToMakeSureThingsWorkSpec extends Ticket1978CommunicationSpec(getCipherConfig("", "SSL_RSA_WITH_NULL_MD5"))
class Ticket1978NonExistingRNGSecureSpec extends Ticket1978CommunicationSpec(CipherConfig(false, AkkaSpec.testConf, "NonExistingRNG", 12345, 12346))
class Ticket1978NonExistingRNGSecureSpec extends Ticket1978CommunicationSpec(CipherConfig(false, AkkaSpec.testConf, "NonExistingRNG", 12345, 12346, None))
abstract class Ticket1978CommunicationSpec(val cipherConfig: CipherConfig) extends AkkaSpec(cipherConfig.config) with ImplicitSender {
@ -121,6 +132,38 @@ abstract class Ticket1978CommunicationSpec(val cipherConfig: CipherConfig) exten
val ignoreMe = other.actorOf(Props(new Actor { def receive = { case ("ping", x) sender() ! ((("pong", x), sender())) } }), "echo")
val otherAddress = other.asInstanceOf[ExtendedActorSystem].provider.asInstanceOf[RemoteActorRefProvider].transport.defaultAddress
"generate random" in {
val rng = cipherConfig.provider.get.createSecureRandom()
val bytes = Array.ofDim[Byte](16)
// awaitAssert just in case we are very unlucky to get same sequence more than once
awaitAssert {
val randomBytes = (1 to 10).map { n
rng.nextBytes(bytes)
bytes.toVector
}.toSet
randomBytes.size should ===(10)
}
}
"have random numbers that are not compressable, because then they are not random" in {
val provider = new ConfigSSLEngineProvider(system)
val rng = provider.createSecureRandom()
val randomData = new Array[Byte](1024 * 1024)
rng.nextBytes(randomData)
val baos = new ByteArrayOutputStream()
val gzipped = new GZIPOutputStream(baos)
try gzipped.write(randomData)
finally gzipped.close()
val compressed = baos.toByteArray
// random data should not be compressible
// Another reproducer of https://doc.akka.io/docs/akka/current/security/2018-08-29-aes-rng.html
// with the broken implementation the compressed size was <5k
compressed.size should be > randomData.length
}
"support tell" in within(timeout.duration) {
val here = {
system.actorSelection(otherAddress.toString + "/user/echo") ! Identify(None)

View file

@ -5,7 +5,9 @@
package akka.remote.artery
package tcp
import java.io.ByteArrayOutputStream
import java.security.NoSuchAlgorithmException
import java.util.zip.GZIPOutputStream
import scala.concurrent.duration._
@ -39,6 +41,13 @@ class TlsTcpWithAES128CounterSecureRNGSpec extends TlsTcpSpec(ConfigFactory.pars
}
"""))
class TlsTcpWithDeprecatedAES128CounterSecureRNGSpec extends TlsTcpSpec(ConfigFactory.parseString("""
akka.remote.artery.ssl.config-ssl-engine {
random-number-generator = "DeprecatedAES128CounterSecureRNG"
enabled-algorithms = ["TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA"]
}
"""))
class TlsTcpWithAES256CounterSecureRNGSpec extends TlsTcpSpec(ConfigFactory.parseString("""
akka.remote.artery.ssl.config-ssl-engine {
random-number-generator = "AES256CounterSecureRNG"
@ -46,6 +55,13 @@ class TlsTcpWithAES256CounterSecureRNGSpec extends TlsTcpSpec(ConfigFactory.pars
}
"""))
class TlsTcpWithDeprecatedAES256CounterSecureRNGSpec extends TlsTcpSpec(ConfigFactory.parseString("""
akka.remote.artery.ssl.config-ssl-engine {
random-number-generator = "DeprecatedAES256CounterSecureRNG"
enabled-algorithms = ["TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_256_CBC_SHA"]
}
"""))
class TlsTcpWithDefaultRNGSecureSpec extends TlsTcpSpec(ConfigFactory.parseString("""
akka.remote.artery.ssl.config-ssl-engine {
random-number-generator = ""
@ -86,7 +102,10 @@ abstract class TlsTcpSpec(config: Config)
val rng = provider.createSecureRandom()
rng.nextInt() // Has to work
val sRng = provider.SSLRandomNumberGenerator
val sRng = provider.SSLRandomNumberGenerator match {
case "AES128CounterSecureRNG" | "AES256CounterSecureRNG" ""
case other other
}
if (rng.getAlgorithm != sRng && sRng != "")
throw new NoSuchAlgorithmException(sRng)
@ -102,7 +121,7 @@ abstract class TlsTcpSpec(config: Config)
engine.getSupportedProtocols.contains(provider.SSLProtocol) ||
(throw new IllegalArgumentException("Protocol not supported: " + provider.SSLProtocol))
} catch {
case e @ ((_: IllegalArgumentException) | (_: NoSuchAlgorithmException))
case e @ (_: IllegalArgumentException | _: NoSuchAlgorithmException)
info(e.toString)
false
}
@ -128,6 +147,41 @@ abstract class TlsTcpSpec(config: Config)
if (isSupported) {
"generate random" in {
val provider = new ConfigSSLEngineProvider(system)
val rng = provider.createSecureRandom()
val bytes = Array.ofDim[Byte](16)
// Reproducer of the specific issue described at
// https://doc.akka.io/docs/akka/current/security/2018-08-29-aes-rng.html
// awaitAssert just in case we are very unlucky to get same sequence more than once
awaitAssert {
val randomBytes = (1 to 10).map { n
rng.nextBytes(bytes)
bytes.toVector
}.toSet
randomBytes.size should ===(10)
}
}
"have random numbers that are not compressable, because then they are not random" in {
val provider = new ConfigSSLEngineProvider(system)
val rng = provider.createSecureRandom()
val randomData = new Array[Byte](1024 * 1024)
rng.nextBytes(randomData)
val baos = new ByteArrayOutputStream()
val gzipped = new GZIPOutputStream(baos)
try gzipped.write(randomData)
finally gzipped.close()
val compressed = baos.toByteArray
// random data should not be compressible
// Another reproducer of https://doc.akka.io/docs/akka/current/security/2018-08-29-aes-rng.html
// with the broken implementation the compressed size was <5k
compressed.size should be > randomData.length
}
"deliver messages" in {
systemB.actorOf(TestActors.echoActorProps, "echo")
val echoRef = identify(rootB / "user" / "echo")