Skip to content

Commit

Permalink
Add QuickCheck tests for cert behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcoPolo committed Oct 20, 2022
1 parent daeb13c commit 92bcb04
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 4 deletions.
2 changes: 1 addition & 1 deletion p2p/transport/webtransport/cert_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func getCurrentBucketStartTime(now time.Time, offset time.Duration) time.Time {
}

func (m *certManager) init(hostKey ic.PrivKey) error {
start := m.clock.Now().Add(-clockSkewAllowance)
start := m.clock.Now()
pubkeyBytes, err := hostKey.GetPublic().Raw()
if err != nil {
return err
Expand Down
28 changes: 25 additions & 3 deletions p2p/transport/webtransport/cert_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/tls"
"fmt"
"testing"
"testing/quick"
"time"

"github.com/benbjohnson/clock"
Expand Down Expand Up @@ -42,7 +43,7 @@ func certHashFromComponent(t *testing.T, comp ma.Component) []byte {
func TestInitialCert(t *testing.T) {
cl := clock.NewMock()
cl.Add(1234567 * time.Hour)
priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 32)
priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 256)
require.NoError(t, err)
m, err := newCertManager(priv, cl)
require.NoError(t, err)
Expand All @@ -65,7 +66,7 @@ func TestInitialCert(t *testing.T) {
func TestCertRenewal(t *testing.T) {
cl := clock.NewMock()
cl.Set(time.UnixMilli(0))
priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 32)
priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 256)
require.NoError(t, err)
m, err := newCertManager(priv, cl)
require.NoError(t, err)
Expand Down Expand Up @@ -116,7 +117,7 @@ func TestDeterministicCertsAcrossReboots(t *testing.T) {
for i := 0; i < runs; i++ {
t.Run(fmt.Sprintf("Run=%d", i), func(t *testing.T) {
cl := clock.NewMock()
priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 32)
priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 256)
require.NoError(t, err)
m, err := newCertManager(priv, cl)
require.NoError(t, err)
Expand Down Expand Up @@ -151,3 +152,24 @@ func TestDeterministicTimeBuckets(t *testing.T) {
startC := getCurrentBucketStartTime(cl.Now().Add(time.Hour*24*15), 0)
require.NotEqual(t, startC, startB)
}

func TestGetCurrentBucketStartTimeIsWithinBounds(t *testing.T) {
require.NoError(t, quick.Check(func(timeSinceUnixEpoch time.Duration, offset time.Duration) bool {
if offset < 0 {
offset = -offset
}
if timeSinceUnixEpoch < 0 {
timeSinceUnixEpoch = -timeSinceUnixEpoch
}

offset = offset % certValidity
// Bound this to 100 years
timeSinceUnixEpoch = time.Duration(timeSinceUnixEpoch % (time.Hour * 24 * 365 * 100))
// Start a bit further in the future to avoid edge cases around epoch
timeSinceUnixEpoch += time.Hour * 24 * 365
start := time.UnixMilli(timeSinceUnixEpoch.Milliseconds())

bucketStart := getCurrentBucketStartTime(start, offset)
return !bucketStart.After(start) || bucketStart.Equal(start)
}, nil))
}
90 changes: 90 additions & 0 deletions p2p/transport/webtransport/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@ import (
"net"
"strings"
"testing"
"testing/quick"
"time"

"github.com/benbjohnson/clock"
"github.com/libp2p/go-libp2p/core/crypto"
ic "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/network"
mocknetwork "github.com/libp2p/go-libp2p/core/network/mocks"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/test"
tpt "github.com/libp2p/go-libp2p/core/transport"
libp2pwebtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport"
"github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/interop/utils"

"github.com/golang/mock/gomock"
ma "github.com/multiformats/go-multiaddr"
Expand Down Expand Up @@ -584,3 +590,87 @@ func TestSNIIsSent(t *testing.T) {
}

}

func TestServerSendsBackValidCert(t *testing.T) {
const clockSkewAllowance = time.Hour
var maxTimeoutErrors = 10

require.NoError(t, quick.Check(func(timeSinceUnixEpoch time.Duration, keySeed int64, randomClientSkew time.Duration) bool {
if timeSinceUnixEpoch < 0 {
timeSinceUnixEpoch = -timeSinceUnixEpoch
}

// Bound this to 100 years
timeSinceUnixEpoch = time.Duration(timeSinceUnixEpoch % (time.Hour * 24 * 365 * 100))
// Start a bit further in the future to avoid edge cases around epoch
timeSinceUnixEpoch += time.Hour * 24 * 365
start := time.UnixMilli(timeSinceUnixEpoch.Milliseconds())

randomClientSkew = randomClientSkew % clockSkewAllowance

cl := clock.NewMock()
cl.Set(start)

priv, _, err := test.SeededTestKeyPair(crypto.Ed25519, 256, keySeed)
if err != nil {
return false
}
tr, err := libp2pwebtransport.New(priv, nil, network.NullResourceManager, libp2pwebtransport.WithClock(cl))
if err != nil {
return false
}
l, err := tr.Listen(ma.StringCast("/ip4/127.0.0.1/udp/9193/quic/webtransport"))
if err != nil {
return false
}
defer l.Close()

getLogWriter, err := utils.GetQLOGWriter()
if err != nil {
panic(err)
}

conn, err := quic.DialAddr(l.Addr().String(), &tls.Config{
NextProtos: []string{"h3"},
InsecureSkipVerify: true,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
for _, c := range rawCerts {
cert, err := x509.ParseCertificate(c)
if err != nil {
return err
}

for _, clientSkew := range []time.Duration{randomClientSkew, -clockSkewAllowance, clockSkewAllowance} {
clientTime := cl.Now().Add(clientSkew)
if clientTime.After(cert.NotAfter) || clientTime.Before(cert.NotBefore) {
return fmt.Errorf("Times are not valid: server_now=%v client_now=%v certstart=%v certend=%v", cl.Now().UTC(), clientTime.UTC(), cert.NotBefore.UTC(), cert.NotAfter.UTC())
}
}

}
return nil
},
}, &quic.Config{MaxIdleTimeout: time.Second})
_ = getLogWriter

if err != nil {
if _, ok := err.(*quic.IdleTimeoutError); ok {
maxTimeoutErrors -= 1
fmt.Println("Timeout")
if maxTimeoutErrors <= 0 {
fmt.Println("Too many timeout errors")
}
// Sporadic timeout errors on macOS
return true
}
// Print the error so we see what happened, since we only return
// true/false to quickcheck
fmt.Println("Error:", err)
return false
}
defer conn.CloseWithError(0, "")

return true

}, nil))
}

0 comments on commit 92bcb04

Please sign in to comment.