Skip to content

Commit

Permalink
Refactor handshake state machine
Browse files Browse the repository at this point in the history
Fully implement retransmition flow specified in RFC6347 Section 4.2.4.
- parse incoming handshake message sequentially instead of
  calling type switch for each message
- fix condition to end client handshake
- return read error transparently from underlying connection
- support packet contains both handshake and encrypted message
  (OpenSSL send such packet.)
- verify CipherSuite on changeCipherSpec and flight cache epoch number
  • Loading branch information
at-wat committed Mar 5, 2020
1 parent 6bf60d2 commit 863572a
Show file tree
Hide file tree
Showing 28 changed files with 2,417 additions and 1,705 deletions.
22 changes: 0 additions & 22 deletions atomic_error.go

This file was deleted.

67 changes: 67 additions & 0 deletions certificate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package dtls

import (
"crypto/tls"
"crypto/x509"
"strings"
)

func (c *handshakeConfig) getCertificate(serverName string) (*tls.Certificate, error) {
c.mu.Lock()
defer c.mu.Unlock()

if c.nameToCertificate == nil {
nameToCertificate := make(map[string]*tls.Certificate)
for i := range c.localCertificates {
cert := &c.localCertificates[i]
x509Cert := cert.Leaf
if x509Cert == nil {
var parseErr error
x509Cert, parseErr = x509.ParseCertificate(cert.Certificate[0])
if parseErr != nil {
continue
}
}
if len(x509Cert.Subject.CommonName) > 0 {
nameToCertificate[strings.ToLower(x509Cert.Subject.CommonName)] = cert
}
for _, san := range x509Cert.DNSNames {
nameToCertificate[strings.ToLower(san)] = cert
}
}
c.nameToCertificate = nameToCertificate
}

if len(c.localCertificates) == 0 {
return nil, errNoCertificates
}

if len(c.localCertificates) == 1 {
// There's only one choice, so no point doing any work.
return &c.localCertificates[0], nil
}

if len(serverName) == 0 {
return &c.localCertificates[0], nil
}

name := strings.TrimRight(strings.ToLower(serverName), ".")

if cert, ok := c.nameToCertificate[name]; ok {
return cert, nil
}

// try replacing labels in the name with wildcards until we get a
// match.
labels := strings.Split(name, ".")
for i := range labels {
labels[i] = "*"
candidate := strings.Join(labels, ".")
if cert, ok := c.nameToCertificate[candidate]; ok {
return cert, nil
}
}

// If nothing matches, return the first certificate.
return &c.localCertificates[0], nil
}
79 changes: 79 additions & 0 deletions certificate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package dtls

import (
"crypto/tls"
"reflect"
"testing"

"github.com/pion/dtls/v2/pkg/crypto/selfsign"
)

func TestGetCertificate(t *testing.T) {
certificateWildcard, err := selfsign.GenerateSelfSignedWithDNS("*.test.test")
if err != nil {
t.Fatal(err)
}

certificateTest, err := selfsign.GenerateSelfSignedWithDNS("test.test", "www.test.test", "pop.test.test")
if err != nil {
t.Fatal(err)
}

certificateRandom, err := selfsign.GenerateSelfSigned()
if err != nil {
t.Fatal(err)
}

cfg := &handshakeConfig{
localCertificates: []tls.Certificate{
certificateRandom,
certificateTest,
certificateWildcard,
},
}

testCases := []struct {
desc string
serverName string
expectedCertificate tls.Certificate
}{
{
desc: "Simple match in CN",
serverName: "test.test",
expectedCertificate: certificateTest,
},
{
desc: "Simple match in SANs",
serverName: "www.test.test",
expectedCertificate: certificateTest,
},

{
desc: "Wildcard match",
serverName: "foo.test.test",
expectedCertificate: certificateWildcard,
},
{
desc: "No match return first",
serverName: "foo.bar",
expectedCertificate: certificateRandom,
},
}

for _, test := range testCases {
test := test

t.Run(test.desc, func(t *testing.T) {
t.Parallel()

cert, err := cfg.getCertificate(test.serverName)
if err != nil {
t.Fatal(err)
}

if !reflect.DeepEqual(cert.Leaf, test.expectedCertificate.Leaf) {
t.Fatalf("Certificate does not match: expected(%v) actual(%v)", test.expectedCertificate.Leaf, cert.Leaf)
}
})
}
}

0 comments on commit 863572a

Please sign in to comment.