diff options
Diffstat (limited to 'src/ssl/test/runner')
-rw-r--r-- | src/ssl/test/runner/common.go | 34 | ||||
-rw-r--r-- | src/ssl/test/runner/conn.go | 16 | ||||
-rw-r--r-- | src/ssl/test/runner/dtls.go | 68 | ||||
-rw-r--r-- | src/ssl/test/runner/handshake_client.go | 27 | ||||
-rw-r--r-- | src/ssl/test/runner/handshake_server.go | 28 | ||||
-rw-r--r-- | src/ssl/test/runner/key_agreement.go | 17 | ||||
-rw-r--r-- | src/ssl/test/runner/runner.go | 700 |
7 files changed, 529 insertions, 361 deletions
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go index 4ac7250..edebba1 100644 --- a/src/ssl/test/runner/common.go +++ b/src/ssl/test/runner/common.go @@ -188,6 +188,7 @@ type ConnectionState struct { VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates ChannelID *ecdsa.PublicKey // the channel ID for this connection SRTPProtectionProfile uint16 // the negotiated DTLS-SRTP protection profile + TLSUnique []byte } // ClientAuthType declares the policy the server will follow for @@ -478,7 +479,9 @@ type ProtocolBugs struct { // MaxHandshakeRecordLength, if non-zero, is the maximum size of a // handshake record. Handshake messages will be split into multiple // records at the specified size, except that the client_version will - // never be fragmented. + // never be fragmented. For DTLS, it is the maximum handshake fragment + // size, not record size; DTLS allows multiple handshake fragments in a + // single handshake record. See |PackHandshakeFragments|. MaxHandshakeRecordLength int // FragmentClientVersion will allow MaxHandshakeRecordLength to apply to @@ -681,13 +684,14 @@ type ProtocolBugs struct { // fragments in DTLS. SendEmptyFragments bool - // NeverResumeOnRenego, if true, causes renegotiations to always be full - // handshakes. - NeverResumeOnRenego bool + // SendSplitAlert, if true, causes an alert to be sent with the header + // and record body split across multiple packets. The peer should + // discard these packets rather than process it. + SendSplitAlert bool - // NoSignatureAlgorithmsOnRenego, if true, causes renegotiations to omit - // the signature_algorithms extension. - NoSignatureAlgorithmsOnRenego bool + // FailIfResumeOnRenego, if true, causes renegotiations to fail if the + // client offers a resumption or the server accepts one. + FailIfResumeOnRenego bool // IgnorePeerCipherPreferences, if true, causes the peer's cipher // preferences to be ignored. @@ -707,6 +711,22 @@ type ProtocolBugs struct { // BadFinished, if true, causes the Finished hash to be broken. BadFinished bool + + // DHGroupPrime, if not nil, is used to define the (finite field) + // Diffie-Hellman group. The generator used is always two. + DHGroupPrime *big.Int + + // PackHandshakeFragments, if true, causes handshake fragments to be + // packed into individual handshake records, up to the specified record + // size. + PackHandshakeFragments int + + // PackHandshakeRecords, if true, causes handshake records to be packed + // into individual packets, up to the specified packet size. + PackHandshakeRecords int + + // EnableAllCiphersInDTLS, if true, causes RC4 to be enabled in DTLS. + EnableAllCiphersInDTLS bool } func (c *Config) serverInit() { diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go index fd198ca..adbc1c3 100644 --- a/src/ssl/test/runner/conn.go +++ b/src/ssl/test/runner/conn.go @@ -44,7 +44,11 @@ type Conn struct { // opposed to the ones presented by the server. verifiedChains [][]*x509.Certificate // serverName contains the server name indicated by the client, if any. - serverName string + serverName string + // firstFinished contains the first Finished hash sent during the + // handshake. This is the "tls-unique" channel binding value. + firstFinished [12]byte + clientRandom, serverRandom [32]byte masterSecret [48]byte @@ -1260,6 +1264,15 @@ func (c *Conn) Handshake() error { return nil } + if c.isDTLS && c.config.Bugs.SendSplitAlert { + c.conn.Write([]byte{ + byte(recordTypeAlert), // type + 0xfe, 0xff, // version + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // sequence + 0x0, 0x2, // length + }) + c.conn.Write([]byte{alertLevelError, byte(alertInternalError)}) + } if c.isClient { c.handshakeErr = c.clientHandshake() } else { @@ -1290,6 +1303,7 @@ func (c *Conn) ConnectionState() ConnectionState { state.ServerName = c.serverName state.ChannelID = c.channelID state.SRTPProtectionProfile = c.srtpProtectionProfile + state.TLSUnique = c.firstFinished[:] } return state diff --git a/src/ssl/test/runner/dtls.go b/src/ssl/test/runner/dtls.go index 85c4247..50f7786 100644 --- a/src/ssl/test/runner/dtls.go +++ b/src/ssl/test/runner/dtls.go @@ -196,6 +196,8 @@ func (c *Conn) dtlsFlushHandshake() error { return nil } + // This is a test-only DTLS implementation, so there is no need to + // retain |c.pendingFragments| for a future retransmit. var fragments [][]byte fragments, c.pendingFragments = c.pendingFragments, fragments @@ -208,38 +210,66 @@ func (c *Conn) dtlsFlushHandshake() error { fragments = tmp } - // Send them all. + maxRecordLen := c.config.Bugs.PackHandshakeFragments + maxPacketLen := c.config.Bugs.PackHandshakeRecords + + // Pack handshake fragments into records. + var records [][]byte for _, fragment := range fragments { if c.config.Bugs.SplitFragmentHeader { - if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment[:2]); err != nil { - return err - } - fragment = fragment[2:] - } else if c.config.Bugs.SplitFragmentBody && len(fragment) > 12 { - if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment[:13]); err != nil { - return err + records = append(records, fragment[:2]) + records = append(records, fragment[2:]) + } else if c.config.Bugs.SplitFragmentBody { + if len(fragment) > 12 { + records = append(records, fragment[:13]) + records = append(records, fragment[13:]) + } else { + records = append(records, fragment) } - fragment = fragment[13:] + } else if i := len(records) - 1; len(records) > 0 && len(records[i])+len(fragment) <= maxRecordLen { + records[i] = append(records[i], fragment...) + } else { + // The fragment will be appended to, so copy it. + records = append(records, append([]byte{}, fragment...)) + } + } + + // Format them into packets. + var packets [][]byte + for _, record := range records { + b, err := c.dtlsSealRecord(recordTypeHandshake, record) + if err != nil { + return err + } + + if i := len(packets) - 1; len(packets) > 0 && len(packets[i])+len(b.data) <= maxPacketLen { + packets[i] = append(packets[i], b.data...) + } else { + // The sealed record will be appended to and reused by + // |c.out|, so copy it. + packets = append(packets, append([]byte{}, b.data...)) } + c.out.freeBlock(b) + } - // TODO(davidben): A real DTLS implementation needs to - // retransmit handshake messages. For testing purposes, we don't - // actually care. - if _, err := c.dtlsWriteRawRecord(recordTypeHandshake, fragment); err != nil { + // Send all the packets. + for _, packet := range packets { + if _, err := c.conn.Write(packet); err != nil { return err } } return nil } -func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error) { +// dtlsSealRecord seals a record into a block from |c.out|'s pool. +func (c *Conn) dtlsSealRecord(typ recordType, data []byte) (b *block, err error) { recordHeaderLen := dtlsRecordHeaderLen maxLen := c.config.Bugs.MaxHandshakeRecordLength if maxLen <= 0 { maxLen = 1024 } - b := c.out.newBlock() + b = c.out.newBlock() explicitIVLen := 0 explicitIVIsSeq := false @@ -286,6 +316,14 @@ func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error } copy(b.data[recordHeaderLen+explicitIVLen:], data) c.out.encrypt(b, explicitIVLen) + return +} + +func (c *Conn) dtlsWriteRawRecord(typ recordType, data []byte) (n int, err error) { + b, err := c.dtlsSealRecord(typ, data) + if err != nil { + return + } _, err = c.conn.Write(b.data) if err != nil { diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go index 0dac05d..a950313 100644 --- a/src/ssl/test/runner/handshake_client.go +++ b/src/ssl/test/runner/handshake_client.go @@ -115,7 +115,7 @@ NextCipherSuite: continue } // Don't advertise non-DTLS cipher suites on DTLS. - if c.isDTLS && suite.flags&suiteNoDTLS != 0 { + if c.isDTLS && suite.flags&suiteNoDTLS != 0 && !c.config.Bugs.EnableAllCiphersInDTLS { continue } hello.cipherSuites = append(hello.cipherSuites, suiteId) @@ -133,16 +133,13 @@ NextCipherSuite: return errors.New("tls: short read from Rand: " + err.Error()) } - if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes && (c.cipherSuite == nil || !c.config.Bugs.NoSignatureAlgorithmsOnRenego) { + if hello.vers >= VersionTLS12 && !c.config.Bugs.NoSignatureAndHashes { hello.signatureAndHashes = c.config.signatureAndHashesForClient() } var session *ClientSessionState var cacheKey string sessionCache := c.config.ClientSessionCache - if c.config.Bugs.NeverResumeOnRenego && c.cipherSuite != nil { - sessionCache = nil - } if sessionCache != nil { hello.ticketSupported = !c.config.SessionTicketsDisabled @@ -316,10 +313,10 @@ NextCipherSuite: if err := hs.readSessionTicket(); err != nil { return err } - if err := hs.readFinished(); err != nil { + if err := hs.readFinished(c.firstFinished[:]); err != nil { return err } - if err := hs.sendFinished(isResume); err != nil { + if err := hs.sendFinished(nil, isResume); err != nil { return err } } else { @@ -329,7 +326,7 @@ NextCipherSuite: if err := hs.establishKeys(); err != nil { return err } - if err := hs.sendFinished(isResume); err != nil { + if err := hs.sendFinished(c.firstFinished[:], isResume); err != nil { return err } // Most retransmits are triggered by a timeout, but the final @@ -344,7 +341,7 @@ NextCipherSuite: if err := hs.readSessionTicket(); err != nil { return err } - if err := hs.readFinished(); err != nil { + if err := hs.readFinished(nil); err != nil { return err } } @@ -727,6 +724,12 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { } if hs.serverResumedSession() { + // For test purposes, assert that the server never accepts the + // resumption offer on renegotiation. + if c.cipherSuite != nil && c.config.Bugs.FailIfResumeOnRenego { + return false, errors.New("tls: server resumed session on renegotiation") + } + // Restore masterSecret and peerCerts from previous state hs.masterSecret = hs.session.masterSecret c.peerCertificates = hs.session.serverCertificates @@ -737,7 +740,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { return false, nil } -func (hs *clientHandshakeState) readFinished() error { +func (hs *clientHandshakeState) readFinished(out []byte) error { c := hs.c c.readRecord(recordTypeChangeCipherSpec) @@ -764,6 +767,7 @@ func (hs *clientHandshakeState) readFinished() error { } } c.serverVerify = append(c.serverVerify[:0], serverFinished.verifyData...) + copy(out, serverFinished.verifyData) hs.writeServerHash(serverFinished.marshal()) return nil } @@ -807,7 +811,7 @@ func (hs *clientHandshakeState) readSessionTicket() error { return nil } -func (hs *clientHandshakeState) sendFinished(isResume bool) error { +func (hs *clientHandshakeState) sendFinished(out []byte, isResume bool) error { c := hs.c var postCCSBytes []byte @@ -859,6 +863,7 @@ func (hs *clientHandshakeState) sendFinished(isResume bool) error { } else { finished.verifyData = hs.finishedHash.clientSum(hs.masterSecret) } + copy(out, finished.verifyData) if c.config.Bugs.BadFinished { finished.verifyData[0]++ } diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go index 59ed9df..85cc0d2 100644 --- a/src/ssl/test/runner/handshake_server.go +++ b/src/ssl/test/runner/handshake_server.go @@ -69,7 +69,7 @@ func (c *Conn) serverHandshake() error { return err } } - if err := hs.sendFinished(); err != nil { + if err := hs.sendFinished(c.firstFinished[:]); err != nil { return err } // Most retransmits are triggered by a timeout, but the final @@ -81,7 +81,7 @@ func (c *Conn) serverHandshake() error { }); err != nil { return err } - if err := hs.readFinished(isResume); err != nil { + if err := hs.readFinished(nil, isResume); err != nil { return err } c.didResume = true @@ -94,7 +94,7 @@ func (c *Conn) serverHandshake() error { if err := hs.establishKeys(); err != nil { return err } - if err := hs.readFinished(isResume); err != nil { + if err := hs.readFinished(c.firstFinished[:], isResume); err != nil { return err } if c.config.Bugs.AlertBeforeFalseStartTest != 0 { @@ -108,7 +108,7 @@ func (c *Conn) serverHandshake() error { if err := hs.sendSessionTicket(); err != nil { return err } - if err := hs.sendFinished(); err != nil { + if err := hs.sendFinished(nil); err != nil { return err } } @@ -274,6 +274,10 @@ Curves: hs.hello.secureRenegotiation = hs.clientHello.secureRenegotiation } + if c.config.Bugs.NoRenegotiationInfo { + hs.hello.secureRenegotiation = nil + } + hs.hello.compressionMethod = compressionNone hs.hello.duplicateExtension = c.config.Bugs.DuplicateExtension if len(hs.clientHello.serverName) > 0 { @@ -333,6 +337,12 @@ Curves: _, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey) + // For test purposes, check that the peer never offers a session when + // renegotiating. + if c.cipherSuite != nil && len(hs.clientHello.sessionId) > 0 && c.config.Bugs.FailIfResumeOnRenego { + return false, errors.New("tls: offered resumption on renegotiation") + } + if hs.checkForResumption() { return true, nil } @@ -382,10 +392,6 @@ Curves: func (hs *serverHandshakeState) checkForResumption() bool { c := hs.c - if c.config.Bugs.NeverResumeOnRenego && c.cipherSuite != nil { - return false - } - if len(hs.clientHello.sessionTicket) > 0 { if c.config.SessionTicketsDisabled { return false @@ -748,7 +754,7 @@ func (hs *serverHandshakeState) establishKeys() error { return nil } -func (hs *serverHandshakeState) readFinished(isResume bool) error { +func (hs *serverHandshakeState) readFinished(out []byte, isResume bool) error { c := hs.c c.readRecord(recordTypeChangeCipherSpec) @@ -817,6 +823,7 @@ func (hs *serverHandshakeState) readFinished(isResume bool) error { return errors.New("tls: client's Finished message is incorrect") } c.clientVerify = append(c.clientVerify[:0], clientFinished.verifyData...) + copy(out, clientFinished.verifyData) hs.writeClientHash(clientFinished.marshal()) return nil @@ -853,11 +860,12 @@ func (hs *serverHandshakeState) sendSessionTicket() error { return nil } -func (hs *serverHandshakeState) sendFinished() error { +func (hs *serverHandshakeState) sendFinished(out []byte) error { c := hs.c finished := new(finishedMsg) finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret) + copy(out, finished.verifyData) if c.config.Bugs.BadFinished { finished.verifyData[0]++ } diff --git a/src/ssl/test/runner/key_agreement.go b/src/ssl/test/runner/key_agreement.go index 5e44b54..2ee0087 100644 --- a/src/ssl/test/runner/key_agreement.go +++ b/src/ssl/test/runner/key_agreement.go @@ -561,11 +561,18 @@ type dheKeyAgreement struct { } func (ka *dheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { - // 2048-bit MODP Group with 256-bit Prime Order Subgroup (RFC - // 5114, Section 2.3) - ka.p, _ = new(big.Int).SetString("87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597", 16) - ka.g, _ = new(big.Int).SetString("3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659", 16) - q, _ := new(big.Int).SetString("8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3", 16) + var q *big.Int + if p := config.Bugs.DHGroupPrime; p != nil { + ka.p = p + ka.g = big.NewInt(2) + q = p + } else { + // 2048-bit MODP Group with 256-bit Prime Order Subgroup (RFC + // 5114, Section 2.3) + ka.p, _ = new(big.Int).SetString("87A8E61DB4B6663CFFBBD19C651959998CEEF608660DD0F25D2CEED4435E3B00E00DF8F1D61957D4FAF7DF4561B2AA3016C3D91134096FAA3BF4296D830E9A7C209E0C6497517ABD5A8A9D306BCF67ED91F9E6725B4758C022E0B1EF4275BF7B6C5BFC11D45F9088B941F54EB1E59BB8BC39A0BF12307F5C4FDB70C581B23F76B63ACAE1CAA6B7902D52526735488A0EF13C6D9A51BFA4AB3AD8347796524D8EF6A167B5A41825D967E144E5140564251CCACB83E6B486F6B3CA3F7971506026C0B857F689962856DED4010ABD0BE621C3A3960A54E710C375F26375D7014103A4B54330C198AF126116D2276E11715F693877FAD7EF09CADB094AE91E1A1597", 16) + ka.g, _ = new(big.Int).SetString("3FB32C9B73134D0B2E77506660EDBD484CA7B18F21EF205407F4793A1A0BA12510DBC15077BE463FFF4FED4AAC0BB555BE3A6C1B0C6B47B1BC3773BF7E8C6F62901228F8C28CBB18A55AE31341000A650196F931C77A57F2DDF463E5E9EC144B777DE62AAAB8A8628AC376D282D6ED3864E67982428EBC831D14348F6F2F9193B5045AF2767164E1DFC967C1FB3F2E55A4BD1BFFE83B9C80D052B985D182EA0ADB2A3B7313D3FE14C8484B1E052588B9B7D2BBD2DF016199ECD06E1557CD0915B3353BBB64E0EC377FD028370DF92B52C7891428CDC67EB6184B523D1DB246C32F63078490F00EF8D647D148D47954515E2327CFEF98C582664B4C0F6CC41659", 16) + q, _ = new(big.Int).SetString("8CF83642A709A097B447997640129DA299B1A47D1EB3750BA308B0FE64F5FBD3", 16) + } var err error ka.xOurs, err = rand.Int(config.rand(), q) diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go index ec2fede..bd03cb1 100644 --- a/src/ssl/test/runner/runner.go +++ b/src/ssl/test/runner/runner.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "io/ioutil" + "math/big" "net" "os" "os/exec" @@ -160,6 +161,10 @@ type testCase struct { // resumeSession controls whether a second connection should be tested // which attempts to resume the first session. resumeSession bool + // expectResumeRejected, if true, specifies that the attempted + // resumption must be rejected by the client. This is only valid for a + // serverTest. + expectResumeRejected bool // resumeConfig, if not nil, points to a Config to be used on // resumption. Unless newSessionsOnResume is set, // SessionTicketKey, ServerSessionCache, and @@ -196,6 +201,9 @@ type testCase struct { // flags, if not empty, contains a list of command-line flags that will // be passed to the shim program. flags []string + // testTLSUnique, if true, causes the shim to send the tls-unique value + // which will be compared against the expected value. + testTLSUnique bool } var testCases = []testCase{ @@ -1085,6 +1093,49 @@ var testCases = []testCase{ }, expectedCipher: TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, }, + { + protocol: dtls, + name: "SendSplitAlert-Sync", + config: Config{ + Bugs: ProtocolBugs{ + SendSplitAlert: true, + }, + }, + }, + { + protocol: dtls, + name: "SendSplitAlert-Async", + config: Config{ + Bugs: ProtocolBugs{ + SendSplitAlert: true, + }, + }, + flags: []string{"-async"}, + }, + { + protocol: dtls, + name: "PackDTLSHandshake", + config: Config{ + Bugs: ProtocolBugs{ + MaxHandshakeRecordLength: 2, + PackHandshakeFragments: 20, + PackHandshakeRecords: 200, + }, + }, + }, + { + testType: serverTest, + protocol: dtls, + name: "NoRC4-DTLS", + config: Config{ + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA}, + Bugs: ProtocolBugs{ + EnableAllCiphersInDTLS: true, + }, + }, + shouldFail: true, + expectedError: ":NO_SHARED_CIPHER:", + }, } func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, isResume bool) error { @@ -1144,16 +1195,20 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i if isResume && test.expectedResumeVersion != 0 { expectedVersion = test.expectedResumeVersion } - if vers := tlsConn.ConnectionState().Version; expectedVersion != 0 && vers != expectedVersion { + connState := tlsConn.ConnectionState() + if vers := connState.Version; expectedVersion != 0 && vers != expectedVersion { return fmt.Errorf("got version %x, expected %x", vers, expectedVersion) } - if cipher := tlsConn.ConnectionState().CipherSuite; test.expectedCipher != 0 && cipher != test.expectedCipher { + if cipher := connState.CipherSuite; test.expectedCipher != 0 && cipher != test.expectedCipher { return fmt.Errorf("got cipher %x, expected %x", cipher, test.expectedCipher) } + if didResume := connState.DidResume; isResume && didResume == test.expectResumeRejected { + return fmt.Errorf("didResume is %t, but we expected the opposite", didResume) + } if test.expectChannelID { - channelID := tlsConn.ConnectionState().ChannelID + channelID := connState.ChannelID if channelID == nil { return fmt.Errorf("no channel ID negotiated") } @@ -1165,18 +1220,18 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i } if expected := test.expectedNextProto; expected != "" { - if actual := tlsConn.ConnectionState().NegotiatedProtocol; actual != expected { + if actual := connState.NegotiatedProtocol; actual != expected { return fmt.Errorf("next proto mismatch: got %s, wanted %s", actual, expected) } } if test.expectedNextProtoType != 0 { - if (test.expectedNextProtoType == alpn) != tlsConn.ConnectionState().NegotiatedProtocolFromALPN { + if (test.expectedNextProtoType == alpn) != connState.NegotiatedProtocolFromALPN { return fmt.Errorf("next proto type mismatch") } } - if p := tlsConn.ConnectionState().SRTPProtectionProfile; p != test.expectedSRTPProtectionProfile { + if p := connState.SRTPProtectionProfile; p != test.expectedSRTPProtectionProfile { return fmt.Errorf("SRTP profile mismatch: got %d, wanted %d", p, test.expectedSRTPProtectionProfile) } @@ -1194,6 +1249,17 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i } } + if test.testTLSUnique { + var peersValue [12]byte + if _, err := io.ReadFull(tlsConn, peersValue[:]); err != nil { + return err + } + expected := tlsConn.ConnectionState().TLSUnique + if !bytes.Equal(peersValue[:], expected) { + return fmt.Errorf("tls-unique mismatch: peer sent %x, but %x was expected", peersValue[:], expected) + } + } + if test.shimWritesFirst { var buf [5]byte _, err := io.ReadFull(tlsConn, buf[:]) @@ -1321,6 +1387,10 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { panic("Error expected without shouldFail in " + test.name) } + if test.expectResumeRejected && !test.resumeSession { + panic("expectResumeRejected without resumeSession in " + test.name) + } + listener, err := net.ListenTCP("tcp4", &net.TCPAddr{IP: net.IP{127, 0, 0, 1}}) if err != nil { panic(err) @@ -1371,6 +1441,13 @@ func runTest(test *testCase, buildDir string, mallocNumToFail int64) error { flags = append(flags, "-use-export-context") } } + if test.expectResumeRejected { + flags = append(flags, "-expect-session-miss") + } + + if test.testTLSUnique { + flags = append(flags, "-tls-unique") + } flags = append(flags, test.flags...) @@ -1569,6 +1646,14 @@ func isDTLSCipher(suiteName string) bool { return !hasComponent(suiteName, "RC4") } +func bigFromHex(hex string) *big.Int { + ret, ok := new(big.Int).SetString(hex, 16) + if !ok { + panic("failed to parse hex number 0x" + hex) + } + return ret +} + func addCipherSuiteTests() { for _, suite := range testCipherSuites { const psk = "12345" @@ -1667,6 +1752,21 @@ func addCipherSuiteTests() { } } } + + testCases = append(testCases, testCase{ + name: "WeakDH", + config: Config{ + CipherSuites: []uint16{TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, + Bugs: ProtocolBugs{ + // This is a 1023-bit prime number, generated + // with: + // openssl gendh 1023 | openssl asn1parse -i + DHGroupPrime: bigFromHex("518E9B7930CE61C6E445C8360584E5FC78D9137C0FFDC880B495D5338ADF7689951A6821C17A76B3ACB8E0156AEA607B7EC406EBEDBB84D8376EB8FE8F8BA1433488BEE0C3EDDFD3A32DBB9481980A7AF6C96BFCF490A094CFFB2B8192C1BB5510B77B658436E27C2D4D023FE3718222AB0CA1273995B51F6D625A4944D0DD4B"), + }, + }, + shouldFail: true, + expectedError: "BAD_DH_P_LENGTH", + }) } func addBadECDSASignatureTests() { @@ -1866,245 +1966,235 @@ func addExtendedMasterSecretTests() { } } - // 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, - }) + for _, isClient := range []bool{false, true} { + for _, supportedInFirstConnection := range []bool{false, true} { + for _, supportedInResumeConnection := range []bool{false, true} { + boolToWord := func(b bool) string { + if b { + return "Yes" + } + return "No" + } + suffix := boolToWord(supportedInFirstConnection) + "To" + boolToWord(supportedInResumeConnection) + "-" + if isClient { + suffix += "Client" + } else { + suffix += "Server" + } + + supportedConfig := Config{ + Bugs: ProtocolBugs{ + RequireExtendedMasterSecret: true, + }, + } + + noSupportConfig := Config{ + Bugs: ProtocolBugs{ + NoExtendedMasterSecret: true, + }, + } + + test := testCase{ + name: "ExtendedMasterSecret-" + suffix, + resumeSession: true, + } + + if !isClient { + test.testType = serverTest + } + + if supportedInFirstConnection { + test.config = supportedConfig + } else { + test.config = noSupportConfig + } + + if supportedInResumeConnection { + test.resumeConfig = &supportedConfig + } else { + test.resumeConfig = &noSupportConfig + } + + switch suffix { + case "YesToYes-Client", "YesToYes-Server": + // 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. + test.flags = []string{expectEMSFlag} + case "NoToYes-Server": + // If an original connection did not + // contain EMS, but a resumption + // handshake does, then a server should + // not resume the session. + test.expectResumeRejected = true + case "YesToNo-Server": + // Resuming an EMS session without the + // EMS extension should cause the + // server to abort the connection. + test.shouldFail = true + test.expectedError = ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:" + case "NoToYes-Client": + // A client should abort a connection + // where the server resumed a non-EMS + // session but echoed the EMS + // extension. + test.shouldFail = true + test.expectedError = ":RESUMED_NON_EMS_SESSION_WITH_EMS_EXTENSION:" + case "YesToNo-Client": + // A client should abort a connection + // where the server didn't echo EMS + // when the session used it. + test.shouldFail = true + test.expectedError = ":RESUMED_EMS_SESSION_WITHOUT_EMS_EXTENSION:" + } + + testCases = append(testCases, test) + } + } + } } // 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. func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) { - var suffix string - var flags []string - var maxHandshakeRecordLength int - if protocol == dtls { - suffix = "-DTLS" - } - if async { - suffix += "-Async" - flags = append(flags, "-async") - } else { - suffix += "-Sync" - } - if splitHandshake { - suffix += "-SplitHandshakeRecords" - maxHandshakeRecordLength = 1 - } + var tests []testCase // Basic handshake, with resumption. Client and server, // session ID and session ticket. - testCases = append(testCases, testCase{ - protocol: protocol, - name: "Basic-Client" + suffix, - config: Config{ - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, - }, - flags: flags, + tests = append(tests, testCase{ + name: "Basic-Client", resumeSession: true, }) - testCases = append(testCases, testCase{ - protocol: protocol, - name: "Basic-Client-RenewTicket" + suffix, + tests = append(tests, testCase{ + name: "Basic-Client-RenewTicket", config: Config{ Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - RenewTicketOnResume: true, + RenewTicketOnResume: true, }, }, - flags: flags, resumeSession: true, }) - testCases = append(testCases, testCase{ - protocol: protocol, - name: "Basic-Client-NoTicket" + suffix, + tests = append(tests, testCase{ + name: "Basic-Client-NoTicket", config: Config{ SessionTicketsDisabled: true, - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: flags, resumeSession: true, }) - testCases = append(testCases, testCase{ - protocol: protocol, - name: "Basic-Client-Implicit" + suffix, - config: Config{ - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, - }, - flags: append(flags, "-implicit-handshake"), + tests = append(tests, testCase{ + name: "Basic-Client-Implicit", + flags: []string{"-implicit-handshake"}, resumeSession: true, }) - testCases = append(testCases, testCase{ - protocol: protocol, - testType: serverTest, - name: "Basic-Server" + suffix, - config: Config{ - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, - }, - flags: flags, + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server", resumeSession: true, }) - testCases = append(testCases, testCase{ - protocol: protocol, + tests = append(tests, testCase{ testType: serverTest, - name: "Basic-Server-NoTickets" + suffix, + name: "Basic-Server-NoTickets", config: Config{ SessionTicketsDisabled: true, - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: flags, resumeSession: true, }) - testCases = append(testCases, testCase{ - protocol: protocol, - testType: serverTest, - name: "Basic-Server-Implicit" + suffix, - config: Config{ - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, - }, - flags: append(flags, "-implicit-handshake"), + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-Implicit", + flags: []string{"-implicit-handshake"}, resumeSession: true, }) - testCases = append(testCases, testCase{ - protocol: protocol, - testType: serverTest, - name: "Basic-Server-EarlyCallback" + suffix, - config: Config{ - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, - }, - flags: append(flags, "-use-early-callback"), + tests = append(tests, testCase{ + testType: serverTest, + name: "Basic-Server-EarlyCallback", + flags: []string{"-use-early-callback"}, resumeSession: true, }) // TLS client auth. - testCases = append(testCases, testCase{ - protocol: protocol, + tests = append(tests, testCase{ testType: clientTest, - name: "ClientAuth-Client" + suffix, + name: "ClientAuth-Client", config: Config{ ClientAuth: RequireAnyClientCert, - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: append(flags, + flags: []string{ "-cert-file", rsaCertificateFile, - "-key-file", rsaKeyFile), + "-key-file", rsaKeyFile, + }, }) - testCases = append(testCases, testCase{ - protocol: protocol, + tests = append(tests, testCase{ testType: serverTest, - name: "ClientAuth-Server" + suffix, + name: "ClientAuth-Server", config: Config{ Certificates: []Certificate{rsaCertificate}, }, - flags: append(flags, "-require-any-client-certificate"), + flags: []string{"-require-any-client-certificate"}, }) // No session ticket support; server doesn't send NewSessionTicket. - testCases = append(testCases, testCase{ - protocol: protocol, - name: "SessionTicketsDisabled-Client" + suffix, + tests = append(tests, testCase{ + name: "SessionTicketsDisabled-Client", config: Config{ SessionTicketsDisabled: true, - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: flags, }) - testCases = append(testCases, testCase{ - protocol: protocol, + tests = append(tests, testCase{ testType: serverTest, - name: "SessionTicketsDisabled-Server" + suffix, + name: "SessionTicketsDisabled-Server", config: Config{ SessionTicketsDisabled: true, - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: flags, }) // Skip ServerKeyExchange in PSK key exchange if there's no // identity hint. - testCases = append(testCases, testCase{ - protocol: protocol, - name: "EmptyPSKHint-Client" + suffix, + tests = append(tests, testCase{ + name: "EmptyPSKHint-Client", config: Config{ CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA}, PreSharedKey: []byte("secret"), - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: append(flags, "-psk", "secret"), + flags: []string{"-psk", "secret"}, }) - testCases = append(testCases, testCase{ - protocol: protocol, + tests = append(tests, testCase{ testType: serverTest, - name: "EmptyPSKHint-Server" + suffix, + name: "EmptyPSKHint-Server", config: Config{ CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA}, PreSharedKey: []byte("secret"), - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: append(flags, "-psk", "secret"), + flags: []string{"-psk", "secret"}, }) if protocol == tls { + tests = append(tests, testCase{ + name: "Renegotiate-Client", + renegotiate: true, + }) // NPN on client and server; results in post-handshake message. - testCases = append(testCases, testCase{ - protocol: protocol, - name: "NPN-Client" + suffix, + tests = append(tests, testCase{ + name: "NPN-Client", config: Config{ NextProtos: []string{"foo"}, - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: append(flags, "-select-next-proto", "foo"), + flags: []string{"-select-next-proto", "foo"}, expectedNextProto: "foo", expectedNextProtoType: npn, }) - testCases = append(testCases, testCase{ - protocol: protocol, + tests = append(tests, testCase{ testType: serverTest, - name: "NPN-Server" + suffix, + name: "NPN-Server", config: Config{ NextProtos: []string{"bar"}, - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: append(flags, + flags: []string{ "-advertise-npn", "\x03foo\x03bar\x03baz", - "-expect-next-proto", "bar"), + "-expect-next-proto", "bar", + }, expectedNextProto: "bar", expectedNextProtoType: npn, }) @@ -2112,146 +2202,148 @@ func addStateMachineCoverageTests(async, splitHandshake bool, protocol protocol) // TODO(davidben): Add tests for when False Start doesn't trigger. // Client does False Start and negotiates NPN. - testCases = append(testCases, testCase{ - protocol: protocol, - name: "FalseStart" + suffix, + tests = append(tests, testCase{ + name: "FalseStart", config: Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, NextProtos: []string{"foo"}, Bugs: ProtocolBugs{ - ExpectFalseStart: true, - MaxHandshakeRecordLength: maxHandshakeRecordLength, + ExpectFalseStart: true, }, }, - flags: append(flags, + flags: []string{ "-false-start", - "-select-next-proto", "foo"), + "-select-next-proto", "foo", + }, shimWritesFirst: true, resumeSession: true, }) // Client does False Start and negotiates ALPN. - testCases = append(testCases, testCase{ - protocol: protocol, - name: "FalseStart-ALPN" + suffix, + tests = append(tests, testCase{ + name: "FalseStart-ALPN", config: Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, NextProtos: []string{"foo"}, Bugs: ProtocolBugs{ - ExpectFalseStart: true, - MaxHandshakeRecordLength: maxHandshakeRecordLength, + ExpectFalseStart: true, }, }, - flags: append(flags, + flags: []string{ "-false-start", - "-advertise-alpn", "\x03foo"), + "-advertise-alpn", "\x03foo", + }, shimWritesFirst: true, resumeSession: true, }) // Client does False Start but doesn't explicitly call // SSL_connect. - testCases = append(testCases, testCase{ - protocol: protocol, - name: "FalseStart-Implicit" + suffix, + tests = append(tests, testCase{ + name: "FalseStart-Implicit", config: Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, NextProtos: []string{"foo"}, - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: append(flags, + flags: []string{ "-implicit-handshake", "-false-start", - "-advertise-alpn", "\x03foo"), + "-advertise-alpn", "\x03foo", + }, }) // False Start without session tickets. - testCases = append(testCases, testCase{ - name: "FalseStart-SessionTicketsDisabled" + suffix, + tests = append(tests, testCase{ + name: "FalseStart-SessionTicketsDisabled", config: Config{ CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, NextProtos: []string{"foo"}, SessionTicketsDisabled: true, Bugs: ProtocolBugs{ - ExpectFalseStart: true, - MaxHandshakeRecordLength: maxHandshakeRecordLength, + ExpectFalseStart: true, }, }, - flags: append(flags, + flags: []string{ "-false-start", "-select-next-proto", "foo", - ), + }, shimWritesFirst: true, }) // Server parses a V2ClientHello. - testCases = append(testCases, testCase{ - protocol: protocol, + tests = append(tests, testCase{ testType: serverTest, - name: "SendV2ClientHello" + suffix, + name: "SendV2ClientHello", 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, + SendV2ClientHello: true, }, }, - flags: flags, }) // Client sends a Channel ID. - testCases = append(testCases, testCase{ - protocol: protocol, - name: "ChannelID-Client" + suffix, + tests = append(tests, testCase{ + name: "ChannelID-Client", config: Config{ RequestChannelID: true, - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: append(flags, - "-send-channel-id", channelIDKeyFile, - ), + flags: []string{"-send-channel-id", channelIDKeyFile}, resumeSession: true, expectChannelID: true, }) // Server accepts a Channel ID. - testCases = append(testCases, testCase{ - protocol: protocol, + tests = append(tests, testCase{ testType: serverTest, - name: "ChannelID-Server" + suffix, + name: "ChannelID-Server", config: Config{ ChannelID: channelIDKey, - Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - }, }, - flags: append(flags, + flags: []string{ "-expect-channel-id", base64.StdEncoding.EncodeToString(channelIDBytes), - ), + }, resumeSession: true, expectChannelID: true, }) } else { - testCases = append(testCases, testCase{ - protocol: protocol, - name: "SkipHelloVerifyRequest" + suffix, + tests = append(tests, testCase{ + name: "SkipHelloVerifyRequest", config: Config{ Bugs: ProtocolBugs{ - MaxHandshakeRecordLength: maxHandshakeRecordLength, - SkipHelloVerifyRequest: true, + SkipHelloVerifyRequest: true, }, }, - flags: flags, }) } + + var suffix string + var flags []string + var maxHandshakeRecordLength int + if protocol == dtls { + suffix = "-DTLS" + } + if async { + suffix += "-Async" + flags = append(flags, "-async") + } else { + suffix += "-Sync" + } + if splitHandshake { + suffix += "-SplitHandshakeRecords" + maxHandshakeRecordLength = 1 + } + for _, test := range tests { + test.protocol = protocol + test.name += suffix + test.config.Bugs.MaxHandshakeRecordLength = maxHandshakeRecordLength + test.flags = append(test.flags, flags...) + testCases = append(testCases, test) + } } func addDDoSCallbackTests() { @@ -2637,8 +2729,8 @@ func addExtensionTests() { CorruptTicket: true, }, }, - resumeSession: true, - flags: []string{"-expect-session-miss"}, + resumeSession: true, + expectResumeRejected: true, }) // Resume with an oversized session id. testCases = append(testCases, testCase{ @@ -2799,7 +2891,6 @@ func addResumptionVersionTests() { testCases = append(testCases, testCase{ protocol: protocol, name: "Resume-Client-NoResume" + suffix, - flags: []string{"-expect-session-miss"}, resumeSession: true, config: Config{ MaxVersion: sessionVers.version, @@ -2811,24 +2902,21 @@ func addResumptionVersionTests() { CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, }, newSessionsOnResume: true, + expectResumeRejected: true, expectedResumeVersion: resumeVers.version, }) - 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, + expectedVersion: sessionVers.version, + expectResumeRejected: sessionVers.version != resumeVers.version, resumeConfig: &Config{ MaxVersion: resumeVers.version, CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, @@ -2857,57 +2945,50 @@ func addResumptionVersionTests() { } func addRenegotiationTests() { + // Servers cannot renegotiate. testCases = append(testCases, testCase{ - testType: serverTest, - name: "Renegotiate-Server", - flags: []string{"-renegotiate"}, - shimWritesFirst: true, + testType: serverTest, + name: "Renegotiate-Server-Forbidden", + renegotiate: true, + flags: []string{"-reject-peer-renegotiations"}, + shouldFail: true, + expectedError: ":NO_RENEGOTIATION:", + expectedLocalError: "remote error: no renegotiation", }) + // TODO(agl): test the renegotiation info SCSV. testCases = append(testCases, testCase{ - testType: serverTest, - name: "Renegotiate-Server-Full", + name: "Renegotiate-Client", config: Config{ Bugs: ProtocolBugs{ - NeverResumeOnRenego: true, + FailIfResumeOnRenego: true, }, }, - flags: []string{"-renegotiate"}, - shimWritesFirst: true, + renegotiate: true, }) testCases = append(testCases, testCase{ - testType: serverTest, - name: "Renegotiate-Server-EmptyExt", + name: "Renegotiate-Client-EmptyExt", + renegotiate: true, config: Config{ Bugs: ProtocolBugs{ EmptyRenegotiationInfo: true, }, }, - flags: []string{"-renegotiate"}, - shimWritesFirst: true, - shouldFail: true, - expectedError: ":RENEGOTIATION_MISMATCH:", + shouldFail: true, + expectedError: ":RENEGOTIATION_MISMATCH:", }) testCases = append(testCases, testCase{ - testType: serverTest, - name: "Renegotiate-Server-BadExt", + name: "Renegotiate-Client-BadExt", + renegotiate: true, config: Config{ Bugs: ProtocolBugs{ BadRenegotiationInfo: true, }, }, - flags: []string{"-renegotiate"}, - shimWritesFirst: true, - shouldFail: true, - expectedError: ":RENEGOTIATION_MISMATCH:", - }) - testCases = append(testCases, testCase{ - testType: serverTest, - name: "Renegotiate-Server-ClientInitiated", - renegotiate: true, + shouldFail: true, + expectedError: ":RENEGOTIATION_MISMATCH:", }) testCases = append(testCases, testCase{ - testType: serverTest, - name: "Renegotiate-Server-ClientInitiated-NoExt", + name: "Renegotiate-Client-NoExt", renegotiate: true, config: Config{ Bugs: ProtocolBugs{ @@ -2916,75 +2997,16 @@ func addRenegotiationTests() { }, shouldFail: true, expectedError: ":UNSAFE_LEGACY_RENEGOTIATION_DISABLED:", + flags: []string{"-no-legacy-server-connect"}, }) testCases = append(testCases, testCase{ - testType: serverTest, - name: "Renegotiate-Server-ClientInitiated-NoExt-Allowed", + name: "Renegotiate-Client-NoExt-Allowed", renegotiate: true, config: Config{ Bugs: ProtocolBugs{ NoRenegotiationInfo: true, }, }, - flags: []string{"-allow-unsafe-legacy-renegotiation"}, - }) - testCases = append(testCases, testCase{ - testType: serverTest, - name: "Renegotiate-Server-ClientInitiated-Forbidden", - renegotiate: true, - flags: []string{"-reject-peer-renegotiations"}, - shouldFail: true, - expectedError: ":NO_RENEGOTIATION:", - expectedLocalError: "remote error: no renegotiation", - }) - // Regression test for CVE-2015-0291. - testCases = append(testCases, testCase{ - testType: serverTest, - name: "Renegotiate-Server-NoSignatureAlgorithms", - config: Config{ - Bugs: ProtocolBugs{ - NeverResumeOnRenego: true, - NoSignatureAlgorithmsOnRenego: true, - }, - }, - flags: []string{"-renegotiate"}, - shimWritesFirst: true, - }) - // TODO(agl): test the renegotiation info SCSV. - testCases = append(testCases, testCase{ - name: "Renegotiate-Client", - renegotiate: true, - }) - testCases = append(testCases, testCase{ - name: "Renegotiate-Client-Full", - config: Config{ - Bugs: ProtocolBugs{ - NeverResumeOnRenego: true, - }, - }, - 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", @@ -3365,6 +3387,59 @@ func addExportKeyingMaterialTests() { }) } +func addTLSUniqueTests() { + for _, isClient := range []bool{false, true} { + for _, isResumption := range []bool{false, true} { + for _, hasEMS := range []bool{false, true} { + var suffix string + if isResumption { + suffix = "Resume-" + } else { + suffix = "Full-" + } + + if hasEMS { + suffix += "EMS-" + } else { + suffix += "NoEMS-" + } + + if isClient { + suffix += "Client" + } else { + suffix += "Server" + } + + test := testCase{ + name: "TLSUnique-" + suffix, + testTLSUnique: true, + config: Config{ + Bugs: ProtocolBugs{ + NoExtendedMasterSecret: !hasEMS, + }, + }, + } + + if isResumption { + test.resumeSession = true + test.resumeConfig = &Config{ + Bugs: ProtocolBugs{ + NoExtendedMasterSecret: !hasEMS, + }, + } + } + + if isResumption && !hasEMS { + test.shouldFail = true + test.expectedError = "failed to get tls-unique" + } + + testCases = append(testCases, test) + } + } + } +} + func worker(statusChan chan statusMsg, c chan *testCase, buildDir string, wg *sync.WaitGroup) { defer wg.Done() @@ -3463,6 +3538,7 @@ func main() { addFastRadioPaddingTests() addDTLSRetransmitTests() addExportKeyingMaterialTests() + addTLSUniqueTests() for _, async := range []bool{false, true} { for _, splitHandshake := range []bool{false, true} { for _, protocol := range []protocol{tls, dtls} { |