2014-06-20 20:00:00 +01:00
package main
import (
"bytes"
2014-08-24 06:46:07 +01:00
"crypto/ecdsa"
"crypto/elliptic"
2014-07-16 17:58:59 +01:00
"crypto/x509"
2014-08-24 06:25:27 +01:00
"encoding/base64"
2014-08-24 06:46:07 +01:00
"encoding/pem"
2014-06-20 20:00:00 +01:00
"flag"
"fmt"
"io"
2014-08-05 23:23:37 +01:00
"io/ioutil"
2014-06-20 20:00:00 +01:00
"net"
"os"
"os/exec"
2014-08-02 20:28:23 +01:00
"path"
2014-08-02 20:22:37 +01:00
"runtime"
2014-11-18 01:26:55 +00:00
"strconv"
2014-06-20 20:00:00 +01:00
"strings"
"sync"
"syscall"
Add DTLS timeout and retransmit tests.
This extends the packet adaptor protocol to send three commands:
type command =
| Packet of []byte
| Timeout of time.Duration
| TimeoutAck
When the shim processes a Timeout in BIO_read, it sends TimeoutAck, fails the
BIO_read, returns out of the SSL stack, advances the clock, calls
DTLSv1_handle_timeout, and continues.
If the Go side sends Timeout right between sending handshake flight N and
reading flight N+1, the shim won't read the Timeout until it has sent flight
N+1 (it only processes packet commands in BIO_read), so the TimeoutAck comes
after N+1. Go then drops all packets before the TimeoutAck, thus dropping one
transmit of flight N+1 without having to actually process the packets to
determine the end of the flight. The shim then sees the updated clock, calls
DTLSv1_handle_timeout, and re-sends flight N+1 for Go to process for real.
When dropping packets, Go checks the epoch and increments sequence numbers so
that we can continue to be strict here. This requires tracking the initial
sequence number of the next epoch.
The final Finished message takes an additional special-case to test. DTLS
triggers retransmits on either a timeout or seeing a stale flight. OpenSSL only
implements the former which should be sufficient (and is necessary) EXCEPT for
the final Finished message. If the peer's final Finished message is lost, it
won't be waiting for a message from us, so it won't time out anything. That
retransmit must be triggered on stale message, so we retransmit the Finished
message in Go.
Change-Id: I3ffbdb1de525beb2ee831b304670a3387877634c
Reviewed-on: https://boringssl-review.googlesource.com/3212
Reviewed-by: Adam Langley <agl@google.com>
2015-01-27 06:09:43 +00:00
"time"
2014-06-20 20:00:00 +01:00
)
2014-11-18 01:26:55 +00:00
var (
useValgrind = flag . Bool ( "valgrind" , false , "If true, run code under valgrind" )
useGDB = flag . Bool ( "gdb" , false , "If true, run BoringSSL code under gdb" )
flagDebug * bool = flag . Bool ( "debug" , false , "Hexdump the contents of the connection" )
mallocTest * int64 = flag . Int64 ( "malloc-test" , - 1 , "If non-negative, run each test with each malloc in turn failing from the given number onwards." )
mallocTestDebug * bool = flag . Bool ( "malloc-test-debug" , false , "If true, ask bssl_shim to abort rather than fail a malloc. This can be used with a specific value for --malloc-test to identity the malloc failing that is causing problems." )
)
2014-06-20 20:00:00 +01:00
2014-07-02 00:53:04 +01:00
const (
rsaCertificateFile = "cert.pem"
ecdsaCertificateFile = "ecdsa_cert.pem"
)
const (
2014-08-24 06:46:07 +01:00
rsaKeyFile = "key.pem"
ecdsaKeyFile = "ecdsa_key.pem"
channelIDKeyFile = "channel_id_key.pem"
2014-07-02 00:53:04 +01:00
)
2014-06-20 20:00:00 +01:00
var rsaCertificate , ecdsaCertificate Certificate
2014-08-24 06:46:07 +01:00
var channelIDKey * ecdsa . PrivateKey
var channelIDBytes [ ] byte
2014-06-20 20:00:00 +01:00
2014-11-25 06:55:35 +00:00
var testOCSPResponse = [ ] byte { 1 , 2 , 3 , 4 }
var testSCTList = [ ] byte { 5 , 6 , 7 , 8 }
2014-06-20 20:00:00 +01:00
func initCertificates ( ) {
var err error
2014-07-02 00:53:04 +01:00
rsaCertificate , err = LoadX509KeyPair ( rsaCertificateFile , rsaKeyFile )
2014-06-20 20:00:00 +01:00
if err != nil {
panic ( err )
}
2014-11-25 06:55:35 +00:00
rsaCertificate . OCSPStaple = testOCSPResponse
rsaCertificate . SignedCertificateTimestampList = testSCTList
2014-06-20 20:00:00 +01:00
2014-07-02 00:53:04 +01:00
ecdsaCertificate , err = LoadX509KeyPair ( ecdsaCertificateFile , ecdsaKeyFile )
2014-06-20 20:00:00 +01:00
if err != nil {
panic ( err )
}
2014-11-25 06:55:35 +00:00
ecdsaCertificate . OCSPStaple = testOCSPResponse
ecdsaCertificate . SignedCertificateTimestampList = testSCTList
2014-08-24 06:46:07 +01:00
channelIDPEMBlock , err := ioutil . ReadFile ( channelIDKeyFile )
if err != nil {
panic ( err )
}
channelIDDERBlock , _ := pem . Decode ( channelIDPEMBlock )
if channelIDDERBlock . Type != "EC PRIVATE KEY" {
panic ( "bad key type" )
}
channelIDKey , err = x509 . ParseECPrivateKey ( channelIDDERBlock . Bytes )
if err != nil {
panic ( err )
}
if channelIDKey . Curve != elliptic . P256 ( ) {
panic ( "bad curve" )
}
channelIDBytes = make ( [ ] byte , 64 )
writeIntPadded ( channelIDBytes [ : 32 ] , channelIDKey . X )
writeIntPadded ( channelIDBytes [ 32 : ] , channelIDKey . Y )
2014-06-20 20:00:00 +01:00
}
var certificateOnce sync . Once
func getRSACertificate ( ) Certificate {
certificateOnce . Do ( initCertificates )
return rsaCertificate
}
func getECDSACertificate ( ) Certificate {
certificateOnce . Do ( initCertificates )
return ecdsaCertificate
}
2014-07-02 00:53:04 +01:00
type testType int
const (
clientTest testType = iota
serverTest
)
2014-08-11 23:43:38 +01:00
type protocol int
const (
tls protocol = iota
dtls
)
2014-09-06 18:21:53 +01:00
const (
alpn = 1
npn = 2
)
2014-06-20 20:00:00 +01:00
type testCase struct {
2014-07-02 00:53:04 +01:00
testType testType
2014-08-11 23:43:38 +01:00
protocol protocol
2014-06-20 20:00:00 +01:00
name string
config Config
shouldFail bool
expectedError string
2014-06-23 20:03:11 +01:00
// expectedLocalError, if not empty, contains a substring that must be
// found in the local error.
expectedLocalError string
2014-08-07 22:44:24 +01:00
// expectedVersion, if non-zero, specifies the TLS version that must be
// negotiated.
expectedVersion uint16
2014-09-24 20:21:44 +01:00
// expectedResumeVersion, if non-zero, specifies the TLS version that
// must be negotiated on resumption. If zero, expectedVersion is used.
expectedResumeVersion uint16
2014-08-24 06:46:07 +01:00
// expectChannelID controls whether the connection should have
// negotiated a Channel ID with channelIDKey.
expectChannelID bool
2014-09-06 17:58:58 +01:00
// expectedNextProto controls whether the connection should
// negotiate a next protocol via NPN or ALPN.
expectedNextProto string
2014-09-06 18:21:53 +01:00
// expectedNextProtoType, if non-zero, is the expected next
// protocol negotiation mechanism.
expectedNextProtoType int
2014-11-16 00:06:08 +00:00
// expectedSRTPProtectionProfile is the DTLS-SRTP profile that
// should be negotiated. If zero, none should be negotiated.
expectedSRTPProtectionProfile uint16
2014-06-20 20:00:00 +01:00
// messageLen is the length, in bytes, of the test message that will be
// sent.
messageLen int
2014-07-02 00:53:04 +01:00
// certFile is the path to the certificate to use for the server.
certFile string
// keyFile is the path to the private key to use for the server.
keyFile string
2014-07-23 00:20:02 +01:00
// resumeSession controls whether a second connection should be tested
2014-09-24 20:21:44 +01:00
// which attempts to resume the first session.
2014-07-23 00:20:02 +01:00
resumeSession bool
2014-09-24 20:21:44 +01:00
// resumeConfig, if not nil, points to a Config to be used on
2014-11-17 08:19:02 +00:00
// resumption. Unless newSessionsOnResume is set,
// SessionTicketKey, ServerSessionCache, and
// ClientSessionCache are copied from the initial connection's
// config. If nil, the initial connection's config is used.
2014-09-24 20:21:44 +01:00
resumeConfig * Config
2014-11-17 08:19:02 +00:00
// newSessionsOnResume, if true, will cause resumeConfig to
// use a different session resumption context.
newSessionsOnResume bool
2014-08-08 18:24:34 +01:00
// sendPrefix sends a prefix on the socket before actually performing a
// handshake.
sendPrefix string
2014-08-24 08:47:07 +01:00
// shimWritesFirst controls whether the shim sends an initial "hello"
// message before doing a roundtrip with the runner.
shimWritesFirst bool
2014-10-29 02:06:14 +00:00
// renegotiate indicates the the connection should be renegotiated
// during the exchange.
renegotiate bool
// renegotiateCiphers is a list of ciphersuite ids that will be
// switched in just before renegotiation.
renegotiateCiphers [ ] uint16
2014-11-07 06:48:35 +00:00
// replayWrites, if true, configures the underlying transport
// to replay every write it makes in DTLS tests.
replayWrites bool
2015-01-22 21:35:40 +00:00
// damageFirstWrite, if true, configures the underlying transport to
// damage the final byte of the first application data write.
damageFirstWrite bool
2014-07-02 00:40:31 +01:00
// flags, if not empty, contains a list of command-line flags that will
// be passed to the shim program.
flags [ ] string
2014-06-20 20:00:00 +01:00
}
2014-07-02 00:53:04 +01:00
var testCases = [ ] testCase {
2014-06-20 20:00:00 +01:00
{
name : "BadRSASignature" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
Bugs : ProtocolBugs {
InvalidSKXSignature : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":BAD_SIGNATURE:" ,
} ,
{
name : "BadECDSASignature" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 } ,
Bugs : ProtocolBugs {
InvalidSKXSignature : true ,
} ,
Certificates : [ ] Certificate { getECDSACertificate ( ) } ,
} ,
shouldFail : true ,
expectedError : ":BAD_SIGNATURE:" ,
} ,
{
name : "BadECDSACurve" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 } ,
Bugs : ProtocolBugs {
InvalidSKXCurve : true ,
} ,
Certificates : [ ] Certificate { getECDSACertificate ( ) } ,
} ,
shouldFail : true ,
expectedError : ":WRONG_CURVE:" ,
} ,
2014-08-07 03:11:10 +01:00
{
testType : serverTest ,
name : "BadRSAVersion" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_RSA_WITH_RC4_128_SHA } ,
Bugs : ProtocolBugs {
RsaClientKeyExchangeVersion : VersionTLS11 ,
} ,
} ,
shouldFail : true ,
expectedError : ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" ,
} ,
2014-06-23 20:03:11 +01:00
{
2014-07-02 00:40:31 +01:00
name : "NoFallbackSCSV" ,
2014-06-23 20:03:11 +01:00
config : Config {
Bugs : ProtocolBugs {
FailIfNotFallbackSCSV : true ,
} ,
} ,
shouldFail : true ,
expectedLocalError : "no fallback SCSV found" ,
} ,
2014-07-02 00:40:31 +01:00
{
2014-08-23 04:46:35 +01:00
name : "SendFallbackSCSV" ,
2014-07-02 00:40:31 +01:00
config : Config {
Bugs : ProtocolBugs {
FailIfNotFallbackSCSV : true ,
} ,
} ,
flags : [ ] string { "-fallback-scsv" } ,
} ,
2014-07-08 22:30:11 +01:00
{
name : "ClientCertificateTypes" ,
config : Config {
ClientAuth : RequestClientCert ,
ClientCertificateTypes : [ ] byte {
CertTypeDSSSign ,
CertTypeRSASign ,
CertTypeECDSASign ,
} ,
} ,
2014-08-24 06:25:27 +01:00
flags : [ ] string {
"-expect-certificate-types" ,
base64 . StdEncoding . EncodeToString ( [ ] byte {
CertTypeDSSSign ,
CertTypeRSASign ,
CertTypeECDSASign ,
} ) ,
} ,
2014-07-08 22:30:11 +01:00
} ,
2014-07-08 22:59:18 +01:00
{
name : "NoClientCertificate" ,
config : Config {
ClientAuth : RequireAnyClientCert ,
} ,
shouldFail : true ,
expectedLocalError : "client didn't provide a certificate" ,
} ,
2014-07-12 05:48:23 +01:00
{
name : "UnauthenticatedECDH" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
Bugs : ProtocolBugs {
UnauthenticatedECDH : true ,
} ,
} ,
shouldFail : true ,
2014-07-12 06:10:19 +01:00
expectedError : ":UNEXPECTED_MESSAGE:" ,
2014-07-12 05:48:23 +01:00
} ,
2014-07-12 18:27:45 +01:00
{
name : "SkipServerKeyExchange" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
Bugs : ProtocolBugs {
SkipServerKeyExchange : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":UNEXPECTED_MESSAGE:" ,
} ,
2014-07-19 22:39:58 +01:00
{
name : "SkipChangeCipherSpec-Client" ,
config : Config {
Bugs : ProtocolBugs {
SkipChangeCipherSpec : true ,
} ,
} ,
shouldFail : true ,
2014-07-21 21:14:03 +01:00
expectedError : ":HANDSHAKE_RECORD_BEFORE_CCS:" ,
2014-07-19 22:39:58 +01:00
} ,
{
testType : serverTest ,
name : "SkipChangeCipherSpec-Server" ,
config : Config {
Bugs : ProtocolBugs {
SkipChangeCipherSpec : true ,
} ,
} ,
shouldFail : true ,
2014-07-21 21:14:03 +01:00
expectedError : ":HANDSHAKE_RECORD_BEFORE_CCS:" ,
2014-07-19 22:39:58 +01:00
} ,
2014-07-21 19:50:23 +01:00
{
testType : serverTest ,
name : "SkipChangeCipherSpec-Server-NPN" ,
config : Config {
NextProtos : [ ] string { "bar" } ,
Bugs : ProtocolBugs {
SkipChangeCipherSpec : true ,
} ,
} ,
flags : [ ] string {
"-advertise-npn" , "\x03foo\x03bar\x03baz" ,
} ,
shouldFail : true ,
2014-07-21 21:14:03 +01:00
expectedError : ":HANDSHAKE_RECORD_BEFORE_CCS:" ,
} ,
{
name : "FragmentAcrossChangeCipherSpec-Client" ,
config : Config {
Bugs : ProtocolBugs {
FragmentAcrossChangeCipherSpec : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":HANDSHAKE_RECORD_BEFORE_CCS:" ,
} ,
{
testType : serverTest ,
name : "FragmentAcrossChangeCipherSpec-Server" ,
config : Config {
Bugs : ProtocolBugs {
FragmentAcrossChangeCipherSpec : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":HANDSHAKE_RECORD_BEFORE_CCS:" ,
} ,
{
testType : serverTest ,
name : "FragmentAcrossChangeCipherSpec-Server-NPN" ,
config : Config {
NextProtos : [ ] string { "bar" } ,
Bugs : ProtocolBugs {
FragmentAcrossChangeCipherSpec : true ,
} ,
} ,
flags : [ ] string {
"-advertise-npn" , "\x03foo\x03bar\x03baz" ,
} ,
shouldFail : true ,
expectedError : ":HANDSHAKE_RECORD_BEFORE_CCS:" ,
2014-07-21 19:50:23 +01:00
} ,
2015-02-03 21:07:32 +00:00
{
testType : serverTest ,
name : "Alert" ,
config : Config {
Bugs : ProtocolBugs {
SendSpuriousAlert : alertRecordOverflow ,
} ,
} ,
shouldFail : true ,
expectedError : ":TLSV1_ALERT_RECORD_OVERFLOW:" ,
} ,
{
protocol : dtls ,
testType : serverTest ,
name : "Alert-DTLS" ,
config : Config {
Bugs : ProtocolBugs {
SendSpuriousAlert : alertRecordOverflow ,
} ,
} ,
shouldFail : true ,
expectedError : ":TLSV1_ALERT_RECORD_OVERFLOW:" ,
} ,
2014-11-01 23:39:08 +00:00
{
testType : serverTest ,
name : "FragmentAlert" ,
config : Config {
Bugs : ProtocolBugs {
2014-11-16 00:06:08 +00:00
FragmentAlert : true ,
2015-02-03 21:07:32 +00:00
SendSpuriousAlert : alertRecordOverflow ,
2014-11-01 23:39:08 +00:00
} ,
} ,
shouldFail : true ,
expectedError : ":BAD_ALERT:" ,
} ,
2015-02-01 01:33:40 +00:00
{
protocol : dtls ,
testType : serverTest ,
name : "FragmentAlert-DTLS" ,
config : Config {
Bugs : ProtocolBugs {
FragmentAlert : true ,
2015-02-03 21:07:32 +00:00
SendSpuriousAlert : alertRecordOverflow ,
2015-02-01 01:33:40 +00:00
} ,
} ,
shouldFail : true ,
expectedError : ":BAD_ALERT:" ,
} ,
2014-07-22 03:42:34 +01:00
{
testType : serverTest ,
name : "EarlyChangeCipherSpec-server-1" ,
config : Config {
Bugs : ProtocolBugs {
EarlyChangeCipherSpec : 1 ,
} ,
} ,
shouldFail : true ,
expectedError : ":CCS_RECEIVED_EARLY:" ,
} ,
{
testType : serverTest ,
name : "EarlyChangeCipherSpec-server-2" ,
config : Config {
Bugs : ProtocolBugs {
EarlyChangeCipherSpec : 2 ,
} ,
} ,
shouldFail : true ,
expectedError : ":CCS_RECEIVED_EARLY:" ,
} ,
2014-07-23 20:09:48 +01:00
{
name : "SkipNewSessionTicket" ,
config : Config {
Bugs : ProtocolBugs {
SkipNewSessionTicket : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":CCS_RECEIVED_EARLY:" ,
} ,
2014-08-02 09:22:02 +01:00
{
testType : serverTest ,
name : "FallbackSCSV" ,
config : Config {
MaxVersion : VersionTLS11 ,
Bugs : ProtocolBugs {
SendFallbackSCSV : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":INAPPROPRIATE_FALLBACK:" ,
} ,
{
testType : serverTest ,
name : "FallbackSCSV-VersionMatch" ,
config : Config {
Bugs : ProtocolBugs {
SendFallbackSCSV : true ,
} ,
} ,
} ,
2014-08-07 23:02:39 +01:00
{
testType : serverTest ,
name : "FragmentedClientVersion" ,
config : Config {
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : 1 ,
FragmentClientVersion : true ,
} ,
} ,
2014-12-12 20:55:27 +00:00
expectedVersion : VersionTLS12 ,
2014-08-07 23:02:39 +01:00
} ,
2014-08-08 18:24:34 +01:00
{
testType : serverTest ,
name : "MinorVersionTolerance" ,
config : Config {
Bugs : ProtocolBugs {
SendClientVersion : 0x03ff ,
} ,
} ,
expectedVersion : VersionTLS12 ,
} ,
{
testType : serverTest ,
name : "MajorVersionTolerance" ,
config : Config {
Bugs : ProtocolBugs {
SendClientVersion : 0x0400 ,
} ,
} ,
expectedVersion : VersionTLS12 ,
} ,
{
testType : serverTest ,
name : "VersionTooLow" ,
config : Config {
Bugs : ProtocolBugs {
SendClientVersion : 0x0200 ,
} ,
} ,
shouldFail : true ,
expectedError : ":UNSUPPORTED_PROTOCOL:" ,
} ,
{
testType : serverTest ,
name : "HttpGET" ,
sendPrefix : "GET / HTTP/1.0\n" ,
shouldFail : true ,
expectedError : ":HTTP_REQUEST:" ,
} ,
{
testType : serverTest ,
name : "HttpPOST" ,
sendPrefix : "POST / HTTP/1.0\n" ,
shouldFail : true ,
expectedError : ":HTTP_REQUEST:" ,
} ,
{
testType : serverTest ,
name : "HttpHEAD" ,
sendPrefix : "HEAD / HTTP/1.0\n" ,
shouldFail : true ,
expectedError : ":HTTP_REQUEST:" ,
} ,
{
testType : serverTest ,
name : "HttpPUT" ,
sendPrefix : "PUT / HTTP/1.0\n" ,
shouldFail : true ,
expectedError : ":HTTP_REQUEST:" ,
} ,
{
testType : serverTest ,
name : "HttpCONNECT" ,
sendPrefix : "CONNECT www.google.com:443 HTTP/1.0\n" ,
shouldFail : true ,
expectedError : ":HTTPS_PROXY_REQUEST:" ,
} ,
2014-12-11 23:48:59 +00:00
{
testType : serverTest ,
name : "Garbage" ,
sendPrefix : "blah" ,
shouldFail : true ,
expectedError : ":UNKNOWN_PROTOCOL:" ,
} ,
2014-08-31 07:23:49 +01:00
{
name : "SkipCipherVersionCheck" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_RSA_WITH_AES_128_GCM_SHA256 } ,
MaxVersion : VersionTLS11 ,
Bugs : ProtocolBugs {
SkipCipherVersionCheck : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":WRONG_CIPHER_RETURNED:" ,
} ,
2014-11-08 16:41:14 +00:00
{
name : "RSAServerKeyExchange" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_RSA_WITH_AES_128_CBC_SHA } ,
Bugs : ProtocolBugs {
RSAServerKeyExchange : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":UNEXPECTED_MESSAGE:" ,
} ,
2014-12-01 06:27:42 +00:00
{
name : "DisableEverything" ,
flags : [ ] string { "-no-tls12" , "-no-tls11" , "-no-tls1" , "-no-ssl3" } ,
shouldFail : true ,
expectedError : ":WRONG_SSL_VERSION:" ,
} ,
{
protocol : dtls ,
name : "DisableEverything-DTLS" ,
flags : [ ] string { "-no-tls12" , "-no-tls1" } ,
shouldFail : true ,
expectedError : ":WRONG_SSL_VERSION:" ,
} ,
2015-01-06 17:03:19 +00:00
{
name : "NoSharedCipher" ,
config : Config {
CipherSuites : [ ] uint16 { } ,
} ,
shouldFail : true ,
expectedError : ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:" ,
} ,
2015-01-11 21:29:36 +00:00
{
protocol : dtls ,
testType : serverTest ,
name : "MTU" ,
config : Config {
Bugs : ProtocolBugs {
MaxPacketLength : 256 ,
} ,
} ,
flags : [ ] string { "-mtu" , "256" } ,
} ,
{
protocol : dtls ,
testType : serverTest ,
name : "MTUExceeded" ,
config : Config {
Bugs : ProtocolBugs {
MaxPacketLength : 255 ,
} ,
} ,
flags : [ ] string { "-mtu" , "256" } ,
shouldFail : true ,
expectedLocalError : "dtls: exceeded maximum packet length" ,
} ,
2014-12-27 06:50:38 +00:00
{
name : "CertMismatchRSA" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 } ,
Certificates : [ ] Certificate { getECDSACertificate ( ) } ,
Bugs : ProtocolBugs {
SendCipherSuite : TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ,
} ,
} ,
shouldFail : true ,
expectedError : ":WRONG_CERTIFICATE_TYPE:" ,
} ,
{
name : "CertMismatchECDSA" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
Certificates : [ ] Certificate { getRSACertificate ( ) } ,
Bugs : ProtocolBugs {
SendCipherSuite : TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 ,
} ,
} ,
shouldFail : true ,
expectedError : ":WRONG_CERTIFICATE_TYPE:" ,
} ,
2015-01-22 21:35:40 +00:00
{
name : "TLSFatalBadPackets" ,
damageFirstWrite : true ,
shouldFail : true ,
expectedError : ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:" ,
} ,
{
protocol : dtls ,
name : "DTLSIgnoreBadPackets" ,
damageFirstWrite : true ,
} ,
{
protocol : dtls ,
name : "DTLSIgnoreBadPackets-Async" ,
damageFirstWrite : true ,
flags : [ ] string { "-async" } ,
} ,
2015-01-26 04:52:39 +00:00
{
name : "AppDataAfterChangeCipherSpec" ,
config : Config {
Bugs : ProtocolBugs {
AppDataAfterChangeCipherSpec : [ ] byte ( "TEST MESSAGE" ) ,
} ,
} ,
shouldFail : true ,
expectedError : ":DATA_BETWEEN_CCS_AND_FINISHED:" ,
} ,
{
protocol : dtls ,
name : "AppDataAfterChangeCipherSpec-DTLS" ,
config : Config {
Bugs : ProtocolBugs {
AppDataAfterChangeCipherSpec : [ ] byte ( "TEST MESSAGE" ) ,
} ,
2015-01-31 22:16:01 +00:00
} ,
} ,
{
protocol : dtls ,
name : "ReorderHandshakeFragments-Small-DTLS" ,
config : Config {
Bugs : ProtocolBugs {
ReorderHandshakeFragments : true ,
// Small enough that every handshake message is
// fragmented.
MaxHandshakeRecordLength : 2 ,
} ,
} ,
} ,
{
protocol : dtls ,
name : "ReorderHandshakeFragments-Large-DTLS" ,
config : Config {
Bugs : ProtocolBugs {
ReorderHandshakeFragments : true ,
// Large enough that no handshake message is
// fragmented.
//
// TODO(davidben): Also test interaction of
// complete handshake messages with
// fragments. The current logic is full of bugs
// here, so the reassembly logic needs a rewrite
// before those tests will pass.
MaxHandshakeRecordLength : 2048 ,
} ,
2015-01-26 04:52:39 +00:00
} ,
} ,
2015-02-03 20:44:39 +00:00
{
name : "SendInvalidRecordType" ,
config : Config {
Bugs : ProtocolBugs {
SendInvalidRecordType : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":UNEXPECTED_RECORD:" ,
} ,
{
protocol : dtls ,
name : "SendInvalidRecordType-DTLS" ,
config : Config {
Bugs : ProtocolBugs {
SendInvalidRecordType : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":UNEXPECTED_RECORD:" ,
} ,
2014-06-20 20:00:00 +01:00
}
2014-09-24 20:21:44 +01:00
func doExchange ( test * testCase , config * Config , conn net . Conn , messageLen int , isResume bool ) error {
2014-11-23 08:01:00 +00:00
var connDebug * recordingConn
2015-01-22 21:35:40 +00:00
var connDamage * damageAdaptor
2014-11-23 08:01:00 +00:00
if * flagDebug {
connDebug = & recordingConn { Conn : conn }
conn = connDebug
defer func ( ) {
connDebug . WriteTo ( os . Stdout )
} ( )
}
2014-08-11 23:43:38 +01:00
if test . protocol == dtls {
Add DTLS timeout and retransmit tests.
This extends the packet adaptor protocol to send three commands:
type command =
| Packet of []byte
| Timeout of time.Duration
| TimeoutAck
When the shim processes a Timeout in BIO_read, it sends TimeoutAck, fails the
BIO_read, returns out of the SSL stack, advances the clock, calls
DTLSv1_handle_timeout, and continues.
If the Go side sends Timeout right between sending handshake flight N and
reading flight N+1, the shim won't read the Timeout until it has sent flight
N+1 (it only processes packet commands in BIO_read), so the TimeoutAck comes
after N+1. Go then drops all packets before the TimeoutAck, thus dropping one
transmit of flight N+1 without having to actually process the packets to
determine the end of the flight. The shim then sees the updated clock, calls
DTLSv1_handle_timeout, and re-sends flight N+1 for Go to process for real.
When dropping packets, Go checks the epoch and increments sequence numbers so
that we can continue to be strict here. This requires tracking the initial
sequence number of the next epoch.
The final Finished message takes an additional special-case to test. DTLS
triggers retransmits on either a timeout or seeing a stale flight. OpenSSL only
implements the former which should be sufficient (and is necessary) EXCEPT for
the final Finished message. If the peer's final Finished message is lost, it
won't be waiting for a message from us, so it won't time out anything. That
retransmit must be triggered on stale message, so we retransmit the Finished
message in Go.
Change-Id: I3ffbdb1de525beb2ee831b304670a3387877634c
Reviewed-on: https://boringssl-review.googlesource.com/3212
Reviewed-by: Adam Langley <agl@google.com>
2015-01-27 06:09:43 +00:00
config . Bugs . PacketAdaptor = newPacketAdaptor ( conn )
conn = config . Bugs . PacketAdaptor
2014-11-07 06:48:35 +00:00
if test . replayWrites {
conn = newReplayAdaptor ( conn )
}
2014-08-11 23:43:38 +01:00
}
2015-01-22 21:35:40 +00:00
if test . damageFirstWrite {
connDamage = newDamageAdaptor ( conn )
conn = connDamage
}
2014-08-11 23:43:38 +01:00
if test . sendPrefix != "" {
if _ , err := conn . Write ( [ ] byte ( test . sendPrefix ) ) ; err != nil {
return err
}
2014-08-08 18:24:34 +01:00
}
2014-07-23 00:20:02 +01:00
var tlsConn * Conn
2014-08-07 22:44:24 +01:00
if test . testType == clientTest {
2014-08-11 23:43:38 +01:00
if test . protocol == dtls {
tlsConn = DTLSServer ( conn , config )
} else {
tlsConn = Server ( conn , config )
}
2014-07-23 00:20:02 +01:00
} else {
config . InsecureSkipVerify = true
2014-08-11 23:43:38 +01:00
if test . protocol == dtls {
tlsConn = DTLSClient ( conn , config )
} else {
tlsConn = Client ( conn , config )
}
2014-07-23 00:20:02 +01:00
}
2014-06-20 20:00:00 +01:00
if err := tlsConn . Handshake ( ) ; err != nil {
return err
}
2014-08-05 23:23:37 +01:00
2014-09-24 20:21:44 +01:00
// TODO(davidben): move all per-connection expectations into a dedicated
// expectations struct that can be specified separately for the two
// legs.
expectedVersion := test . expectedVersion
if isResume && test . expectedResumeVersion != 0 {
expectedVersion = test . expectedResumeVersion
}
if vers := tlsConn . ConnectionState ( ) . Version ; expectedVersion != 0 && vers != expectedVersion {
return fmt . Errorf ( "got version %x, expected %x" , vers , expectedVersion )
2014-08-07 22:44:24 +01:00
}
2014-08-24 06:46:07 +01:00
if test . expectChannelID {
channelID := tlsConn . ConnectionState ( ) . ChannelID
if channelID == nil {
return fmt . Errorf ( "no channel ID negotiated" )
}
if channelID . Curve != channelIDKey . Curve ||
channelIDKey . X . Cmp ( channelIDKey . X ) != 0 ||
channelIDKey . Y . Cmp ( channelIDKey . Y ) != 0 {
return fmt . Errorf ( "incorrect channel ID" )
}
}
2014-09-06 17:58:58 +01:00
if expected := test . expectedNextProto ; expected != "" {
if actual := tlsConn . ConnectionState ( ) . NegotiatedProtocol ; actual != expected {
return fmt . Errorf ( "next proto mismatch: got %s, wanted %s" , actual , expected )
}
}
2014-09-06 18:21:53 +01:00
if test . expectedNextProtoType != 0 {
if ( test . expectedNextProtoType == alpn ) != tlsConn . ConnectionState ( ) . NegotiatedProtocolFromALPN {
return fmt . Errorf ( "next proto type mismatch" )
}
}
2014-11-16 00:06:08 +00:00
if p := tlsConn . ConnectionState ( ) . SRTPProtectionProfile ; p != test . expectedSRTPProtectionProfile {
return fmt . Errorf ( "SRTP profile mismatch: got %d, wanted %d" , p , test . expectedSRTPProtectionProfile )
}
2014-08-24 08:47:07 +01:00
if test . shimWritesFirst {
var buf [ 5 ] byte
_ , err := io . ReadFull ( tlsConn , buf [ : ] )
if err != nil {
return err
}
if string ( buf [ : ] ) != "hello" {
return fmt . Errorf ( "bad initial message" )
}
}
2014-10-29 02:06:14 +00:00
if test . renegotiate {
if test . renegotiateCiphers != nil {
config . CipherSuites = test . renegotiateCiphers
}
if err := tlsConn . Renegotiate ( ) ; err != nil {
return err
}
} else if test . renegotiateCiphers != nil {
panic ( "renegotiateCiphers without renegotiate" )
}
2015-01-22 21:35:40 +00:00
if test . damageFirstWrite {
connDamage . setDamage ( true )
tlsConn . Write ( [ ] byte ( "DAMAGED WRITE" ) )
connDamage . setDamage ( false )
}
2014-08-05 23:23:37 +01:00
if messageLen < 0 {
2014-08-11 23:43:38 +01:00
if test . protocol == dtls {
return fmt . Errorf ( "messageLen < 0 not supported for DTLS tests" )
}
2014-08-05 23:23:37 +01:00
// Read until EOF.
_ , err := io . Copy ( ioutil . Discard , tlsConn )
return err
}
2015-01-26 04:52:39 +00:00
var testMessage [ ] byte
if config . Bugs . AppDataAfterChangeCipherSpec != nil {
// We've already sent a message. Expect the shim to echo it
// back.
testMessage = config . Bugs . AppDataAfterChangeCipherSpec
} else {
if messageLen == 0 {
messageLen = 32
}
testMessage = make ( [ ] byte , messageLen )
for i := range testMessage {
testMessage [ i ] = 0x42
}
tlsConn . Write ( testMessage )
2014-06-20 20:00:00 +01:00
}
2014-06-20 20:00:00 +01:00
buf := make ( [ ] byte , len ( testMessage ) )
2014-08-11 23:43:38 +01:00
if test . protocol == dtls {
bufTmp := make ( [ ] byte , len ( buf ) + 1 )
n , err := tlsConn . Read ( bufTmp )
if err != nil {
return err
}
if n != len ( buf ) {
return fmt . Errorf ( "bad reply; length mismatch (%d vs %d)" , n , len ( buf ) )
}
copy ( buf , bufTmp )
} else {
_ , err := io . ReadFull ( tlsConn , buf )
if err != nil {
return err
}
2014-06-20 20:00:00 +01:00
}
for i , v := range buf {
if v != testMessage [ i ] ^ 0xff {
return fmt . Errorf ( "bad reply contents at byte %d" , i )
}
}
return nil
}
2014-07-02 00:40:31 +01:00
func valgrindOf ( dbAttach bool , path string , args ... string ) * exec . Cmd {
valgrindArgs := [ ] string { "--error-exitcode=99" , "--track-origins=yes" , "--leak-check=full" }
2014-06-20 20:00:00 +01:00
if dbAttach {
2014-07-02 00:40:31 +01:00
valgrindArgs = append ( valgrindArgs , "--db-attach=yes" , "--db-command=xterm -e gdb -nw %f %p" )
2014-06-20 20:00:00 +01:00
}
2014-07-02 00:40:31 +01:00
valgrindArgs = append ( valgrindArgs , path )
valgrindArgs = append ( valgrindArgs , args ... )
2014-06-20 20:00:00 +01:00
2014-07-02 00:40:31 +01:00
return exec . Command ( "valgrind" , valgrindArgs ... )
2014-06-20 20:00:00 +01:00
}
2014-07-02 00:40:31 +01:00
func gdbOf ( path string , args ... string ) * exec . Cmd {
xtermArgs := [ ] string { "-e" , "gdb" , "--args" }
xtermArgs = append ( xtermArgs , path )
xtermArgs = append ( xtermArgs , args ... )
2014-06-20 20:00:00 +01:00
2014-07-02 00:40:31 +01:00
return exec . Command ( "xterm" , xtermArgs ... )
2014-06-20 20:00:00 +01:00
}
2014-07-23 00:20:02 +01:00
func openSocketPair ( ) ( shimEnd * os . File , conn net . Conn ) {
2014-06-20 20:00:00 +01:00
socks , err := syscall . Socketpair ( syscall . AF_UNIX , syscall . SOCK_STREAM , 0 )
if err != nil {
panic ( err )
}
syscall . CloseOnExec ( socks [ 0 ] )
syscall . CloseOnExec ( socks [ 1 ] )
2014-07-23 00:20:02 +01:00
shimEnd = os . NewFile ( uintptr ( socks [ 0 ] ) , "shim end" )
2014-06-20 20:00:00 +01:00
connFile := os . NewFile ( uintptr ( socks [ 1 ] ) , "our end" )
2014-07-23 00:20:02 +01:00
conn , err = net . FileConn ( connFile )
if err != nil {
panic ( err )
}
2014-06-20 20:00:00 +01:00
connFile . Close ( )
if err != nil {
panic ( err )
}
2014-07-23 00:20:02 +01:00
return shimEnd , conn
}
2014-11-18 01:26:55 +00:00
type moreMallocsError struct { }
func ( moreMallocsError ) Error ( ) string {
return "child process did not exhaust all allocation calls"
}
var errMoreMallocs = moreMallocsError { }
func runTest ( test * testCase , buildDir string , mallocNumToFail int64 ) error {
2014-10-17 03:04:35 +01:00
if ! test . shouldFail && ( len ( test . expectedError ) > 0 || len ( test . expectedLocalError ) > 0 ) {
panic ( "Error expected without shouldFail in " + test . name )
}
2014-07-23 00:20:02 +01:00
shimEnd , conn := openSocketPair ( )
shimEndResume , connResume := openSocketPair ( )
2014-06-20 20:00:00 +01:00
2014-08-02 20:28:23 +01:00
shim_path := path . Join ( buildDir , "ssl/test/bssl_shim" )
2014-08-12 00:51:50 +01:00
var flags [ ] string
2014-07-23 00:20:02 +01:00
if test . testType == serverTest {
2014-08-12 00:51:50 +01:00
flags = append ( flags , "-server" )
2014-07-02 00:53:04 +01:00
flags = append ( flags , "-key-file" )
if test . keyFile == "" {
flags = append ( flags , rsaKeyFile )
} else {
flags = append ( flags , test . keyFile )
}
flags = append ( flags , "-cert-file" )
if test . certFile == "" {
flags = append ( flags , rsaCertificateFile )
} else {
flags = append ( flags , test . certFile )
}
}
2014-08-12 00:51:50 +01:00
2014-08-11 23:43:38 +01:00
if test . protocol == dtls {
flags = append ( flags , "-dtls" )
}
2014-08-12 00:51:50 +01:00
if test . resumeSession {
flags = append ( flags , "-resume" )
}
2014-08-24 08:47:07 +01:00
if test . shimWritesFirst {
flags = append ( flags , "-shim-writes-first" )
}
2014-07-02 00:53:04 +01:00
flags = append ( flags , test . flags ... )
var shim * exec . Cmd
2014-06-20 20:00:00 +01:00
if * useValgrind {
2014-07-02 00:53:04 +01:00
shim = valgrindOf ( false , shim_path , flags ... )
2014-10-11 00:23:43 +01:00
} else if * useGDB {
shim = gdbOf ( shim_path , flags ... )
2014-06-20 20:00:00 +01:00
} else {
2014-07-02 00:53:04 +01:00
shim = exec . Command ( shim_path , flags ... )
2014-06-20 20:00:00 +01:00
}
2014-07-23 00:20:02 +01:00
shim . ExtraFiles = [ ] * os . File { shimEnd , shimEndResume }
2014-07-02 00:53:04 +01:00
shim . Stdin = os . Stdin
2014-06-20 20:00:00 +01:00
var stdoutBuf , stderrBuf bytes . Buffer
2014-07-02 00:53:04 +01:00
shim . Stdout = & stdoutBuf
shim . Stderr = & stderrBuf
2014-11-18 01:26:55 +00:00
if mallocNumToFail >= 0 {
shim . Env = [ ] string { "MALLOC_NUMBER_TO_FAIL=" + strconv . FormatInt ( mallocNumToFail , 10 ) }
if * mallocTestDebug {
shim . Env = append ( shim . Env , "MALLOC_ABORT_ON_FAIL=1" )
}
shim . Env = append ( shim . Env , "_MALLOC_CHECK=1" )
}
2014-06-20 20:00:00 +01:00
2014-07-02 00:53:04 +01:00
if err := shim . Start ( ) ; err != nil {
2014-06-20 20:00:00 +01:00
panic ( err )
}
2014-07-02 00:53:04 +01:00
shimEnd . Close ( )
2014-07-23 00:20:02 +01:00
shimEndResume . Close ( )
2014-06-20 20:00:00 +01:00
config := test . config
2014-07-23 00:20:02 +01:00
config . ClientSessionCache = NewLRUClientSessionCache ( 1 )
2014-11-17 08:19:02 +00:00
config . ServerSessionCache = NewLRUServerSessionCache ( 1 )
2014-07-02 00:53:04 +01:00
if test . testType == clientTest {
if len ( config . Certificates ) == 0 {
config . Certificates = [ ] Certificate { getRSACertificate ( ) }
}
}
2014-06-20 20:00:00 +01:00
2014-09-24 20:21:44 +01:00
err := doExchange ( test , & config , conn , test . messageLen ,
false /* not a resumption */ )
2014-06-20 20:00:00 +01:00
conn . Close ( )
2014-11-23 08:01:00 +00:00
2014-07-23 00:20:02 +01:00
if err == nil && test . resumeSession {
2014-09-24 20:21:44 +01:00
var resumeConfig Config
if test . resumeConfig != nil {
resumeConfig = * test . resumeConfig
if len ( resumeConfig . Certificates ) == 0 {
resumeConfig . Certificates = [ ] Certificate { getRSACertificate ( ) }
}
2014-11-17 08:19:02 +00:00
if ! test . newSessionsOnResume {
resumeConfig . SessionTicketKey = config . SessionTicketKey
resumeConfig . ClientSessionCache = config . ClientSessionCache
resumeConfig . ServerSessionCache = config . ServerSessionCache
}
2014-09-24 20:21:44 +01:00
} else {
resumeConfig = config
}
err = doExchange ( test , & resumeConfig , connResume , test . messageLen ,
true /* resumption */ )
2014-07-23 00:20:02 +01:00
}
2014-09-06 17:49:07 +01:00
connResume . Close ( )
2014-07-23 00:20:02 +01:00
2014-07-02 00:53:04 +01:00
childErr := shim . Wait ( )
2014-11-18 01:26:55 +00:00
if exitError , ok := childErr . ( * exec . ExitError ) ; ok {
if exitError . Sys ( ) . ( syscall . WaitStatus ) . ExitStatus ( ) == 88 {
return errMoreMallocs
}
}
2014-06-20 20:00:00 +01:00
stdout := string ( stdoutBuf . Bytes ( ) )
stderr := string ( stderrBuf . Bytes ( ) )
failed := err != nil || childErr != nil
correctFailure := len ( test . expectedError ) == 0 || strings . Contains ( stdout , test . expectedError )
2014-06-23 20:03:11 +01:00
localError := "none"
if err != nil {
localError = err . Error ( )
}
if len ( test . expectedLocalError ) != 0 {
correctFailure = correctFailure && strings . Contains ( localError , test . expectedLocalError )
}
2014-06-20 20:00:00 +01:00
if failed != test . shouldFail || failed && ! correctFailure {
childError := "none"
if childErr != nil {
childError = childErr . Error ( )
}
var msg string
switch {
case failed && ! test . shouldFail :
msg = "unexpected failure"
case ! failed && test . shouldFail :
msg = "unexpected success"
case failed && ! correctFailure :
2014-06-23 20:03:11 +01:00
msg = "bad error (wanted '" + test . expectedError + "' / '" + test . expectedLocalError + "')"
2014-06-20 20:00:00 +01:00
default :
panic ( "internal error" )
}
return fmt . Errorf ( "%s: local error '%s', child error '%s', stdout:\n%s\nstderr:\n%s" , msg , localError , childError , string ( stdoutBuf . Bytes ( ) ) , stderr )
}
if ! * useValgrind && len ( stderr ) > 0 {
println ( stderr )
}
return nil
}
var tlsVersions = [ ] struct {
name string
version uint16
2014-08-07 22:44:24 +01:00
flag string
2014-11-23 07:47:52 +00:00
hasDTLS bool
2014-06-20 20:00:00 +01:00
} {
2014-11-23 07:47:52 +00:00
{ "SSL3" , VersionSSL30 , "-no-ssl3" , false } ,
{ "TLS1" , VersionTLS10 , "-no-tls1" , true } ,
{ "TLS11" , VersionTLS11 , "-no-tls11" , false } ,
{ "TLS12" , VersionTLS12 , "-no-tls12" , true } ,
2014-06-20 20:00:00 +01:00
}
var testCipherSuites = [ ] struct {
name string
id uint16
} {
{ "3DES-SHA" , TLS_RSA_WITH_3DES_EDE_CBC_SHA } ,
2014-08-02 22:35:45 +01:00
{ "AES128-GCM" , TLS_RSA_WITH_AES_128_GCM_SHA256 } ,
2014-06-20 20:00:00 +01:00
{ "AES128-SHA" , TLS_RSA_WITH_AES_128_CBC_SHA } ,
2014-08-31 07:06:47 +01:00
{ "AES128-SHA256" , TLS_RSA_WITH_AES_128_CBC_SHA256 } ,
2014-08-02 22:35:45 +01:00
{ "AES256-GCM" , TLS_RSA_WITH_AES_256_GCM_SHA384 } ,
2014-06-20 20:00:00 +01:00
{ "AES256-SHA" , TLS_RSA_WITH_AES_256_CBC_SHA } ,
2014-08-31 07:06:47 +01:00
{ "AES256-SHA256" , TLS_RSA_WITH_AES_256_CBC_SHA256 } ,
2014-08-02 22:35:45 +01:00
{ "DHE-RSA-AES128-GCM" , TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 } ,
{ "DHE-RSA-AES128-SHA" , TLS_DHE_RSA_WITH_AES_128_CBC_SHA } ,
2014-08-31 07:06:47 +01:00
{ "DHE-RSA-AES128-SHA256" , TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 } ,
2014-08-02 22:35:45 +01:00
{ "DHE-RSA-AES256-GCM" , TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 } ,
{ "DHE-RSA-AES256-SHA" , TLS_DHE_RSA_WITH_AES_256_CBC_SHA } ,
2014-08-31 07:06:47 +01:00
{ "DHE-RSA-AES256-SHA256" , TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 } ,
2014-06-20 20:00:00 +01:00
{ "ECDHE-ECDSA-AES128-GCM" , TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 } ,
{ "ECDHE-ECDSA-AES128-SHA" , TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA } ,
2014-08-31 07:06:47 +01:00
{ "ECDHE-ECDSA-AES128-SHA256" , TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 } ,
{ "ECDHE-ECDSA-AES256-GCM" , TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 } ,
2014-06-20 20:00:00 +01:00
{ "ECDHE-ECDSA-AES256-SHA" , TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA } ,
2014-08-31 07:06:47 +01:00
{ "ECDHE-ECDSA-AES256-SHA384" , TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 } ,
2014-06-20 20:00:00 +01:00
{ "ECDHE-ECDSA-RC4-SHA" , TLS_ECDHE_ECDSA_WITH_RC4_128_SHA } ,
2014-10-27 06:23:15 +00:00
{ "ECDHE-PSK-WITH-AES-128-GCM-SHA256" , TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 } ,
2014-06-20 20:00:00 +01:00
{ "ECDHE-RSA-AES128-GCM" , TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
{ "ECDHE-RSA-AES128-SHA" , TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA } ,
2014-08-31 07:06:47 +01:00
{ "ECDHE-RSA-AES128-SHA256" , TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 } ,
2014-08-02 22:35:45 +01:00
{ "ECDHE-RSA-AES256-GCM" , TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 } ,
2014-06-20 20:00:00 +01:00
{ "ECDHE-RSA-AES256-SHA" , TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA } ,
2014-08-31 07:06:47 +01:00
{ "ECDHE-RSA-AES256-SHA384" , TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 } ,
2014-06-20 20:00:00 +01:00
{ "ECDHE-RSA-RC4-SHA" , TLS_ECDHE_RSA_WITH_RC4_128_SHA } ,
2014-10-27 05:06:24 +00:00
{ "PSK-AES128-CBC-SHA" , TLS_PSK_WITH_AES_128_CBC_SHA } ,
{ "PSK-AES256-CBC-SHA" , TLS_PSK_WITH_AES_256_CBC_SHA } ,
{ "PSK-RC4-SHA" , TLS_PSK_WITH_RC4_128_SHA } ,
2014-06-20 20:00:00 +01:00
{ "RC4-MD5" , TLS_RSA_WITH_RC4_128_MD5 } ,
2014-08-02 22:35:45 +01:00
{ "RC4-SHA" , TLS_RSA_WITH_RC4_128_SHA } ,
2014-06-20 20:00:00 +01:00
}
2014-11-23 07:47:52 +00:00
func hasComponent ( suiteName , component string ) bool {
return strings . Contains ( "-" + suiteName + "-" , "-" + component + "-" )
}
2014-08-31 07:06:47 +01:00
func isTLS12Only ( suiteName string ) bool {
2014-11-23 07:47:52 +00:00
return hasComponent ( suiteName , "GCM" ) ||
hasComponent ( suiteName , "SHA256" ) ||
hasComponent ( suiteName , "SHA384" )
}
func isDTLSCipher ( suiteName string ) bool {
2014-12-23 16:16:01 +00:00
return ! hasComponent ( suiteName , "RC4" )
2014-08-31 07:06:47 +01:00
}
2014-06-20 20:00:00 +01:00
func addCipherSuiteTests ( ) {
for _ , suite := range testCipherSuites {
2014-10-27 05:06:24 +00:00
const psk = "12345"
const pskIdentity = "luggage combo"
2014-06-20 20:00:00 +01:00
var cert Certificate
2014-07-02 00:53:04 +01:00
var certFile string
var keyFile string
2014-11-23 07:47:52 +00:00
if hasComponent ( suite . name , "ECDSA" ) {
2014-06-20 20:00:00 +01:00
cert = getECDSACertificate ( )
2014-07-02 00:53:04 +01:00
certFile = ecdsaCertificateFile
keyFile = ecdsaKeyFile
2014-06-20 20:00:00 +01:00
} else {
cert = getRSACertificate ( )
2014-07-02 00:53:04 +01:00
certFile = rsaCertificateFile
keyFile = rsaKeyFile
2014-06-20 20:00:00 +01:00
}
2014-10-27 05:06:24 +00:00
var flags [ ] string
2014-11-23 07:47:52 +00:00
if hasComponent ( suite . name , "PSK" ) {
2014-10-27 05:06:24 +00:00
flags = append ( flags ,
"-psk" , psk ,
"-psk-identity" , pskIdentity )
}
2014-06-20 20:00:00 +01:00
for _ , ver := range tlsVersions {
2014-08-31 07:06:47 +01:00
if ver . version < VersionTLS12 && isTLS12Only ( suite . name ) {
2014-06-20 20:00:00 +01:00
continue
}
2014-07-02 00:53:04 +01:00
testCases = append ( testCases , testCase {
testType : clientTest ,
name : ver . name + "-" + suite . name + "-client" ,
2014-06-20 20:00:00 +01:00
config : Config {
2014-10-27 05:06:24 +00:00
MinVersion : ver . version ,
MaxVersion : ver . version ,
CipherSuites : [ ] uint16 { suite . id } ,
Certificates : [ ] Certificate { cert } ,
PreSharedKey : [ ] byte ( psk ) ,
PreSharedKeyIdentity : pskIdentity ,
2014-06-20 20:00:00 +01:00
} ,
2014-10-27 05:06:24 +00:00
flags : flags ,
2014-11-17 08:19:02 +00:00
resumeSession : true ,
2014-06-20 20:00:00 +01:00
} )
2014-07-02 00:53:04 +01:00
2014-08-14 21:25:34 +01:00
testCases = append ( testCases , testCase {
testType : serverTest ,
name : ver . name + "-" + suite . name + "-server" ,
config : Config {
2014-10-27 05:06:24 +00:00
MinVersion : ver . version ,
MaxVersion : ver . version ,
CipherSuites : [ ] uint16 { suite . id } ,
Certificates : [ ] Certificate { cert } ,
PreSharedKey : [ ] byte ( psk ) ,
PreSharedKeyIdentity : pskIdentity ,
2014-08-14 21:25:34 +01:00
} ,
certFile : certFile ,
keyFile : keyFile ,
2014-10-27 05:06:24 +00:00
flags : flags ,
2014-11-17 08:19:02 +00:00
resumeSession : true ,
2014-08-14 21:25:34 +01:00
} )
2014-08-11 23:43:38 +01:00
2014-11-23 07:47:52 +00:00
if ver . hasDTLS && isDTLSCipher ( suite . name ) {
2014-08-11 23:43:38 +01:00
testCases = append ( testCases , testCase {
testType : clientTest ,
protocol : dtls ,
name : "D" + ver . name + "-" + suite . name + "-client" ,
config : Config {
2014-10-27 05:06:24 +00:00
MinVersion : ver . version ,
MaxVersion : ver . version ,
CipherSuites : [ ] uint16 { suite . id } ,
Certificates : [ ] Certificate { cert } ,
PreSharedKey : [ ] byte ( psk ) ,
PreSharedKeyIdentity : pskIdentity ,
2014-08-11 23:43:38 +01:00
} ,
2014-10-27 05:06:24 +00:00
flags : flags ,
2014-11-17 08:19:02 +00:00
resumeSession : true ,
2014-08-11 23:43:38 +01:00
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
protocol : dtls ,
name : "D" + ver . name + "-" + suite . name + "-server" ,
config : Config {
2014-10-27 05:06:24 +00:00
MinVersion : ver . version ,
MaxVersion : ver . version ,
CipherSuites : [ ] uint16 { suite . id } ,
Certificates : [ ] Certificate { cert } ,
PreSharedKey : [ ] byte ( psk ) ,
PreSharedKeyIdentity : pskIdentity ,
2014-08-11 23:43:38 +01:00
} ,
certFile : certFile ,
keyFile : keyFile ,
2014-10-27 05:06:24 +00:00
flags : flags ,
2014-11-17 08:19:02 +00:00
resumeSession : true ,
2014-08-11 23:43:38 +01:00
} )
}
2014-06-20 20:00:00 +01:00
}
}
}
func addBadECDSASignatureTests ( ) {
for badR := BadValue ( 1 ) ; badR < NumBadValues ; badR ++ {
for badS := BadValue ( 1 ) ; badS < NumBadValues ; badS ++ {
2014-07-02 00:53:04 +01:00
testCases = append ( testCases , testCase {
2014-06-20 20:00:00 +01:00
name : fmt . Sprintf ( "BadECDSA-%d-%d" , badR , badS ) ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 } ,
Certificates : [ ] Certificate { getECDSACertificate ( ) } ,
Bugs : ProtocolBugs {
BadECDSAR : badR ,
BadECDSAS : badS ,
} ,
} ,
shouldFail : true ,
expectedError : "SIGNATURE" ,
} )
}
}
}
2014-06-20 20:00:00 +01:00
func addCBCPaddingTests ( ) {
2014-07-02 00:53:04 +01:00
testCases = append ( testCases , testCase {
2014-06-20 20:00:00 +01:00
name : "MaxCBCPadding" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA } ,
Bugs : ProtocolBugs {
MaxPadding : true ,
} ,
} ,
messageLen : 12 , // 20 bytes of SHA-1 + 12 == 0 % block size
} )
2014-07-02 00:53:04 +01:00
testCases = append ( testCases , testCase {
2014-06-20 20:00:00 +01:00
name : "BadCBCPadding" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA } ,
Bugs : ProtocolBugs {
PaddingFirstByteBad : true ,
} ,
} ,
shouldFail : true ,
expectedError : "DECRYPTION_FAILED_OR_BAD_RECORD_MAC" ,
} )
// OpenSSL previously had an issue where the first byte of padding in
// 255 bytes of padding wasn't checked.
2014-07-02 00:53:04 +01:00
testCases = append ( testCases , testCase {
2014-06-20 20:00:00 +01:00
name : "BadCBCPadding255" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA } ,
Bugs : ProtocolBugs {
MaxPadding : true ,
PaddingFirstByteBadIf255 : true ,
} ,
} ,
messageLen : 12 , // 20 bytes of SHA-1 + 12 == 0 % block size
shouldFail : true ,
expectedError : "DECRYPTION_FAILED_OR_BAD_RECORD_MAC" ,
} )
}
2014-08-05 23:23:37 +01:00
func addCBCSplittingTests ( ) {
testCases = append ( testCases , testCase {
name : "CBCRecordSplitting" ,
config : Config {
MaxVersion : VersionTLS10 ,
MinVersion : VersionTLS10 ,
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA } ,
} ,
messageLen : - 1 , // read until EOF
flags : [ ] string {
"-async" ,
"-write-different-record-sizes" ,
"-cbc-record-splitting" ,
} ,
2014-08-07 03:11:10 +01:00
} )
testCases = append ( testCases , testCase {
2014-08-05 23:23:37 +01:00
name : "CBCRecordSplittingPartialWrite" ,
config : Config {
MaxVersion : VersionTLS10 ,
MinVersion : VersionTLS10 ,
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA } ,
} ,
messageLen : - 1 , // read until EOF
flags : [ ] string {
"-async" ,
"-write-different-record-sizes" ,
"-cbc-record-splitting" ,
"-partial-write" ,
} ,
} )
}
2014-07-08 22:59:18 +01:00
func addClientAuthTests ( ) {
2014-07-16 17:58:59 +01:00
// Add a dummy cert pool to stress certificate authority parsing.
// TODO(davidben): Add tests that those values parse out correctly.
certPool := x509 . NewCertPool ( )
cert , err := x509 . ParseCertificate ( rsaCertificate . Certificate [ 0 ] )
if err != nil {
panic ( err )
}
certPool . AddCert ( cert )
2014-07-08 22:59:18 +01:00
for _ , ver := range tlsVersions {
testCases = append ( testCases , testCase {
testType : clientTest ,
2014-07-12 20:47:52 +01:00
name : ver . name + "-Client-ClientAuth-RSA" ,
2014-07-08 22:59:18 +01:00
config : Config {
2014-08-28 04:13:20 +01:00
MinVersion : ver . version ,
MaxVersion : ver . version ,
ClientAuth : RequireAnyClientCert ,
ClientCAs : certPool ,
2014-07-08 22:59:18 +01:00
} ,
flags : [ ] string {
"-cert-file" , rsaCertificateFile ,
"-key-file" , rsaKeyFile ,
} ,
} )
2014-07-12 20:47:52 +01:00
testCases = append ( testCases , testCase {
testType : serverTest ,
name : ver . name + "-Server-ClientAuth-RSA" ,
config : Config {
2014-08-28 04:13:20 +01:00
MinVersion : ver . version ,
MaxVersion : ver . version ,
2014-07-12 20:47:52 +01:00
Certificates : [ ] Certificate { rsaCertificate } ,
} ,
flags : [ ] string { "-require-any-client-certificate" } ,
} )
2014-08-28 04:13:20 +01:00
if ver . version != VersionSSL30 {
testCases = append ( testCases , testCase {
testType : serverTest ,
name : ver . name + "-Server-ClientAuth-ECDSA" ,
config : Config {
MinVersion : ver . version ,
MaxVersion : ver . version ,
Certificates : [ ] Certificate { ecdsaCertificate } ,
} ,
flags : [ ] string { "-require-any-client-certificate" } ,
} )
testCases = append ( testCases , testCase {
testType : clientTest ,
name : ver . name + "-Client-ClientAuth-ECDSA" ,
config : Config {
MinVersion : ver . version ,
MaxVersion : ver . version ,
ClientAuth : RequireAnyClientCert ,
ClientCAs : certPool ,
} ,
flags : [ ] string {
"-cert-file" , ecdsaCertificateFile ,
"-key-file" , ecdsaKeyFile ,
} ,
} )
}
2014-07-08 22:59:18 +01:00
}
}
2014-10-11 00:23:43 +01:00
func addExtendedMasterSecretTests ( ) {
const expectEMSFlag = "-expect-extended-master-secret"
for _ , with := range [ ] bool { false , true } {
prefix := "No"
var flags [ ] string
if with {
prefix = ""
flags = [ ] string { expectEMSFlag }
}
for _ , isClient := range [ ] bool { false , true } {
suffix := "-Server"
testType := serverTest
if isClient {
suffix = "-Client"
testType = clientTest
}
for _ , ver := range tlsVersions {
test := testCase {
testType : testType ,
name : prefix + "ExtendedMasterSecret-" + ver . name + suffix ,
config : Config {
MinVersion : ver . version ,
MaxVersion : ver . version ,
Bugs : ProtocolBugs {
NoExtendedMasterSecret : ! with ,
RequireExtendedMasterSecret : with ,
} ,
} ,
2014-10-27 05:06:24 +00:00
flags : flags ,
shouldFail : ver . version == VersionSSL30 && with ,
2014-10-11 00:23:43 +01:00
}
if test . shouldFail {
test . expectedLocalError = "extended master secret required but not supported by peer"
}
testCases = append ( testCases , test )
}
}
}
// When a session is resumed, it should still be aware that its master
// secret was generated via EMS and thus it's safe to use tls-unique.
testCases = append ( testCases , testCase {
name : "ExtendedMasterSecret-Resume" ,
config : Config {
Bugs : ProtocolBugs {
RequireExtendedMasterSecret : true ,
} ,
} ,
flags : [ ] string { expectEMSFlag } ,
resumeSession : true ,
} )
}
2014-08-05 07:28:57 +01:00
// Adds tests that try to cover the range of the handshake state machine, under
// various conditions. Some of these are redundant with other tests, but they
// only cover the synchronous case.
2014-08-11 23:43:38 +01:00
func addStateMachineCoverageTests ( async , splitHandshake bool , protocol protocol ) {
2014-08-05 07:28:57 +01:00
var suffix string
var flags [ ] string
var maxHandshakeRecordLength int
2014-08-11 23:43:38 +01:00
if protocol == dtls {
suffix = "-DTLS"
}
2014-08-05 07:28:57 +01:00
if async {
2014-08-11 23:43:38 +01:00
suffix += "-Async"
2014-08-05 07:28:57 +01:00
flags = append ( flags , "-async" )
} else {
2014-08-11 23:43:38 +01:00
suffix += "-Sync"
2014-08-05 07:28:57 +01:00
}
if splitHandshake {
suffix += "-SplitHandshakeRecords"
2014-08-07 23:02:39 +01:00
maxHandshakeRecordLength = 1
2014-08-05 07:28:57 +01:00
}
2014-11-17 08:19:02 +00:00
// Basic handshake, with resumption. Client and server,
// session ID and session ticket.
2014-08-05 07:28:57 +01:00
testCases = append ( testCases , testCase {
2014-08-11 23:43:38 +01:00
protocol : protocol ,
name : "Basic-Client" + suffix ,
2014-08-05 07:28:57 +01:00
config : Config {
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
2014-08-08 00:13:38 +01:00
flags : flags ,
resumeSession : true ,
} )
testCases = append ( testCases , testCase {
2014-08-11 23:43:38 +01:00
protocol : protocol ,
name : "Basic-Client-RenewTicket" + suffix ,
2014-08-08 00:13:38 +01:00
config : Config {
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
RenewTicketOnResume : true ,
} ,
} ,
flags : flags ,
resumeSession : true ,
2014-08-05 07:28:57 +01:00
} )
2014-11-17 08:19:02 +00:00
testCases = append ( testCases , testCase {
protocol : protocol ,
name : "Basic-Client-NoTicket" + suffix ,
config : Config {
SessionTicketsDisabled : true ,
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
flags : flags ,
resumeSession : true ,
} )
2014-08-05 07:28:57 +01:00
testCases = append ( testCases , testCase {
2014-08-11 23:43:38 +01:00
protocol : protocol ,
2014-08-05 07:28:57 +01:00
testType : serverTest ,
name : "Basic-Server" + suffix ,
config : Config {
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
2014-08-08 00:13:38 +01:00
flags : flags ,
resumeSession : true ,
2014-08-05 07:28:57 +01:00
} )
2014-11-17 08:19:02 +00:00
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : serverTest ,
name : "Basic-Server-NoTickets" + suffix ,
config : Config {
SessionTicketsDisabled : true ,
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
flags : flags ,
resumeSession : true ,
} )
2014-08-05 07:28:57 +01:00
2014-08-11 23:43:38 +01:00
// TLS client auth.
2014-08-05 07:28:57 +01:00
testCases = append ( testCases , testCase {
2014-08-11 23:43:38 +01:00
protocol : protocol ,
testType : clientTest ,
name : "ClientAuth-Client" + suffix ,
2014-08-05 07:28:57 +01:00
config : Config {
2014-08-28 04:13:20 +01:00
ClientAuth : RequireAnyClientCert ,
2014-08-05 07:28:57 +01:00
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
2014-08-11 23:43:38 +01:00
flags : append ( flags ,
"-cert-file" , rsaCertificateFile ,
"-key-file" , rsaKeyFile ) ,
2014-08-05 07:28:57 +01:00
} )
testCases = append ( testCases , testCase {
2014-08-11 23:43:38 +01:00
protocol : protocol ,
2014-08-05 07:28:57 +01:00
testType : serverTest ,
2014-08-11 23:43:38 +01:00
name : "ClientAuth-Server" + suffix ,
2014-08-05 07:28:57 +01:00
config : Config {
2014-08-11 23:43:38 +01:00
Certificates : [ ] Certificate { rsaCertificate } ,
2014-08-05 07:28:57 +01:00
} ,
2014-08-11 23:43:38 +01:00
flags : append ( flags , "-require-any-client-certificate" ) ,
2014-08-05 07:28:57 +01:00
} )
2014-08-11 23:43:38 +01:00
// No session ticket support; server doesn't send NewSessionTicket.
2014-08-05 07:28:57 +01:00
testCases = append ( testCases , testCase {
2014-08-11 23:43:38 +01:00
protocol : protocol ,
name : "SessionTicketsDisabled-Client" + suffix ,
2014-08-05 07:28:57 +01:00
config : Config {
2014-08-11 23:43:38 +01:00
SessionTicketsDisabled : true ,
2014-08-05 07:28:57 +01:00
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
2014-08-11 23:43:38 +01:00
flags : flags ,
2014-08-05 07:28:57 +01:00
} )
testCases = append ( testCases , testCase {
2014-08-11 23:43:38 +01:00
protocol : protocol ,
2014-08-05 07:28:57 +01:00
testType : serverTest ,
2014-08-11 23:43:38 +01:00
name : "SessionTicketsDisabled-Server" + suffix ,
2014-08-05 07:28:57 +01:00
config : Config {
2014-08-11 23:43:38 +01:00
SessionTicketsDisabled : true ,
2014-08-05 07:28:57 +01:00
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
2014-08-11 23:43:38 +01:00
flags : flags ,
2014-08-05 07:28:57 +01:00
} )
2014-10-27 05:06:24 +00:00
// Skip ServerKeyExchange in PSK key exchange if there's no
// identity hint.
testCases = append ( testCases , testCase {
protocol : protocol ,
name : "EmptyPSKHint-Client" + suffix ,
config : Config {
CipherSuites : [ ] uint16 { TLS_PSK_WITH_AES_128_CBC_SHA } ,
PreSharedKey : [ ] byte ( "secret" ) ,
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
flags : append ( flags , "-psk" , "secret" ) ,
} )
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : serverTest ,
name : "EmptyPSKHint-Server" + suffix ,
config : Config {
CipherSuites : [ ] uint16 { TLS_PSK_WITH_AES_128_CBC_SHA } ,
PreSharedKey : [ ] byte ( "secret" ) ,
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
flags : append ( flags , "-psk" , "secret" ) ,
} )
2014-08-11 23:43:38 +01:00
if protocol == tls {
// NPN on client and server; results in post-handshake message.
testCases = append ( testCases , testCase {
protocol : protocol ,
name : "NPN-Client" + suffix ,
config : Config {
2014-09-06 17:58:58 +01:00
NextProtos : [ ] string { "foo" } ,
2014-08-11 23:43:38 +01:00
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
2014-08-05 07:28:57 +01:00
} ,
2014-09-06 18:21:53 +01:00
flags : append ( flags , "-select-next-proto" , "foo" ) ,
expectedNextProto : "foo" ,
expectedNextProtoType : npn ,
2014-08-11 23:43:38 +01:00
} )
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : serverTest ,
name : "NPN-Server" + suffix ,
config : Config {
NextProtos : [ ] string { "bar" } ,
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
flags : append ( flags ,
"-advertise-npn" , "\x03foo\x03bar\x03baz" ,
"-expect-next-proto" , "bar" ) ,
2014-09-06 18:21:53 +01:00
expectedNextProto : "bar" ,
expectedNextProtoType : npn ,
2014-08-11 23:43:38 +01:00
} )
2014-08-05 07:28:57 +01:00
2014-08-11 23:43:38 +01:00
// Client does False Start and negotiates NPN.
testCases = append ( testCases , testCase {
protocol : protocol ,
name : "FalseStart" + suffix ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
NextProtos : [ ] string { "foo" } ,
Bugs : ProtocolBugs {
2014-08-24 08:47:07 +01:00
ExpectFalseStart : true ,
2014-08-11 23:43:38 +01:00
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
2014-08-05 07:28:57 +01:00
} ,
2014-08-11 23:43:38 +01:00
flags : append ( flags ,
"-false-start" ,
"-select-next-proto" , "foo" ) ,
2014-08-24 08:47:07 +01:00
shimWritesFirst : true ,
resumeSession : true ,
2014-08-11 23:43:38 +01:00
} )
2014-08-08 17:27:04 +01:00
2014-09-06 17:58:58 +01:00
// Client does False Start and negotiates ALPN.
testCases = append ( testCases , testCase {
protocol : protocol ,
name : "FalseStart-ALPN" + suffix ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
NextProtos : [ ] string { "foo" } ,
Bugs : ProtocolBugs {
ExpectFalseStart : true ,
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
flags : append ( flags ,
"-false-start" ,
"-advertise-alpn" , "\x03foo" ) ,
shimWritesFirst : true ,
resumeSession : true ,
} )
2014-08-11 23:43:38 +01:00
// False Start without session tickets.
testCases = append ( testCases , testCase {
name : "FalseStart-SessionTicketsDisabled" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
NextProtos : [ ] string { "foo" } ,
SessionTicketsDisabled : true ,
2014-08-24 06:45:30 +01:00
Bugs : ProtocolBugs {
2014-08-24 08:47:07 +01:00
ExpectFalseStart : true ,
2014-08-24 06:45:30 +01:00
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
2014-08-08 17:27:04 +01:00
} ,
2014-08-24 06:45:30 +01:00
flags : append ( flags ,
2014-08-11 23:43:38 +01:00
"-false-start" ,
"-select-next-proto" , "foo" ,
2014-08-24 06:45:30 +01:00
) ,
2014-08-24 08:47:07 +01:00
shimWritesFirst : true ,
2014-08-11 23:43:38 +01:00
} )
2014-08-24 06:46:07 +01:00
// Server parses a V2ClientHello.
2014-08-11 23:43:38 +01:00
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : serverTest ,
name : "SendV2ClientHello" + suffix ,
config : Config {
// Choose a cipher suite that does not involve
// elliptic curves, so no extensions are
// involved.
CipherSuites : [ ] uint16 { TLS_RSA_WITH_RC4_128_SHA } ,
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
SendV2ClientHello : true ,
} ,
} ,
flags : flags ,
} )
2014-08-24 06:46:07 +01:00
// Client sends a Channel ID.
testCases = append ( testCases , testCase {
protocol : protocol ,
name : "ChannelID-Client" + suffix ,
config : Config {
RequestChannelID : true ,
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
flags : append ( flags ,
"-send-channel-id" , channelIDKeyFile ,
) ,
resumeSession : true ,
expectChannelID : true ,
} )
// Server accepts a Channel ID.
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : serverTest ,
name : "ChannelID-Server" + suffix ,
config : Config {
ChannelID : channelIDKey ,
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
flags : append ( flags ,
"-expect-channel-id" ,
base64 . StdEncoding . EncodeToString ( channelIDBytes ) ,
) ,
resumeSession : true ,
expectChannelID : true ,
} )
2014-08-11 23:43:38 +01:00
} else {
testCases = append ( testCases , testCase {
protocol : protocol ,
name : "SkipHelloVerifyRequest" + suffix ,
config : Config {
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
SkipHelloVerifyRequest : true ,
} ,
} ,
flags : flags ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
protocol : protocol ,
name : "CookieExchange" + suffix ,
config : Config {
Bugs : ProtocolBugs {
MaxHandshakeRecordLength : maxHandshakeRecordLength ,
} ,
} ,
flags : append ( flags , "-cookie-exchange" ) ,
} )
}
2014-08-05 07:28:57 +01:00
}
2014-08-07 22:44:24 +01:00
func addVersionNegotiationTests ( ) {
for i , shimVers := range tlsVersions {
// Assemble flags to disable all newer versions on the shim.
var flags [ ] string
for _ , vers := range tlsVersions [ i + 1 : ] {
flags = append ( flags , vers . flag )
}
for _ , runnerVers := range tlsVersions {
2014-11-23 07:47:52 +00:00
protocols := [ ] protocol { tls }
if runnerVers . hasDTLS && shimVers . hasDTLS {
protocols = append ( protocols , dtls )
2014-08-07 22:44:24 +01:00
}
2014-11-23 07:47:52 +00:00
for _ , protocol := range protocols {
expectedVersion := shimVers . version
if runnerVers . version < shimVers . version {
expectedVersion = runnerVers . version
}
2014-08-07 22:44:24 +01:00
2014-11-23 07:47:52 +00:00
suffix := shimVers . name + "-" + runnerVers . name
if protocol == dtls {
suffix += "-DTLS"
}
2014-08-07 22:44:24 +01:00
2014-12-12 23:17:51 +00:00
shimVersFlag := strconv . Itoa ( int ( versionToWire ( shimVers . version , protocol == dtls ) ) )
2014-12-10 07:27:24 +00:00
clientVers := shimVers . version
if clientVers > VersionTLS10 {
clientVers = VersionTLS10
}
2014-11-23 07:47:52 +00:00
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : clientTest ,
name : "VersionNegotiation-Client-" + suffix ,
config : Config {
MaxVersion : runnerVers . version ,
2014-12-10 07:27:24 +00:00
Bugs : ProtocolBugs {
ExpectInitialRecordVersion : clientVers ,
} ,
2014-11-23 07:47:52 +00:00
} ,
flags : flags ,
expectedVersion : expectedVersion ,
} )
2014-12-12 23:17:51 +00:00
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : clientTest ,
name : "VersionNegotiation-Client2-" + suffix ,
config : Config {
MaxVersion : runnerVers . version ,
Bugs : ProtocolBugs {
ExpectInitialRecordVersion : clientVers ,
} ,
} ,
flags : [ ] string { "-max-version" , shimVersFlag } ,
expectedVersion : expectedVersion ,
} )
2014-11-23 07:47:52 +00:00
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : serverTest ,
name : "VersionNegotiation-Server-" + suffix ,
config : Config {
MaxVersion : runnerVers . version ,
2014-12-10 07:27:24 +00:00
Bugs : ProtocolBugs {
ExpectInitialRecordVersion : expectedVersion ,
} ,
2014-11-23 07:47:52 +00:00
} ,
flags : flags ,
expectedVersion : expectedVersion ,
} )
2014-12-12 23:17:51 +00:00
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : serverTest ,
name : "VersionNegotiation-Server2-" + suffix ,
config : Config {
MaxVersion : runnerVers . version ,
Bugs : ProtocolBugs {
ExpectInitialRecordVersion : expectedVersion ,
} ,
} ,
flags : [ ] string { "-max-version" , shimVersFlag } ,
expectedVersion : expectedVersion ,
} )
2014-11-23 07:47:52 +00:00
}
2014-08-07 22:44:24 +01:00
}
}
}
2014-12-13 04:44:33 +00:00
func addMinimumVersionTests ( ) {
for i , shimVers := range tlsVersions {
// Assemble flags to disable all older versions on the shim.
var flags [ ] string
for _ , vers := range tlsVersions [ : i ] {
flags = append ( flags , vers . flag )
}
for _ , runnerVers := range tlsVersions {
protocols := [ ] protocol { tls }
if runnerVers . hasDTLS && shimVers . hasDTLS {
protocols = append ( protocols , dtls )
}
for _ , protocol := range protocols {
suffix := shimVers . name + "-" + runnerVers . name
if protocol == dtls {
suffix += "-DTLS"
}
shimVersFlag := strconv . Itoa ( int ( versionToWire ( shimVers . version , protocol == dtls ) ) )
var expectedVersion uint16
var shouldFail bool
var expectedError string
2014-12-13 06:55:01 +00:00
var expectedLocalError string
2014-12-13 04:44:33 +00:00
if runnerVers . version >= shimVers . version {
expectedVersion = runnerVers . version
} else {
shouldFail = true
expectedError = ":UNSUPPORTED_PROTOCOL:"
2014-12-13 06:55:01 +00:00
if runnerVers . version > VersionSSL30 {
expectedLocalError = "remote error: protocol version not supported"
} else {
expectedLocalError = "remote error: handshake failure"
}
2014-12-13 04:44:33 +00:00
}
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : clientTest ,
name : "MinimumVersion-Client-" + suffix ,
config : Config {
MaxVersion : runnerVers . version ,
} ,
2014-12-13 06:55:01 +00:00
flags : flags ,
expectedVersion : expectedVersion ,
shouldFail : shouldFail ,
expectedError : expectedError ,
expectedLocalError : expectedLocalError ,
2014-12-13 04:44:33 +00:00
} )
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : clientTest ,
name : "MinimumVersion-Client2-" + suffix ,
config : Config {
MaxVersion : runnerVers . version ,
} ,
2014-12-13 06:55:01 +00:00
flags : [ ] string { "-min-version" , shimVersFlag } ,
expectedVersion : expectedVersion ,
shouldFail : shouldFail ,
expectedError : expectedError ,
expectedLocalError : expectedLocalError ,
2014-12-13 04:44:33 +00:00
} )
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : serverTest ,
name : "MinimumVersion-Server-" + suffix ,
config : Config {
MaxVersion : runnerVers . version ,
} ,
2014-12-13 06:55:01 +00:00
flags : flags ,
expectedVersion : expectedVersion ,
shouldFail : shouldFail ,
expectedError : expectedError ,
expectedLocalError : expectedLocalError ,
2014-12-13 04:44:33 +00:00
} )
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : serverTest ,
name : "MinimumVersion-Server2-" + suffix ,
config : Config {
MaxVersion : runnerVers . version ,
} ,
2014-12-13 06:55:01 +00:00
flags : [ ] string { "-min-version" , shimVersFlag } ,
expectedVersion : expectedVersion ,
shouldFail : shouldFail ,
expectedError : expectedError ,
expectedLocalError : expectedLocalError ,
2014-12-13 04:44:33 +00:00
} )
}
}
}
}
2014-08-31 05:59:27 +01:00
func addD5BugTests ( ) {
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "D5Bug-NoQuirk-Reject" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_RSA_WITH_AES_128_GCM_SHA256 } ,
Bugs : ProtocolBugs {
SSL3RSAKeyExchange : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG:" ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "D5Bug-Quirk-Normal" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_RSA_WITH_AES_128_GCM_SHA256 } ,
} ,
flags : [ ] string { "-tls-d5-bug" } ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "D5Bug-Quirk-Bug" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_RSA_WITH_AES_128_GCM_SHA256 } ,
Bugs : ProtocolBugs {
SSL3RSAKeyExchange : true ,
} ,
} ,
flags : [ ] string { "-tls-d5-bug" } ,
} )
}
2014-09-06 17:45:15 +01:00
func addExtensionTests ( ) {
testCases = append ( testCases , testCase {
testType : clientTest ,
name : "DuplicateExtensionClient" ,
config : Config {
Bugs : ProtocolBugs {
DuplicateExtension : true ,
} ,
} ,
shouldFail : true ,
expectedLocalError : "remote error: error decoding message" ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "DuplicateExtensionServer" ,
config : Config {
Bugs : ProtocolBugs {
DuplicateExtension : true ,
} ,
} ,
shouldFail : true ,
expectedLocalError : "remote error: error decoding message" ,
} )
testCases = append ( testCases , testCase {
testType : clientTest ,
name : "ServerNameExtensionClient" ,
config : Config {
Bugs : ProtocolBugs {
ExpectServerName : "example.com" ,
} ,
} ,
flags : [ ] string { "-host-name" , "example.com" } ,
} )
testCases = append ( testCases , testCase {
testType : clientTest ,
name : "ServerNameExtensionClient" ,
config : Config {
Bugs : ProtocolBugs {
ExpectServerName : "mismatch.com" ,
} ,
} ,
flags : [ ] string { "-host-name" , "example.com" } ,
shouldFail : true ,
expectedLocalError : "tls: unexpected server name" ,
} )
testCases = append ( testCases , testCase {
testType : clientTest ,
name : "ServerNameExtensionClient" ,
config : Config {
Bugs : ProtocolBugs {
ExpectServerName : "missing.com" ,
} ,
} ,
shouldFail : true ,
expectedLocalError : "tls: unexpected server name" ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "ServerNameExtensionServer" ,
config : Config {
ServerName : "example.com" ,
} ,
flags : [ ] string { "-expect-server-name" , "example.com" } ,
resumeSession : true ,
} )
2014-09-06 17:58:58 +01:00
testCases = append ( testCases , testCase {
testType : clientTest ,
name : "ALPNClient" ,
config : Config {
NextProtos : [ ] string { "foo" } ,
} ,
flags : [ ] string {
"-advertise-alpn" , "\x03foo\x03bar\x03baz" ,
"-expect-alpn" , "foo" ,
} ,
2014-09-06 18:21:53 +01:00
expectedNextProto : "foo" ,
expectedNextProtoType : alpn ,
resumeSession : true ,
2014-09-06 17:58:58 +01:00
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "ALPNServer" ,
config : Config {
NextProtos : [ ] string { "foo" , "bar" , "baz" } ,
} ,
flags : [ ] string {
"-expect-advertised-alpn" , "\x03foo\x03bar\x03baz" ,
"-select-alpn" , "foo" ,
} ,
2014-09-06 18:21:53 +01:00
expectedNextProto : "foo" ,
expectedNextProtoType : alpn ,
resumeSession : true ,
} )
// Test that the server prefers ALPN over NPN.
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "ALPNServer-Preferred" ,
config : Config {
NextProtos : [ ] string { "foo" , "bar" , "baz" } ,
} ,
flags : [ ] string {
"-expect-advertised-alpn" , "\x03foo\x03bar\x03baz" ,
"-select-alpn" , "foo" ,
"-advertise-npn" , "\x03foo\x03bar\x03baz" ,
} ,
expectedNextProto : "foo" ,
expectedNextProtoType : alpn ,
resumeSession : true ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "ALPNServer-Preferred-Swapped" ,
config : Config {
NextProtos : [ ] string { "foo" , "bar" , "baz" } ,
Bugs : ProtocolBugs {
SwapNPNAndALPN : true ,
} ,
} ,
flags : [ ] string {
"-expect-advertised-alpn" , "\x03foo\x03bar\x03baz" ,
"-select-alpn" , "foo" ,
"-advertise-npn" , "\x03foo\x03bar\x03baz" ,
} ,
expectedNextProto : "foo" ,
expectedNextProtoType : alpn ,
resumeSession : true ,
2014-09-06 17:58:58 +01:00
} )
2014-10-17 03:04:35 +01:00
// Resume with a corrupt ticket.
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "CorruptTicket" ,
config : Config {
Bugs : ProtocolBugs {
CorruptTicket : true ,
} ,
} ,
resumeSession : true ,
flags : [ ] string { "-expect-session-miss" } ,
} )
// Resume with an oversized session id.
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "OversizedSessionId" ,
config : Config {
Bugs : ProtocolBugs {
OversizedSessionId : true ,
} ,
} ,
resumeSession : true ,
2014-10-11 00:23:43 +01:00
shouldFail : true ,
2014-10-17 03:04:35 +01:00
expectedError : ":DECODE_ERROR:" ,
} )
2014-11-16 00:06:08 +00:00
// Basic DTLS-SRTP tests. Include fake profiles to ensure they
// are ignored.
testCases = append ( testCases , testCase {
protocol : dtls ,
name : "SRTP-Client" ,
config : Config {
SRTPProtectionProfiles : [ ] uint16 { 40 , SRTP_AES128_CM_HMAC_SHA1_80 , 42 } ,
} ,
flags : [ ] string {
"-srtp-profiles" ,
"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32" ,
} ,
expectedSRTPProtectionProfile : SRTP_AES128_CM_HMAC_SHA1_80 ,
} )
testCases = append ( testCases , testCase {
protocol : dtls ,
testType : serverTest ,
name : "SRTP-Server" ,
config : Config {
SRTPProtectionProfiles : [ ] uint16 { 40 , SRTP_AES128_CM_HMAC_SHA1_80 , 42 } ,
} ,
flags : [ ] string {
"-srtp-profiles" ,
"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32" ,
} ,
expectedSRTPProtectionProfile : SRTP_AES128_CM_HMAC_SHA1_80 ,
} )
// Test that the MKI is ignored.
testCases = append ( testCases , testCase {
protocol : dtls ,
testType : serverTest ,
name : "SRTP-Server-IgnoreMKI" ,
config : Config {
SRTPProtectionProfiles : [ ] uint16 { SRTP_AES128_CM_HMAC_SHA1_80 } ,
Bugs : ProtocolBugs {
SRTPMasterKeyIdentifer : "bogus" ,
} ,
} ,
flags : [ ] string {
"-srtp-profiles" ,
"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32" ,
} ,
expectedSRTPProtectionProfile : SRTP_AES128_CM_HMAC_SHA1_80 ,
} )
// Test that SRTP isn't negotiated on the server if there were
// no matching profiles.
testCases = append ( testCases , testCase {
protocol : dtls ,
testType : serverTest ,
name : "SRTP-Server-NoMatch" ,
config : Config {
SRTPProtectionProfiles : [ ] uint16 { 100 , 101 , 102 } ,
} ,
flags : [ ] string {
"-srtp-profiles" ,
"SRTP_AES128_CM_SHA1_80:SRTP_AES128_CM_SHA1_32" ,
} ,
expectedSRTPProtectionProfile : 0 ,
} )
// Test that the server returning an invalid SRTP profile is
// flagged as an error by the client.
testCases = append ( testCases , testCase {
protocol : dtls ,
name : "SRTP-Client-NoMatch" ,
config : Config {
Bugs : ProtocolBugs {
SendSRTPProtectionProfile : SRTP_AES128_CM_HMAC_SHA1_32 ,
} ,
} ,
flags : [ ] string {
"-srtp-profiles" ,
"SRTP_AES128_CM_SHA1_80" ,
} ,
shouldFail : true ,
expectedError : ":BAD_SRTP_PROTECTION_PROFILE_LIST:" ,
} )
2014-11-25 06:55:35 +00:00
// Test OCSP stapling and SCT list.
testCases = append ( testCases , testCase {
name : "OCSPStapling" ,
flags : [ ] string {
"-enable-ocsp-stapling" ,
"-expect-ocsp-response" ,
base64 . StdEncoding . EncodeToString ( testOCSPResponse ) ,
} ,
} )
testCases = append ( testCases , testCase {
name : "SignedCertificateTimestampList" ,
flags : [ ] string {
"-enable-signed-cert-timestamps" ,
"-expect-signed-cert-timestamps" ,
base64 . StdEncoding . EncodeToString ( testSCTList ) ,
} ,
} )
2014-09-06 17:45:15 +01:00
}
2014-09-24 20:21:44 +01:00
func addResumptionVersionTests ( ) {
for _ , sessionVers := range tlsVersions {
for _ , resumeVers := range tlsVersions {
2014-11-23 07:47:52 +00:00
protocols := [ ] protocol { tls }
if sessionVers . hasDTLS && resumeVers . hasDTLS {
protocols = append ( protocols , dtls )
}
for _ , protocol := range protocols {
suffix := "-" + sessionVers . name + "-" + resumeVers . name
if protocol == dtls {
suffix += "-DTLS"
}
2014-09-24 20:21:44 +01:00
2014-11-23 07:47:52 +00:00
testCases = append ( testCases , testCase {
protocol : protocol ,
name : "Resume-Client" + suffix ,
resumeSession : true ,
config : Config {
MaxVersion : sessionVers . version ,
CipherSuites : [ ] uint16 { TLS_RSA_WITH_AES_128_CBC_SHA } ,
Bugs : ProtocolBugs {
AllowSessionVersionMismatch : true ,
} ,
2014-09-24 20:21:44 +01:00
} ,
2014-11-23 07:47:52 +00:00
expectedVersion : sessionVers . version ,
resumeConfig : & Config {
MaxVersion : resumeVers . version ,
CipherSuites : [ ] uint16 { TLS_RSA_WITH_AES_128_CBC_SHA } ,
Bugs : ProtocolBugs {
AllowSessionVersionMismatch : true ,
} ,
2014-09-24 20:21:44 +01:00
} ,
2014-11-23 07:47:52 +00:00
expectedResumeVersion : resumeVers . version ,
} )
2014-09-24 20:21:44 +01:00
2014-11-23 07:47:52 +00:00
testCases = append ( testCases , testCase {
protocol : protocol ,
name : "Resume-Client-NoResume" + suffix ,
flags : [ ] string { "-expect-session-miss" } ,
resumeSession : true ,
config : Config {
MaxVersion : sessionVers . version ,
CipherSuites : [ ] uint16 { TLS_RSA_WITH_AES_128_CBC_SHA } ,
} ,
expectedVersion : sessionVers . version ,
resumeConfig : & Config {
MaxVersion : resumeVers . version ,
CipherSuites : [ ] uint16 { TLS_RSA_WITH_AES_128_CBC_SHA } ,
} ,
newSessionsOnResume : true ,
expectedResumeVersion : resumeVers . version ,
} )
2014-11-11 05:52:15 +00:00
2014-11-23 07:47:52 +00:00
var flags [ ] string
if sessionVers . version != resumeVers . version {
flags = append ( flags , "-expect-session-miss" )
}
testCases = append ( testCases , testCase {
protocol : protocol ,
testType : serverTest ,
name : "Resume-Server" + suffix ,
flags : flags ,
resumeSession : true ,
config : Config {
MaxVersion : sessionVers . version ,
CipherSuites : [ ] uint16 { TLS_RSA_WITH_AES_128_CBC_SHA } ,
} ,
expectedVersion : sessionVers . version ,
resumeConfig : & Config {
MaxVersion : resumeVers . version ,
CipherSuites : [ ] uint16 { TLS_RSA_WITH_AES_128_CBC_SHA } ,
} ,
expectedResumeVersion : resumeVers . version ,
} )
2014-11-11 05:52:15 +00:00
}
2014-09-24 20:21:44 +01:00
}
}
}
2014-10-29 00:29:33 +00:00
func addRenegotiationTests ( ) {
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "Renegotiate-Server" ,
flags : [ ] string { "-renegotiate" } ,
shimWritesFirst : true ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "Renegotiate-Server-EmptyExt" ,
config : Config {
Bugs : ProtocolBugs {
EmptyRenegotiationInfo : true ,
} ,
} ,
flags : [ ] string { "-renegotiate" } ,
shimWritesFirst : true ,
shouldFail : true ,
expectedError : ":RENEGOTIATION_MISMATCH:" ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "Renegotiate-Server-BadExt" ,
config : Config {
Bugs : ProtocolBugs {
BadRenegotiationInfo : true ,
} ,
} ,
flags : [ ] string { "-renegotiate" } ,
shimWritesFirst : true ,
shouldFail : true ,
expectedError : ":RENEGOTIATION_MISMATCH:" ,
} )
2014-11-08 17:31:52 +00:00
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "Renegotiate-Server-ClientInitiated" ,
renegotiate : true ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "Renegotiate-Server-ClientInitiated-NoExt" ,
renegotiate : true ,
config : Config {
Bugs : ProtocolBugs {
NoRenegotiationInfo : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":UNSAFE_LEGACY_RENEGOTIATION_DISABLED:" ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "Renegotiate-Server-ClientInitiated-NoExt-Allowed" ,
renegotiate : true ,
config : Config {
Bugs : ProtocolBugs {
NoRenegotiationInfo : true ,
} ,
} ,
flags : [ ] string { "-allow-unsafe-legacy-renegotiation" } ,
} )
2014-10-29 00:29:33 +00:00
// TODO(agl): test the renegotiation info SCSV.
2014-10-29 02:06:14 +00:00
testCases = append ( testCases , testCase {
name : "Renegotiate-Client" ,
renegotiate : true ,
} )
testCases = append ( testCases , testCase {
name : "Renegotiate-Client-EmptyExt" ,
renegotiate : true ,
config : Config {
Bugs : ProtocolBugs {
EmptyRenegotiationInfo : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":RENEGOTIATION_MISMATCH:" ,
} )
testCases = append ( testCases , testCase {
name : "Renegotiate-Client-BadExt" ,
renegotiate : true ,
config : Config {
Bugs : ProtocolBugs {
BadRenegotiationInfo : true ,
} ,
} ,
shouldFail : true ,
expectedError : ":RENEGOTIATION_MISMATCH:" ,
} )
testCases = append ( testCases , testCase {
name : "Renegotiate-Client-SwitchCiphers" ,
renegotiate : true ,
config : Config {
CipherSuites : [ ] uint16 { TLS_RSA_WITH_RC4_128_SHA } ,
} ,
renegotiateCiphers : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
} )
testCases = append ( testCases , testCase {
name : "Renegotiate-Client-SwitchCiphers2" ,
renegotiate : true ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
} ,
renegotiateCiphers : [ ] uint16 { TLS_RSA_WITH_RC4_128_SHA } ,
} )
2014-11-23 17:11:01 +00:00
testCases = append ( testCases , testCase {
name : "Renegotiate-SameClientVersion" ,
renegotiate : true ,
config : Config {
MaxVersion : VersionTLS10 ,
Bugs : ProtocolBugs {
RequireSameRenegoClientVersion : true ,
} ,
} ,
} )
2014-10-29 00:29:33 +00:00
}
2014-11-07 06:48:35 +00:00
func addDTLSReplayTests ( ) {
// Test that sequence number replays are detected.
testCases = append ( testCases , testCase {
protocol : dtls ,
name : "DTLS-Replay" ,
replayWrites : true ,
} )
// Test the outgoing sequence number skipping by values larger
// than the retransmit window.
testCases = append ( testCases , testCase {
protocol : dtls ,
name : "DTLS-Replay-LargeGaps" ,
config : Config {
Bugs : ProtocolBugs {
SequenceNumberIncrement : 127 ,
} ,
} ,
replayWrites : true ,
} )
}
2014-11-22 06:47:56 +00:00
func addFastRadioPaddingTests ( ) {
2014-12-10 07:27:24 +00:00
testCases = append ( testCases , testCase {
protocol : tls ,
name : "FastRadio-Padding" ,
2014-11-22 06:47:56 +00:00
config : Config {
Bugs : ProtocolBugs {
RequireFastradioPadding : true ,
} ,
} ,
2014-12-10 07:27:24 +00:00
flags : [ ] string { "-fastradio-padding" } ,
2014-11-22 06:47:56 +00:00
} )
2014-12-10 07:27:24 +00:00
testCases = append ( testCases , testCase {
protocol : dtls ,
name : "FastRadio-Padding" ,
2014-11-22 06:47:56 +00:00
config : Config {
Bugs : ProtocolBugs {
RequireFastradioPadding : true ,
} ,
} ,
2014-12-10 07:27:24 +00:00
flags : [ ] string { "-fastradio-padding" } ,
2014-11-22 06:47:56 +00:00
} )
}
2014-11-14 06:43:59 +00:00
var testHashes = [ ] struct {
name string
id uint8
} {
{ "SHA1" , hashSHA1 } ,
{ "SHA224" , hashSHA224 } ,
{ "SHA256" , hashSHA256 } ,
{ "SHA384" , hashSHA384 } ,
{ "SHA512" , hashSHA512 } ,
}
func addSigningHashTests ( ) {
// Make sure each hash works. Include some fake hashes in the list and
// ensure they're ignored.
for _ , hash := range testHashes {
testCases = append ( testCases , testCase {
name : "SigningHash-ClientAuth-" + hash . name ,
config : Config {
ClientAuth : RequireAnyClientCert ,
SignatureAndHashes : [ ] signatureAndHash {
{ signatureRSA , 42 } ,
{ signatureRSA , hash . id } ,
{ signatureRSA , 255 } ,
} ,
} ,
flags : [ ] string {
"-cert-file" , rsaCertificateFile ,
"-key-file" , rsaKeyFile ,
} ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "SigningHash-ServerKeyExchange-Sign-" + hash . name ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
SignatureAndHashes : [ ] signatureAndHash {
{ signatureRSA , 42 } ,
{ signatureRSA , hash . id } ,
{ signatureRSA , 255 } ,
} ,
} ,
} )
}
// Test that hash resolution takes the signature type into account.
testCases = append ( testCases , testCase {
name : "SigningHash-ClientAuth-SignatureType" ,
config : Config {
ClientAuth : RequireAnyClientCert ,
SignatureAndHashes : [ ] signatureAndHash {
{ signatureECDSA , hashSHA512 } ,
{ signatureRSA , hashSHA384 } ,
{ signatureECDSA , hashSHA1 } ,
} ,
} ,
flags : [ ] string {
"-cert-file" , rsaCertificateFile ,
"-key-file" , rsaKeyFile ,
} ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "SigningHash-ServerKeyExchange-SignatureType" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
SignatureAndHashes : [ ] signatureAndHash {
{ signatureECDSA , hashSHA512 } ,
{ signatureRSA , hashSHA384 } ,
{ signatureECDSA , hashSHA1 } ,
} ,
} ,
} )
// Test that, if the list is missing, the peer falls back to SHA-1.
testCases = append ( testCases , testCase {
name : "SigningHash-ClientAuth-Fallback" ,
config : Config {
ClientAuth : RequireAnyClientCert ,
SignatureAndHashes : [ ] signatureAndHash {
{ signatureRSA , hashSHA1 } ,
} ,
Bugs : ProtocolBugs {
NoSignatureAndHashes : true ,
} ,
} ,
flags : [ ] string {
"-cert-file" , rsaCertificateFile ,
"-key-file" , rsaKeyFile ,
} ,
} )
testCases = append ( testCases , testCase {
testType : serverTest ,
name : "SigningHash-ServerKeyExchange-Fallback" ,
config : Config {
CipherSuites : [ ] uint16 { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 } ,
SignatureAndHashes : [ ] signatureAndHash {
{ signatureRSA , hashSHA1 } ,
} ,
Bugs : ProtocolBugs {
NoSignatureAndHashes : true ,
} ,
} ,
} )
}
Add DTLS timeout and retransmit tests.
This extends the packet adaptor protocol to send three commands:
type command =
| Packet of []byte
| Timeout of time.Duration
| TimeoutAck
When the shim processes a Timeout in BIO_read, it sends TimeoutAck, fails the
BIO_read, returns out of the SSL stack, advances the clock, calls
DTLSv1_handle_timeout, and continues.
If the Go side sends Timeout right between sending handshake flight N and
reading flight N+1, the shim won't read the Timeout until it has sent flight
N+1 (it only processes packet commands in BIO_read), so the TimeoutAck comes
after N+1. Go then drops all packets before the TimeoutAck, thus dropping one
transmit of flight N+1 without having to actually process the packets to
determine the end of the flight. The shim then sees the updated clock, calls
DTLSv1_handle_timeout, and re-sends flight N+1 for Go to process for real.
When dropping packets, Go checks the epoch and increments sequence numbers so
that we can continue to be strict here. This requires tracking the initial
sequence number of the next epoch.
The final Finished message takes an additional special-case to test. DTLS
triggers retransmits on either a timeout or seeing a stale flight. OpenSSL only
implements the former which should be sufficient (and is necessary) EXCEPT for
the final Finished message. If the peer's final Finished message is lost, it
won't be waiting for a message from us, so it won't time out anything. That
retransmit must be triggered on stale message, so we retransmit the Finished
message in Go.
Change-Id: I3ffbdb1de525beb2ee831b304670a3387877634c
Reviewed-on: https://boringssl-review.googlesource.com/3212
Reviewed-by: Adam Langley <agl@google.com>
2015-01-27 06:09:43 +00:00
// timeouts is the retransmit schedule for BoringSSL. It doubles and
// caps at 60 seconds. On the 13th timeout, it gives up.
var timeouts = [ ] time . Duration {
1 * time . Second ,
2 * time . Second ,
4 * time . Second ,
8 * time . Second ,
16 * time . Second ,
32 * time . Second ,
60 * time . Second ,
60 * time . Second ,
60 * time . Second ,
60 * time . Second ,
60 * time . Second ,
60 * time . Second ,
60 * time . Second ,
}
func addDTLSRetransmitTests ( ) {
// Test that this is indeed the timeout schedule. Stress all
// four patterns of handshake.
for i := 1 ; i < len ( timeouts ) ; i ++ {
number := strconv . Itoa ( i )
testCases = append ( testCases , testCase {
protocol : dtls ,
name : "DTLS-Retransmit-Client-" + number ,
config : Config {
Bugs : ProtocolBugs {
TimeoutSchedule : timeouts [ : i ] ,
} ,
} ,
resumeSession : true ,
flags : [ ] string { "-async" } ,
} )
testCases = append ( testCases , testCase {
protocol : dtls ,
testType : serverTest ,
name : "DTLS-Retransmit-Server-" + number ,
config : Config {
Bugs : ProtocolBugs {
TimeoutSchedule : timeouts [ : i ] ,
} ,
} ,
resumeSession : true ,
flags : [ ] string { "-async" } ,
} )
}
// Test that exceeding the timeout schedule hits a read
// timeout.
testCases = append ( testCases , testCase {
protocol : dtls ,
name : "DTLS-Retransmit-Timeout" ,
config : Config {
Bugs : ProtocolBugs {
TimeoutSchedule : timeouts ,
} ,
} ,
resumeSession : true ,
flags : [ ] string { "-async" } ,
shouldFail : true ,
expectedError : ":READ_TIMEOUT_EXPIRED:" ,
} )
// Test that timeout handling has a fudge factor, due to API
// problems.
testCases = append ( testCases , testCase {
protocol : dtls ,
name : "DTLS-Retransmit-Fudge" ,
config : Config {
Bugs : ProtocolBugs {
TimeoutSchedule : [ ] time . Duration {
timeouts [ 0 ] - 10 * time . Millisecond ,
} ,
} ,
} ,
resumeSession : true ,
flags : [ ] string { "-async" } ,
} )
}
2014-08-02 20:28:23 +01:00
func worker ( statusChan chan statusMsg , c chan * testCase , buildDir string , wg * sync . WaitGroup ) {
2014-06-20 20:00:00 +01:00
defer wg . Done ( )
for test := range c {
2014-11-18 01:26:55 +00:00
var err error
if * mallocTest < 0 {
statusChan <- statusMsg { test : test , started : true }
err = runTest ( test , buildDir , - 1 )
} else {
for mallocNumToFail := int64 ( * mallocTest ) ; ; mallocNumToFail ++ {
statusChan <- statusMsg { test : test , started : true }
if err = runTest ( test , buildDir , mallocNumToFail ) ; err != errMoreMallocs {
if err != nil {
fmt . Printf ( "\n\nmalloc test failed at %d: %s\n" , mallocNumToFail , err )
}
break
}
}
}
2014-06-20 20:00:00 +01:00
statusChan <- statusMsg { test : test , err : err }
}
}
type statusMsg struct {
test * testCase
started bool
err error
}
func statusPrinter ( doneChan chan struct { } , statusChan chan statusMsg , total int ) {
var started , done , failed , lineLen int
defer close ( doneChan )
for msg := range statusChan {
if msg . started {
started ++
} else {
done ++
}
fmt . Printf ( "\x1b[%dD\x1b[K" , lineLen )
if msg . err != nil {
fmt . Printf ( "FAILED (%s)\n%s\n" , msg . test . name , msg . err )
failed ++
}
line := fmt . Sprintf ( "%d/%d/%d/%d" , failed , done , started , total )
lineLen = len ( line )
os . Stdout . WriteString ( line )
}
}
func main ( ) {
var flagTest * string = flag . String ( "test" , "" , "The name of a test to run, or empty to run all tests" )
2014-08-02 20:22:37 +01:00
var flagNumWorkers * int = flag . Int ( "num-workers" , runtime . NumCPU ( ) , "The number of workers to run in parallel." )
2014-08-02 20:28:23 +01:00
var flagBuildDir * string = flag . String ( "build-dir" , "../../../build" , "The build directory to run the shim from." )
2014-06-20 20:00:00 +01:00
flag . Parse ( )
addCipherSuiteTests ( )
addBadECDSASignatureTests ( )
2014-06-20 20:00:00 +01:00
addCBCPaddingTests ( )
2014-08-05 23:23:37 +01:00
addCBCSplittingTests ( )
2014-07-08 22:59:18 +01:00
addClientAuthTests ( )
2014-08-07 22:44:24 +01:00
addVersionNegotiationTests ( )
2014-12-13 04:44:33 +00:00
addMinimumVersionTests ( )
2014-08-31 05:59:27 +01:00
addD5BugTests ( )
2014-09-06 17:45:15 +01:00
addExtensionTests ( )
2014-09-24 20:21:44 +01:00
addResumptionVersionTests ( )
2014-10-11 00:23:43 +01:00
addExtendedMasterSecretTests ( )
2014-10-29 00:29:33 +00:00
addRenegotiationTests ( )
2014-11-07 06:48:35 +00:00
addDTLSReplayTests ( )
2014-11-14 06:43:59 +00:00
addSigningHashTests ( )
2014-11-22 06:47:56 +00:00
addFastRadioPaddingTests ( )
Add DTLS timeout and retransmit tests.
This extends the packet adaptor protocol to send three commands:
type command =
| Packet of []byte
| Timeout of time.Duration
| TimeoutAck
When the shim processes a Timeout in BIO_read, it sends TimeoutAck, fails the
BIO_read, returns out of the SSL stack, advances the clock, calls
DTLSv1_handle_timeout, and continues.
If the Go side sends Timeout right between sending handshake flight N and
reading flight N+1, the shim won't read the Timeout until it has sent flight
N+1 (it only processes packet commands in BIO_read), so the TimeoutAck comes
after N+1. Go then drops all packets before the TimeoutAck, thus dropping one
transmit of flight N+1 without having to actually process the packets to
determine the end of the flight. The shim then sees the updated clock, calls
DTLSv1_handle_timeout, and re-sends flight N+1 for Go to process for real.
When dropping packets, Go checks the epoch and increments sequence numbers so
that we can continue to be strict here. This requires tracking the initial
sequence number of the next epoch.
The final Finished message takes an additional special-case to test. DTLS
triggers retransmits on either a timeout or seeing a stale flight. OpenSSL only
implements the former which should be sufficient (and is necessary) EXCEPT for
the final Finished message. If the peer's final Finished message is lost, it
won't be waiting for a message from us, so it won't time out anything. That
retransmit must be triggered on stale message, so we retransmit the Finished
message in Go.
Change-Id: I3ffbdb1de525beb2ee831b304670a3387877634c
Reviewed-on: https://boringssl-review.googlesource.com/3212
Reviewed-by: Adam Langley <agl@google.com>
2015-01-27 06:09:43 +00:00
addDTLSRetransmitTests ( )
2014-08-05 07:28:57 +01:00
for _ , async := range [ ] bool { false , true } {
for _ , splitHandshake := range [ ] bool { false , true } {
2014-08-11 23:43:38 +01:00
for _ , protocol := range [ ] protocol { tls , dtls } {
addStateMachineCoverageTests ( async , splitHandshake , protocol )
}
2014-08-05 07:28:57 +01:00
}
}
2014-06-20 20:00:00 +01:00
var wg sync . WaitGroup
2014-08-02 20:22:37 +01:00
numWorkers := * flagNumWorkers
2014-06-20 20:00:00 +01:00
statusChan := make ( chan statusMsg , numWorkers )
testChan := make ( chan * testCase , numWorkers )
doneChan := make ( chan struct { } )
2014-07-02 00:53:04 +01:00
go statusPrinter ( doneChan , statusChan , len ( testCases ) )
2014-06-20 20:00:00 +01:00
for i := 0 ; i < numWorkers ; i ++ {
wg . Add ( 1 )
2014-08-02 20:28:23 +01:00
go worker ( statusChan , testChan , * flagBuildDir , & wg )
2014-06-20 20:00:00 +01:00
}
2014-07-02 00:53:04 +01:00
for i := range testCases {
if len ( * flagTest ) == 0 || * flagTest == testCases [ i ] . name {
testChan <- & testCases [ i ]
2014-06-20 20:00:00 +01:00
}
}
close ( testChan )
wg . Wait ( )
close ( statusChan )
<- doneChan
fmt . Printf ( "\n" )
}