Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/peer certificates #115

Merged
merged 4 commits into from
Apr 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package coap
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"net"
Expand Down Expand Up @@ -322,6 +323,11 @@ func (co *ClientConn) RemoteAddr() net.Addr {
return co.commander.RemoteAddr()
}

// PeerCertificates implements the networkSession.PeerCertificates method.
func (co *ClientConn) PeerCertificates() []*x509.Certificate {
return co.commander.PeerCertificates()
}

func (co *ClientConn) Exchange(m Message) (Message, error) {
return co.commander.ExchangeWithContext(context.Background(), m)
}
Expand Down
6 changes: 6 additions & 0 deletions clientcommander.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package coap
import (
"bytes"
"context"
"crypto/x509"
"io"
"io/ioutil"
"net"
Expand Down Expand Up @@ -88,6 +89,11 @@ func (cc *ClientCommander) RemoteAddr() net.Addr {
return cc.networkSession.RemoteAddr()
}

// PeerCertificates implements the networkSession.PeerCertificates method.
func (cc *ClientCommander) PeerCertificates() []*x509.Certificate {
return cc.networkSession.PeerCertificates()
}

// Equal compare two ClientCommanders
func (cc *ClientCommander) Equal(cc1 *ClientCommander) bool {
return cc.RemoteAddr().String() == cc1.RemoteAddr().String() && cc.LocalAddr().String() == cc1.LocalAddr().String()
Expand Down
68 changes: 66 additions & 2 deletions dtls_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package coap

import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
Expand All @@ -11,9 +14,11 @@ import (
"fmt"
"math/big"
"os"
"sync"
"testing"
"time"

coapNet "github.com/go-ocf/go-coap/net"
dtls "github.com/pion/dtls/v2"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -77,8 +82,8 @@ func pemBlockForKey(priv interface{}) *pem.Block {
}
}

func generateRSACertsPEM(t *testing.T) ([]byte, []byte) {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
func generateCertsPEM(t *testing.T, generateKeyFunc func() (crypto.PrivateKey, error)) ([]byte, []byte) {
priv, err := generateKeyFunc()
if err != nil {
require.NoError(t, err)
}
Expand All @@ -99,6 +104,18 @@ func generateRSACertsPEM(t *testing.T) ([]byte, []byte) {
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), pem.EncodeToMemory(pemBlockForKey(priv))
}

func generateRSACertsPEM(t *testing.T) ([]byte, []byte) {
return generateCertsPEM(t, func() (crypto.PrivateKey, error) {
return rsa.GenerateKey(rand.Reader, 2048)
})
}

func generateECDSACertsPEM(t *testing.T) ([]byte, []byte) {
return generateCertsPEM(t, func() (crypto.PrivateKey, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
})
}

func TestRSACerts(t *testing.T) {
cert, key := generateRSACertsPEM(t)
c, err := tls.X509KeyPair(cert, key)
Expand All @@ -120,3 +137,50 @@ func TestRSACerts(t *testing.T) {
err = s.ListenAndServe()
require.Error(t, err)
}

func TestECDSACerts_PeerCertificate(t *testing.T) {
cert, key := generateECDSACertsPEM(t)
c, err := tls.X509KeyPair(cert, key)
require.NoError(t, err)

config := dtls.Config{
Certificates: []tls.Certificate{c},
CipherSuites: []dtls.CipherSuiteID{dtls.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8},
InsecureSkipVerify: true,
ClientAuth: dtls.RequireAnyClientCert,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
return nil
},
}
l, err := coapNet.NewDTLSListener("udp", ":", &config, time.Millisecond*100)
require.NoError(t, err)

s := &Server{
Net: "udp-dtls",
Listener: l,
KeepAlive: MustMakeKeepAlive(time.Second),
}
s.Handler = HandlerFunc(func(w ResponseWriter, r *Request) {
certs := r.Client.PeerCertificates()
require.NotEmpty(t, certs)
err := s.Shutdown()
require.NoError(t, err)
})
var wg sync.WaitGroup
wg.Add(1)
defer wg.Wait()
go func() {
defer wg.Done()
s.ActivateAndServe()
}()

conn, err := DialDTLS("udp-dtls", l.Addr().String(), &config)
require.NoError(t, err)
certs := conn.PeerCertificates()
require.NotEmpty(t, certs)
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*100)
defer cancel()
_, err = conn.GetWithContext(ctx, "/")
require.Error(t, err)
conn.Close()
}
5 changes: 5 additions & 0 deletions net/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func (c *Conn) LocalAddr() net.Addr {
return c.connection.LocalAddr()
}

// Connection returns the network connection. The Conn returned is shared by all invocations of Connection, so do not modify it.
func (c *Conn) Connection() net.Conn {
return c.connection
}

// RemoteAddr returns the remote network address. The Addr returned is shared by all invocations of RemoteAddr, so do not modify it.
func (c *Conn) RemoteAddr() net.Addr {
return c.connection.RemoteAddr()
Expand Down
4 changes: 4 additions & 0 deletions networksession.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package coap

import (
"context"
"crypto/x509"
"net"
)

Expand All @@ -12,6 +13,9 @@ type networkSession interface {
LocalAddr() net.Addr
// RemoteAddr returns the net.Addr of the client that sent the current request.
RemoteAddr() net.Addr
// PeerCertificates returns the certificate chain presented by the remote
// peer of the current request.
PeerCertificates() []*x509.Certificate
// WriteContextMsg writes a reply back to the client.
WriteMsgWithContext(ctx context.Context, resp Message) error
// Close closes the connection.
Expand Down
4 changes: 3 additions & 1 deletion request.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package coap

import "context"
import (
"context"
)

type Request struct {
Msg Message
Expand Down
24 changes: 22 additions & 2 deletions sessiondtls.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ package coap
import (
"bytes"
"context"
"crypto/x509"
"fmt"
"net"

"github.com/go-ocf/go-coap/codes"
coapNet "github.com/go-ocf/go-coap/net"
dtls "github.com/pion/dtls/v2"
)

type sessionDTLS struct {
*sessionBase
connection *coapNet.Conn
connection *coapNet.Conn
peerCertificates []*x509.Certificate
}

// newSessionDTLS create new session for DTLS connection
Expand All @@ -31,8 +34,20 @@ func newSessionDTLS(connection *coapNet.Conn, srv *Server) (networkSession, erro
}

s := sessionDTLS{
connection: connection,
sessionBase: newBaseSession(BlockWiseTransfer, BlockWiseTransferSzx, srv),
connection: connection,
}

dtlsConn := connection.Connection().(*dtls.Conn)
cert := dtlsConn.RemoteCertificate()
if len(cert) > 0 {
flatCerts := bytes.Join(cert, nil)
peerCertificates, err := x509.ParseCertificates(flatCerts)
if err != nil {
return nil, err
}

s.peerCertificates = peerCertificates
}

return &s, nil
Expand All @@ -48,6 +63,11 @@ func (s *sessionDTLS) RemoteAddr() net.Addr {
return s.connection.RemoteAddr()
}

// PeerCertificates implements the networkSession.PeerCertificates method.
func (s *sessionDTLS) PeerCertificates() []*x509.Certificate {
return s.peerCertificates
}

// BlockWiseTransferEnabled
func (s *sessionDTLS) blockWiseEnabled() bool {
return s.blockWiseTransfer
Expand Down
6 changes: 6 additions & 0 deletions sessiontcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package coap
import (
"bytes"
"context"
"crypto/x509"
"fmt"
"net"
"sync/atomic"
Expand Down Expand Up @@ -55,6 +56,11 @@ func (s *sessionTCP) RemoteAddr() net.Addr {
return s.connection.RemoteAddr()
}

// PeerCertificates implements the networkSession.PeerCertificates method.
func (s *sessionTCP) PeerCertificates() []*x509.Certificate {
return nil
}

func (s *sessionTCP) blockWiseEnabled() bool {
return s.blockWiseTransfer /*&& atomic.LoadUint32(&s.peerBlockWiseTransfer) != 0*/
}
Expand Down
6 changes: 6 additions & 0 deletions sessionudp.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package coap
import (
"bytes"
"context"
"crypto/x509"
"fmt"
"net"

Expand Down Expand Up @@ -57,6 +58,11 @@ func (s *sessionUDP) RemoteAddr() net.Addr {
return s.sessionUDPData.RemoteAddr()
}

// PeerCertificates implements the networkSession.PeerCertificates method.
func (s *sessionUDP) PeerCertificates() []*x509.Certificate {
return nil
}

// BlockWiseTransferEnabled
func (s *sessionUDP) blockWiseEnabled() bool {
return s.blockWiseTransfer
Expand Down