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>
This commit is contained in:
committed by
Adam Langley
parent
ce5be4bd5c
commit
ca6c82643a
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:]
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user