storj/private/revocation/tlsopts_test.go

213 lines
6.0 KiB
Go
Raw Permalink Normal View History

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package revocation_test
import (
"crypto/x509"
"os"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"storj.io/common/identity"
"storj.io/common/identity/testidentity"
"storj.io/common/peertls"
"storj.io/common/peertls/extensions"
"storj.io/common/peertls/testpeertls"
"storj.io/common/peertls/tlsopts"
"storj.io/common/storj"
"storj.io/common/testcontext"
"storj.io/storj/private/kvstore"
"storj.io/storj/private/revocation"
"storj.io/storj/private/testrevocation"
)
func TestNewOptions(t *testing.T) {
// TODO: this is not a great test...
ctx := testcontext.New(t)
defer ctx.Cleanup()
fi, err := testidentity.PregeneratedIdentity(0, storj.LatestIDVersion())
require.NoError(t, err)
whitelistPath := ctx.File("whitelist.pem")
chainData, err := peertls.ChainBytes(fi.CA)
assert.NoError(t, err)
err = os.WriteFile(whitelistPath, chainData, 0644)
assert.NoError(t, err)
cases := []struct {
testID string
config tlsopts.Config
clientVerificationFuncsLen int
serverVerificationFuncsLen int
}{
{
"default",
tlsopts.Config{},
1, 1,
}, {
"revocation processing",
tlsopts.Config{
RevocationDBURL: "bolt://" + ctx.File("revocation1.db"),
Extensions: extensions.Config{
Revocation: true,
},
},
1, 1,
}, {
"ca whitelist verification",
tlsopts.Config{
PeerCAWhitelistPath: whitelistPath,
UsePeerCAWhitelist: true,
},
2, 1,
}, {
"ca whitelist verification and whitelist signed leaf verification",
tlsopts.Config{
// NB: file doesn't actually exist
PeerCAWhitelistPath: whitelistPath,
UsePeerCAWhitelist: true,
Extensions: extensions.Config{
WhitelistSignedLeaf: true,
},
},
2, 1,
}, {
"revocation processing and whitelist verification",
tlsopts.Config{
// NB: file doesn't actually exist
PeerCAWhitelistPath: whitelistPath,
UsePeerCAWhitelist: true,
RevocationDBURL: "bolt://" + ctx.File("revocation2.db"),
Extensions: extensions.Config{
Revocation: true,
},
},
2, 1,
}, {
"revocation processing, whitelist, and signed leaf verification",
tlsopts.Config{
// NB: file doesn't actually exist
PeerCAWhitelistPath: whitelistPath,
UsePeerCAWhitelist: true,
RevocationDBURL: "bolt://" + ctx.File("revocation3.db"),
Extensions: extensions.Config{
Revocation: true,
WhitelistSignedLeaf: true,
},
},
2, 1,
},
}
for _, c := range cases {
t.Log(c.testID)
revocationDB, err := revocation.OpenDBFromCfg(ctx, c.config)
require.NoError(t, err)
tlsOptions, err := tlsopts.NewOptions(fi, c.config, revocationDB)
assert.NoError(t, err)
assert.True(t, reflect.DeepEqual(fi, tlsOptions.Ident))
assert.Equal(t, c.config, tlsOptions.Config)
assert.Len(t, tlsOptions.VerificationFuncs.Client(), c.clientVerificationFuncsLen)
assert.Len(t, tlsOptions.VerificationFuncs.Server(), c.serverVerificationFuncsLen)
require.NoError(t, revocationDB.Close())
}
}
func TestExtensionMap_HandleExtensions(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
testidentity.IdentityVersionsTest(t, func(t *testing.T, version storj.IDVersion, _ *identity.FullIdentity) {
keys, originalChain, err := testpeertls.NewCertChain(2, version.Number)
assert.NoError(t, err)
rev := new(extensions.Revocation)
oldRevokedLeafChain, revocationExt, err := testpeertls.RevokeLeaf(keys[peertls.CAIndex], originalChain)
require.NoError(t, err)
err = rev.Unmarshal(revocationExt.Value)
require.NoError(t, err)
err = rev.Verify(oldRevokedLeafChain[peertls.CAIndex])
require.NoError(t, err)
// NB: node ID is the same, timestamp must change
// (see: identity.RevocationDB#Put)
time.Sleep(1 * time.Second)
newRevokedLeafChain, revocationExt, err := testpeertls.RevokeLeaf(keys[peertls.CAIndex], oldRevokedLeafChain)
require.NoError(t, err)
err = rev.Unmarshal(revocationExt.Value)
require.NoError(t, err)
err = rev.Verify(newRevokedLeafChain[peertls.CAIndex])
require.NoError(t, err)
testrevocation.RunDBs(t, func(t *testing.T, revDB extensions.RevocationDB, db kvstore.Store) {
opts := &extensions.Options{
RevocationDB: revDB,
PeerIDVersions: "*",
}
testcases := []struct {
name string
chain []*x509.Certificate
}{
{"no extensions", originalChain},
{"leaf revocation", oldRevokedLeafChain},
{"double leaf revocation", newRevokedLeafChain},
// TODO: more and more diverse extensions in cases
}
{
handlerFuncMap := extensions.DefaultHandlers.WithOptions(opts)
for _, testcase := range testcases {
t.Log(testcase.name)
extensionsMap := tlsopts.NewExtensionsMap(testcase.chain...)
err := extensionsMap.HandleExtensions(handlerFuncMap, identity.ToChains(testcase.chain))
assert.NoError(t, err)
}
}
})
})
}
func TestExtensionMap_HandleExtensions_error(t *testing.T) {
ctx := testcontext.New(t)
defer ctx.Cleanup()
testrevocation.RunDBs(t, func(t *testing.T, revDB extensions.RevocationDB, db kvstore.Store) {
keys, chain, oldRevocation, err := testpeertls.NewRevokedLeafChain()
assert.NoError(t, err)
// NB: node ID is the same, timestamp must change
// (see: identity.RevocationDB#Put)
time.Sleep(time.Second)
_, newRevocation, err := testpeertls.RevokeLeaf(keys[peertls.CAIndex], chain)
require.NoError(t, err)
assert.NotEqual(t, oldRevocation, newRevocation)
err = revDB.Put(ctx, chain, newRevocation)
assert.NoError(t, err)
opts := &extensions.Options{RevocationDB: revDB}
handlerFuncMap := extensions.HandlerFactories{
extensions.RevocationUpdateHandler,
}.WithOptions(opts)
extensionsMap := tlsopts.NewExtensionsMap(chain[peertls.LeafIndex])
assert.Equal(t, oldRevocation, extensionsMap[extensions.RevocationExtID.String()])
err = extensionsMap.HandleExtensions(handlerFuncMap, identity.ToChains(chain))
assert.Errorf(t, err, extensions.ErrRevocationTimestamp.Error())
})
}