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:
parent
56498e7e58
commit
b75bb8fc46
15 changed files with 282 additions and 66 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue