Browse Source

Add DTLS-SRTP tests.

Just the negotiation portion as everything else is external. This feature is
used in WebRTC.

Change-Id: Iccc3983ea99e7d054b59010182f9a56a8099e116
Reviewed-on: https://boringssl-review.googlesource.com/2310
Reviewed-by: Adam Langley <agl@google.com>
kris/onging/CECPQ3_patch15
David Benjamin 10 years ago
committed by Adam Langley
parent
commit
ca6c82643a
9 changed files with 297 additions and 56 deletions
  1. +6
    -0
      ssl/test/bssl_shim.cc
  2. +21
    -0
      ssl/test/runner/common.go
  3. +3
    -0
      ssl/test/runner/conn.go
  4. +36
    -15
      ssl/test/runner/handshake_client.go
  5. +128
    -40
      ssl/test/runner/handshake_messages.go
  6. +17
    -0
      ssl/test/runner/handshake_server.go
  7. +84
    -1
      ssl/test/runner/runner.go
  8. +1
    -0
      ssl/test/test_config.cc
  9. +1
    -0
      ssl/test/test_config.h

+ 6
- 0
ssl/test/bssl_shim.cc View File

@@ -425,6 +425,12 @@ static int do_exchange(SSL_SESSION **out_session,
return 1;
}
}
if (!config->srtp_profiles.empty()) {
if (!SSL_set_srtp_profiles(ssl, config->srtp_profiles.c_str())) {
BIO_print_errors_fp(stdout);
return 1;
}
}

BIO *bio = BIO_new_fd(fd, 1 /* take ownership */);
if (bio == NULL) {


+ 21
- 0
ssl/test/runner/common.go View File

@@ -77,6 +77,7 @@ const (
extensionSupportedCurves uint16 = 10
extensionSupportedPoints uint16 = 11
extensionSignatureAlgorithms uint16 = 13
extensionUseSRTP uint16 = 14
extensionALPN uint16 = 16
extensionExtendedMasterSecret uint16 = 23
extensionSessionTicket uint16 = 35
@@ -161,6 +162,12 @@ var supportedClientCertSignatureAlgorithms = []signatureAndHash{
{signatureECDSA, hashSHA256},
}

// SRTP protection profiles (See RFC 5764, section 4.1.2)
const (
SRTP_AES128_CM_HMAC_SHA1_80 uint16 = 0x0001
SRTP_AES128_CM_HMAC_SHA1_32 = 0x0002
)

// ConnectionState records basic TLS details about the connection.
type ConnectionState struct {
Version uint16 // TLS version used by the connection (e.g. VersionTLS12)
@@ -174,6 +181,7 @@ type ConnectionState struct {
PeerCertificates []*x509.Certificate // certificate chain presented by remote peer
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
}

// ClientAuthType declares the policy the server will follow for
@@ -334,6 +342,10 @@ type Config struct {
// with the PSK cipher suites.
PreSharedKeyIdentity string

// SRTPProtectionProfiles, if not nil, is the list of SRTP
// protection profiles to offer in DTLS-SRTP.
SRTPProtectionProfiles []uint16

// Bugs specifies optional misbehaviour to be used for testing other
// implementations.
Bugs ProtocolBugs
@@ -520,6 +532,15 @@ type ProtocolBugs struct {
// RSAServerKeyExchange, if true, causes the server to send a
// ServerKeyExchange message in the plain RSA key exchange.
RSAServerKeyExchange bool

// SRTPMasterKeyIdentifer, if not empty, is the SRTP MKI value that the
// client offers when negotiating SRTP. MKI support is still missing so
// the peer must still send none.
SRTPMasterKeyIdentifer string

// SendSRTPProtectionProfile, if non-zero, is the SRTP profile that the
// server sends in the ServerHello instead of the negotiated one.
SendSRTPProtectionProfile uint16
}

func (c *Config) serverInit() {


+ 3
- 0
ssl/test/runner/conn.go View File

@@ -56,6 +56,8 @@ type Conn struct {

channelID *ecdsa.PublicKey

srtpProtectionProfile uint16

// input/output
in, out halfConn // in.Mutex < out.Mutex
rawInput *block // raw input, right off the wire
@@ -1184,6 +1186,7 @@ func (c *Conn) ConnectionState() ConnectionState {
state.VerifiedChains = c.verifiedChains
state.ServerName = c.serverName
state.ChannelID = c.channelID
state.SRTPProtectionProfile = c.srtpProtectionProfile
}

return state


+ 36
- 15
ssl/test/runner/handshake_client.go View File

@@ -56,21 +56,23 @@ func (c *Conn) clientHandshake() error {
}

hello := &clientHelloMsg{
isDTLS: c.isDTLS,
vers: c.config.maxVersion(),
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
ocspStapling: true,
serverName: c.config.ServerName,
supportedCurves: c.config.curvePreferences(),
supportedPoints: []uint8{pointFormatUncompressed},
nextProtoNeg: len(c.config.NextProtos) > 0,
secureRenegotiation: []byte{},
alpnProtocols: c.config.NextProtos,
duplicateExtension: c.config.Bugs.DuplicateExtension,
channelIDSupported: c.config.ChannelID != nil,
npnLast: c.config.Bugs.SwapNPNAndALPN,
extendedMasterSecret: c.config.maxVersion() >= VersionTLS10,
isDTLS: c.isDTLS,
vers: c.config.maxVersion(),
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
ocspStapling: true,
serverName: c.config.ServerName,
supportedCurves: c.config.curvePreferences(),
supportedPoints: []uint8{pointFormatUncompressed},
nextProtoNeg: len(c.config.NextProtos) > 0,
secureRenegotiation: []byte{},
alpnProtocols: c.config.NextProtos,
duplicateExtension: c.config.Bugs.DuplicateExtension,
channelIDSupported: c.config.ChannelID != nil,
npnLast: c.config.Bugs.SwapNPNAndALPN,
extendedMasterSecret: c.config.maxVersion() >= VersionTLS10,
srtpProtectionProfiles: c.config.SRTPProtectionProfiles,
srtpMasterKeyIdentifier: c.config.Bugs.SRTPMasterKeyIdentifer,
}

if c.config.Bugs.SendClientVersion != 0 {
@@ -666,6 +668,25 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
return false, errors.New("server advertised unrequested Channel ID extension")
}

if hs.serverHello.srtpProtectionProfile != 0 {
if hs.serverHello.srtpMasterKeyIdentifier != "" {
return false, errors.New("tls: server selected SRTP MKI value")
}

found := false
for _, p := range c.config.SRTPProtectionProfiles {
if p == hs.serverHello.srtpProtectionProfile {
found = true
break
}
}
if !found {
return false, errors.New("tls: server advertised unsupported SRTP profile")
}

c.srtpProtectionProfile = hs.serverHello.srtpProtectionProfile
}

if hs.serverResumedSession() {
// Restore masterSecret and peerCerts from previous state
hs.masterSecret = hs.session.masterSecret


+ 128
- 40
ssl/test/runner/handshake_messages.go View File

@@ -7,28 +7,30 @@ package main
import "bytes"

type clientHelloMsg struct {
raw []byte
isDTLS bool
vers uint16
random []byte
sessionId []byte
cookie []byte
cipherSuites []uint16
compressionMethods []uint8
nextProtoNeg bool
serverName string
ocspStapling bool
supportedCurves []CurveID
supportedPoints []uint8
ticketSupported bool
sessionTicket []uint8
signatureAndHashes []signatureAndHash
secureRenegotiation []byte
alpnProtocols []string
duplicateExtension bool
channelIDSupported bool
npnLast bool
extendedMasterSecret bool
raw []byte
isDTLS bool
vers uint16
random []byte
sessionId []byte
cookie []byte
cipherSuites []uint16
compressionMethods []uint8
nextProtoNeg bool
serverName string
ocspStapling bool
supportedCurves []CurveID
supportedPoints []uint8
ticketSupported bool
sessionTicket []uint8
signatureAndHashes []signatureAndHash
secureRenegotiation []byte
alpnProtocols []string
duplicateExtension bool
channelIDSupported bool
npnLast bool
extendedMasterSecret bool
srtpProtectionProfiles []uint16
srtpMasterKeyIdentifier string
}

func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -59,7 +61,9 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
m.duplicateExtension == m1.duplicateExtension &&
m.channelIDSupported == m1.channelIDSupported &&
m.npnLast == m1.npnLast &&
m.extendedMasterSecret == m1.extendedMasterSecret
m.extendedMasterSecret == m1.extendedMasterSecret &&
eqUint16s(m.srtpProtectionProfiles, m1.srtpProtectionProfiles) &&
m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier
}

func (m *clientHelloMsg) marshal() []byte {
@@ -124,6 +128,11 @@ func (m *clientHelloMsg) marshal() []byte {
if m.extendedMasterSecret {
numExtensions++
}
if len(m.srtpProtectionProfiles) > 0 {
extensionsLength += 2 + 2*len(m.srtpProtectionProfiles)
extensionsLength += 1 + len(m.srtpMasterKeyIdentifier)
numExtensions++
}
if numExtensions > 0 {
extensionsLength += 4 * numExtensions
length += 2 + extensionsLength
@@ -334,6 +343,29 @@ func (m *clientHelloMsg) marshal() []byte {
z[1] = byte(extensionExtendedMasterSecret & 0xff)
z = z[4:]
}
if len(m.srtpProtectionProfiles) > 0 {
z[0] = byte(extensionUseSRTP >> 8)
z[1] = byte(extensionUseSRTP & 0xff)

profilesLen := 2 * len(m.srtpProtectionProfiles)
mkiLen := len(m.srtpMasterKeyIdentifier)
l := 2 + profilesLen + 1 + mkiLen
z[2] = byte(l >> 8)
z[3] = byte(l & 0xff)

z[4] = byte(profilesLen >> 8)
z[5] = byte(profilesLen & 0xff)
z = z[6:]
for _, p := range m.srtpProtectionProfiles {
z[0] = byte(p >> 8)
z[1] = byte(p & 0xff)
z = z[2:]
}

z[0] = byte(mkiLen)
copy(z[1:], []byte(m.srtpMasterKeyIdentifier))
z = z[1+mkiLen:]
}

m.raw = x

@@ -538,6 +570,25 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
return false
}
m.extendedMasterSecret = true
case extensionUseSRTP:
if length < 2 {
return false
}
l := int(data[0])<<8 | int(data[1])
if l > length-2 || l%2 != 0 {
return false
}
n := l / 2
m.srtpProtectionProfiles = make([]uint16, n)
d := data[2:length]
for i := 0; i < n; i++ {
m.srtpProtectionProfiles[i] = uint16(d[0])<<8 | uint16(d[1])
d = d[2:]
}
if len(d) < 1 || int(d[0]) != len(d)-1 {
return false
}
m.srtpMasterKeyIdentifier = string(d[1:])
}
data = data[length:]
}
@@ -546,22 +597,24 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
}

type serverHelloMsg struct {
raw []byte
isDTLS bool
vers uint16
random []byte
sessionId []byte
cipherSuite uint16
compressionMethod uint8
nextProtoNeg bool
nextProtos []string
ocspStapling bool
ticketSupported bool
secureRenegotiation []byte
alpnProtocol string
duplicateExtension bool
channelIDRequested bool
extendedMasterSecret bool
raw []byte
isDTLS bool
vers uint16
random []byte
sessionId []byte
cipherSuite uint16
compressionMethod uint8
nextProtoNeg bool
nextProtos []string
ocspStapling bool
ticketSupported bool
secureRenegotiation []byte
alpnProtocol string
duplicateExtension bool
channelIDRequested bool
extendedMasterSecret bool
srtpProtectionProfile uint16
srtpMasterKeyIdentifier string
}

func (m *serverHelloMsg) equal(i interface{}) bool {
@@ -586,7 +639,9 @@ func (m *serverHelloMsg) equal(i interface{}) bool {
m.alpnProtocol == m1.alpnProtocol &&
m.duplicateExtension == m1.duplicateExtension &&
m.channelIDRequested == m1.channelIDRequested &&
m.extendedMasterSecret == m1.extendedMasterSecret
m.extendedMasterSecret == m1.extendedMasterSecret &&
m.srtpProtectionProfile == m1.srtpProtectionProfile &&
m.srtpMasterKeyIdentifier == m1.srtpMasterKeyIdentifier
}

func (m *serverHelloMsg) marshal() []byte {
@@ -633,6 +688,10 @@ func (m *serverHelloMsg) marshal() []byte {
if m.extendedMasterSecret {
numExtensions++
}
if m.srtpProtectionProfile != 0 {
extensionsLength += 2 + 2 + 1 + len(m.srtpMasterKeyIdentifier)
numExtensions++
}

if numExtensions > 0 {
extensionsLength += 4 * numExtensions
@@ -734,6 +793,21 @@ func (m *serverHelloMsg) marshal() []byte {
z[1] = byte(extensionExtendedMasterSecret & 0xff)
z = z[4:]
}
if m.srtpProtectionProfile != 0 {
z[0] = byte(extensionUseSRTP >> 8)
z[1] = byte(extensionUseSRTP & 0xff)
l := 2 + 2 + 1 + len(m.srtpMasterKeyIdentifier)
z[2] = byte(l >> 8)
z[3] = byte(l & 0xff)
z[4] = 0
z[5] = 2
z[6] = byte(m.srtpProtectionProfile >> 8)
z[7] = byte(m.srtpProtectionProfile & 0xff)
l = len(m.srtpMasterKeyIdentifier)
z[8] = byte(l)
copy(z[9:], []byte(m.srtpMasterKeyIdentifier))
z = z[9+l:]
}

m.raw = x

@@ -846,6 +920,20 @@ func (m *serverHelloMsg) unmarshal(data []byte) bool {
return false
}
m.extendedMasterSecret = true
case extensionUseSRTP:
if length < 2+2+1 {
return false
}
if data[0] != 0 || data[1] != 2 {
return false
}
m.srtpProtectionProfile = uint16(data[2])<<8 | uint16(data[3])
d := data[4:length]
l := int(d[0])
if l != len(d)-1 {
return false
}
m.srtpMasterKeyIdentifier = string(d[1:])
}
data = data[length:]
}


+ 17
- 0
ssl/test/runner/handshake_server.go View File

@@ -270,6 +270,23 @@ Curves:
hs.hello.channelIDRequested = true
}

if hs.clientHello.srtpProtectionProfiles != nil {
SRTPLoop:
for _, p1 := range c.config.SRTPProtectionProfiles {
for _, p2 := range hs.clientHello.srtpProtectionProfiles {
if p1 == p2 {
hs.hello.srtpProtectionProfile = p1
c.srtpProtectionProfile = p1
break SRTPLoop
}
}
}
}

if c.config.Bugs.SendSRTPProtectionProfile != 0 {
hs.hello.srtpProtectionProfile = c.config.Bugs.SendSRTPProtectionProfile
}

_, hs.ecdsaOk = hs.cert.PrivateKey.(*ecdsa.PrivateKey)

if hs.checkForResumption() {


+ 84
- 1
ssl/test/runner/runner.go View File

@@ -129,6 +129,9 @@ type testCase struct {
// expectedNextProtoType, if non-zero, is the expected next
// protocol negotiation mechanism.
expectedNextProtoType int
// expectedSRTPProtectionProfile is the DTLS-SRTP profile that
// should be negotiated. If zero, none should be negotiated.
expectedSRTPProtectionProfile uint16
// messageLen is the length, in bytes, of the test message that will be
// sent.
messageLen int
@@ -357,7 +360,7 @@ var testCases = []testCase{
name: "FragmentAlert",
config: Config{
Bugs: ProtocolBugs{
FragmentAlert: true,
FragmentAlert: true,
SendSpuriousAlert: true,
},
},
@@ -589,6 +592,10 @@ func doExchange(test *testCase, config *Config, conn net.Conn, messageLen int, i
}
}

if p := tlsConn.ConnectionState().SRTPProtectionProfile; p != test.expectedSRTPProtectionProfile {
return fmt.Errorf("SRTP profile mismatch: got %d, wanted %d", p, test.expectedSRTPProtectionProfile)
}

if test.shimWritesFirst {
var buf [5]byte
_, err := io.ReadFull(tlsConn, buf[:])
@@ -1741,6 +1748,82 @@ func addExtensionTests() {
shouldFail: true,
expectedError: ":DECODE_ERROR:",
})
// 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:",
})
}

func addResumptionVersionTests() {


+ 1
- 0
ssl/test/test_config.cc View File

@@ -81,6 +81,7 @@ const StringFlag kStringFlags[] = {
{ "-select-alpn", &TestConfig::select_alpn },
{ "-psk", &TestConfig::psk },
{ "-psk-identity", &TestConfig::psk_identity },
{ "-srtp-profiles", &TestConfig::srtp_profiles },
};

const size_t kNumStringFlags = sizeof(kStringFlags) / sizeof(kStringFlags[0]);


+ 1
- 0
ssl/test/test_config.h View File

@@ -58,6 +58,7 @@ struct TestConfig {
std::string psk_identity;
bool renegotiate;
bool allow_unsafe_legacy_renegotiation;
std::string srtp_profiles;
};

bool ParseConfig(int argc, char **argv, TestConfig *out_config);


Loading…
Cancel
Save