wip: secureservice with verifiers
This commit is contained in:
parent
d91d0941f6
commit
d30d79a110
@ -11,9 +11,13 @@ type contextKey uint
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
contextKeyPeerId contextKey = iota
|
contextKeyPeerId contextKey = iota
|
||||||
|
contextKeyIdentity
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrPeerIdNotFoundInContext = errors.New("peer id not found in context")
|
var (
|
||||||
|
ErrPeerIdNotFoundInContext = errors.New("peer id not found in context")
|
||||||
|
ErrIdentityNotFoundInContext = errors.New("identity not found in context")
|
||||||
|
)
|
||||||
|
|
||||||
// CtxPeerId first tries to get peer id under our own key, but if it is not found tries to get through DRPC key
|
// CtxPeerId first tries to get peer id under our own key, but if it is not found tries to get through DRPC key
|
||||||
func CtxPeerId(ctx context.Context) (string, error) {
|
func CtxPeerId(ctx context.Context) (string, error) {
|
||||||
@ -30,3 +34,16 @@ func CtxPeerId(ctx context.Context) (string, error) {
|
|||||||
func CtxWithPeerId(ctx context.Context, peerId string) context.Context {
|
func CtxWithPeerId(ctx context.Context, peerId string) context.Context {
|
||||||
return context.WithValue(ctx, contextKeyPeerId, peerId)
|
return context.WithValue(ctx, contextKeyPeerId, peerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CtxIdentity returns identity from context
|
||||||
|
func CtxIdentity(ctx context.Context) ([]byte, error) {
|
||||||
|
if identity, ok := ctx.Value(contextKeyIdentity).([]byte); ok {
|
||||||
|
return identity, nil
|
||||||
|
}
|
||||||
|
return nil, ErrIdentityNotFoundInContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// CtxWithIdentity sets identity in the context
|
||||||
|
func CtxWithIdentity(ctx context.Context, identity []byte) context.Context {
|
||||||
|
return context.WithValue(ctx, contextKeyIdentity, identity)
|
||||||
|
}
|
||||||
|
|||||||
72
net/secureservice/credential.go
Normal file
72
net/secureservice/credential.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package secureservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
|
||||||
|
"github.com/anytypeio/any-sync/net/secureservice/handshake"
|
||||||
|
"github.com/anytypeio/any-sync/net/secureservice/handshake/handshakeproto"
|
||||||
|
"github.com/anytypeio/any-sync/util/keys/asymmetric/signingkey"
|
||||||
|
"github.com/libp2p/go-libp2p/core/sec"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newNoVerifyChecker() handshake.CredentialChecker {
|
||||||
|
return &noVerifyChecker{cred: &handshakeproto.Credentials{Type: handshakeproto.CredentialsType_SkipVerify}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type noVerifyChecker struct {
|
||||||
|
cred *handshakeproto.Credentials
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n noVerifyChecker) MakeCredentials(sc sec.SecureConn) *handshakeproto.Credentials {
|
||||||
|
return n.cred
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n noVerifyChecker) CheckCredential(sc sec.SecureConn, cred *handshakeproto.Credentials) (identity []byte, err error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPeerSignVerifier(account *accountdata.AccountData) handshake.CredentialChecker {
|
||||||
|
return &peerSignVerifier{account: account}
|
||||||
|
}
|
||||||
|
|
||||||
|
type peerSignVerifier struct {
|
||||||
|
account *accountdata.AccountData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *peerSignVerifier) MakeCredentials(sc sec.SecureConn) *handshakeproto.Credentials {
|
||||||
|
sign, err := p.account.SignKey.Sign([]byte(p.account.PeerId + sc.RemotePeer().String()))
|
||||||
|
if err != nil {
|
||||||
|
log.Warn("can't sign identity credentials", zap.Error(err))
|
||||||
|
}
|
||||||
|
msg := &handshakeproto.PayloadSignedPeerIds{
|
||||||
|
Identity: p.account.Identity,
|
||||||
|
Sign: sign,
|
||||||
|
}
|
||||||
|
payload, _ := msg.Marshal()
|
||||||
|
return &handshakeproto.Credentials{
|
||||||
|
Type: handshakeproto.CredentialsType_SignedPeerIds,
|
||||||
|
Payload: payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *peerSignVerifier) CheckCredential(sc sec.SecureConn, cred *handshakeproto.Credentials) (identity []byte, err error) {
|
||||||
|
if cred.Type != handshakeproto.CredentialsType_SignedPeerIds {
|
||||||
|
return nil, handshake.ErrSkipVerifyNotAllowed
|
||||||
|
}
|
||||||
|
var msg = &handshakeproto.PayloadSignedPeerIds{}
|
||||||
|
if err = msg.Unmarshal(cred.Payload); err != nil {
|
||||||
|
return nil, handshake.ErrUnexpectedPayload
|
||||||
|
}
|
||||||
|
pubKey, err := signingkey.NewSigningEd25519PubKeyFromBytes(msg.Identity)
|
||||||
|
if err != nil {
|
||||||
|
return nil, handshake.ErrInvalidCredentials
|
||||||
|
}
|
||||||
|
ok, err := pubKey.Verify([]byte((sc.RemotePeer().String() + p.account.PeerId)), msg.Sign)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
return nil, handshake.ErrInvalidCredentials
|
||||||
|
}
|
||||||
|
return msg.Identity, nil
|
||||||
|
}
|
||||||
77
net/secureservice/credential_test.go
Normal file
77
net/secureservice/credential_test.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package secureservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
|
||||||
|
"github.com/anytypeio/any-sync/net/secureservice/handshake"
|
||||||
|
"github.com/anytypeio/any-sync/testutil/accounttest"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/libp2p/go-libp2p/core/network"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
"github.com/libp2p/go-libp2p/core/sec"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPeerSignVerifier_CheckCredential(t *testing.T) {
|
||||||
|
a1 := newTestAccData(t)
|
||||||
|
a2 := newTestAccData(t)
|
||||||
|
|
||||||
|
cc1 := newPeerSignVerifier(a1)
|
||||||
|
cc2 := newPeerSignVerifier(a2)
|
||||||
|
|
||||||
|
c1 := newTestSC(a2.PeerId)
|
||||||
|
c2 := newTestSC(a1.PeerId)
|
||||||
|
|
||||||
|
cr1 := cc1.MakeCredentials(c1)
|
||||||
|
cr2 := cc2.MakeCredentials(c2)
|
||||||
|
id1, err := cc1.CheckCredential(c1, cr2)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, a2.Identity, id1)
|
||||||
|
|
||||||
|
id2, err := cc2.CheckCredential(c2, cr1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, a1.Identity, id2)
|
||||||
|
|
||||||
|
_, err = cc1.CheckCredential(c1, cr1)
|
||||||
|
assert.EqualError(t, err, handshake.ErrInvalidCredentials.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestAccData(t *testing.T) *accountdata.AccountData {
|
||||||
|
as := accounttest.AccountTestService{}
|
||||||
|
require.NoError(t, as.Init(nil))
|
||||||
|
return as.Account()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestSC(peerId string) sec.SecureConn {
|
||||||
|
pid, _ := peer.Decode(peerId)
|
||||||
|
return &testSc{
|
||||||
|
ID: pid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testSc struct {
|
||||||
|
net.Conn
|
||||||
|
peer.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSc) LocalPeer() peer.ID {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSc) LocalPrivateKey() crypto.PrivKey {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSc) RemotePeer() peer.ID {
|
||||||
|
return t.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSc) RemotePublicKey() crypto.PubKey {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSc) ConnState() network.ConnectionState {
|
||||||
|
return network.ConnectionState{}
|
||||||
|
}
|
||||||
@ -5,6 +5,9 @@ import (
|
|||||||
commonaccount "github.com/anytypeio/any-sync/accountservice"
|
commonaccount "github.com/anytypeio/any-sync/accountservice"
|
||||||
"github.com/anytypeio/any-sync/app"
|
"github.com/anytypeio/any-sync/app"
|
||||||
"github.com/anytypeio/any-sync/app/logger"
|
"github.com/anytypeio/any-sync/app/logger"
|
||||||
|
"github.com/anytypeio/any-sync/commonspace/object/accountdata"
|
||||||
|
"github.com/anytypeio/any-sync/net/secureservice/handshake"
|
||||||
|
"github.com/anytypeio/any-sync/nodeconf"
|
||||||
"github.com/libp2p/go-libp2p/core/crypto"
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
"github.com/libp2p/go-libp2p/core/sec"
|
"github.com/libp2p/go-libp2p/core/sec"
|
||||||
libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls"
|
libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls"
|
||||||
@ -41,7 +44,13 @@ type SecureService interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type secureService struct {
|
type secureService struct {
|
||||||
key crypto.PrivKey
|
outboundTr *libp2ptls.Transport
|
||||||
|
account *accountdata.AccountData
|
||||||
|
key crypto.PrivKey
|
||||||
|
nodeconf nodeconf.Service
|
||||||
|
|
||||||
|
noVerifyChecker handshake.CredentialChecker
|
||||||
|
peerSignVerifier handshake.CredentialChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *secureService) Init(a *app.App) (err error) {
|
func (s *secureService) Init(a *app.App) (err error) {
|
||||||
@ -54,8 +63,12 @@ func (s *secureService) Init(a *app.App) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("secure service init", zap.String("peerId", account.Account().PeerId))
|
s.noVerifyChecker = newNoVerifyChecker()
|
||||||
|
s.peerSignVerifier = newPeerSignVerifier(account.Account())
|
||||||
|
|
||||||
|
s.nodeconf = a.MustComponent(nodeconf.CName).(nodeconf.Service)
|
||||||
|
|
||||||
|
log.Info("secure service init", zap.String("peerId", account.Account().PeerId))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,9 +85,22 @@ func (s *secureService) BasicListener(lis net.Listener, timeoutMillis int) Conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *secureService) TLSConn(ctx context.Context, conn net.Conn) (sec.SecureConn, error) {
|
func (s *secureService) TLSConn(ctx context.Context, conn net.Conn) (sec.SecureConn, error) {
|
||||||
tr, err := libp2ptls.New(libp2ptls.ID, s.key, nil)
|
sc, err := s.outboundTr.SecureOutbound(ctx, conn, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, HandshakeError{err: err, remoteAddr: conn.RemoteAddr().String()}
|
||||||
}
|
}
|
||||||
return tr.SecureOutbound(ctx, conn, "")
|
peerId := sc.RemotePeer().String()
|
||||||
|
confTypes := s.nodeconf.GetLast().NodeTypes(peerId)
|
||||||
|
var checker handshake.CredentialChecker
|
||||||
|
if len(confTypes) > 0 {
|
||||||
|
checker = s.peerSignVerifier
|
||||||
|
} else {
|
||||||
|
checker = s.noVerifyChecker
|
||||||
|
}
|
||||||
|
// ignore identity for outgoing connection because we don't need it at this moment
|
||||||
|
_, err = handshake.OutgoingHandshake(sc, checker)
|
||||||
|
if err != nil {
|
||||||
|
return nil, HandshakeError{err: err, remoteAddr: conn.RemoteAddr().String()}
|
||||||
|
}
|
||||||
|
return sc, nil
|
||||||
}
|
}
|
||||||
|
|||||||
35
net/secureservice/secureservice_test.go
Normal file
35
net/secureservice/secureservice_test.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package secureservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/anytypeio/any-sync/app"
|
||||||
|
"github.com/anytypeio/any-sync/testutil/accounttest"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ctx = context.Background()
|
||||||
|
|
||||||
|
func TestHandshake(t *testing.T) {
|
||||||
|
fx := newFixture(t)
|
||||||
|
defer fx.Finish(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFixture(t *testing.T) *fixture {
|
||||||
|
fx := &fixture{
|
||||||
|
secureService: New().(*secureService),
|
||||||
|
a: new(app.App),
|
||||||
|
}
|
||||||
|
fx.a.Register(&accounttest.AccountTestService{}).Register(fx.secureService)
|
||||||
|
require.NoError(t, fx.a.Start(ctx))
|
||||||
|
return fx
|
||||||
|
}
|
||||||
|
|
||||||
|
type fixture struct {
|
||||||
|
*secureService
|
||||||
|
a *app.App
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fx *fixture) Finish(t *testing.T) {
|
||||||
|
require.NoError(t, fx.a.Close(ctx))
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ package secureservice
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/anytypeio/any-sync/net/peer"
|
"github.com/anytypeio/any-sync/net/peer"
|
||||||
|
"github.com/anytypeio/any-sync/net/secureservice/handshake"
|
||||||
"github.com/anytypeio/any-sync/net/timeoutconn"
|
"github.com/anytypeio/any-sync/net/timeoutconn"
|
||||||
"github.com/libp2p/go-libp2p/core/crypto"
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls"
|
libp2ptls "github.com/libp2p/go-libp2p/p2p/security/tls"
|
||||||
@ -22,9 +23,10 @@ type ContextListener interface {
|
|||||||
Addr() net.Addr
|
Addr() net.Addr
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTLSListener(key crypto.PrivKey, lis net.Listener, timeoutMillis int) ContextListener {
|
func newTLSListener(cc handshake.CredentialChecker, key crypto.PrivKey, lis net.Listener, timeoutMillis int) ContextListener {
|
||||||
tr, _ := libp2ptls.New(libp2ptls.ID, key, nil)
|
tr, _ := libp2ptls.New(libp2ptls.ID, key, nil)
|
||||||
return &tlsListener{
|
return &tlsListener{
|
||||||
|
cc: cc,
|
||||||
tr: tr,
|
tr: tr,
|
||||||
Listener: lis,
|
Listener: lis,
|
||||||
timeoutMillis: timeoutMillis,
|
timeoutMillis: timeoutMillis,
|
||||||
@ -35,6 +37,7 @@ type tlsListener struct {
|
|||||||
net.Listener
|
net.Listener
|
||||||
tr *libp2ptls.Transport
|
tr *libp2ptls.Transport
|
||||||
timeoutMillis int
|
timeoutMillis int
|
||||||
|
cc handshake.CredentialChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tlsListener) Accept(ctx context.Context) (context.Context, net.Conn, error) {
|
func (p *tlsListener) Accept(ctx context.Context) (context.Context, net.Conn, error) {
|
||||||
@ -54,6 +57,14 @@ func (p *tlsListener) upgradeConn(ctx context.Context, conn net.Conn) (context.C
|
|||||||
err: err,
|
err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
identity, err := handshake.IncomingHandshake(secure, p.cc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, HandshakeError{
|
||||||
|
remoteAddr: conn.RemoteAddr().String(),
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
ctx = peer.CtxWithPeerId(ctx, secure.RemotePeer().String())
|
ctx = peer.CtxWithPeerId(ctx, secure.RemotePeer().String())
|
||||||
|
ctx = peer.CtxWithIdentity(ctx, identity)
|
||||||
return ctx, secure, nil
|
return ctx, secure, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,8 @@ type Configuration interface {
|
|||||||
CHash() chash.CHash
|
CHash() chash.CHash
|
||||||
// Partition returns partition number by spaceId
|
// Partition returns partition number by spaceId
|
||||||
Partition(spaceId string) (part int)
|
Partition(spaceId string) (part int)
|
||||||
|
// NodeTypes returns list of known nodeTypes by nodeId, if node not registered in configuration will return empty list
|
||||||
|
NodeTypes(nodeId string) []NodeType
|
||||||
}
|
}
|
||||||
|
|
||||||
type configuration struct {
|
type configuration struct {
|
||||||
@ -82,6 +84,15 @@ func (c *configuration) Partition(spaceId string) (part int) {
|
|||||||
return c.chash.GetPartition(ReplKey(spaceId))
|
return c.chash.GetPartition(ReplKey(spaceId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *configuration) NodeTypes(nodeId string) []NodeType {
|
||||||
|
for _, m := range c.allMembers {
|
||||||
|
if m.PeerId == nodeId {
|
||||||
|
return m.Types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func ReplKey(spaceId string) (replKey string) {
|
func ReplKey(spaceId string) (replKey string) {
|
||||||
if i := strings.LastIndex(spaceId, "."); i != -1 {
|
if i := strings.LastIndex(spaceId, "."); i != -1 {
|
||||||
return spaceId[i+1:]
|
return spaceId[i+1:]
|
||||||
|
|||||||
@ -34,15 +34,21 @@ func (s *AccountTestService) Init(a *app.App) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
peerId, err := peer.IdFromSigningPubKey(signKey.GetPublic())
|
peerKey, _, err := signingkey.GenerateRandomEd25519KeyPair()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
peerId, err := peer.IdFromSigningPubKey(peerKey.GetPublic())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.acc = &accountdata.AccountData{
|
s.acc = &accountdata.AccountData{
|
||||||
PeerId: peerId.String(),
|
|
||||||
Identity: ident,
|
Identity: ident,
|
||||||
|
PeerKey: peerKey,
|
||||||
SignKey: signKey,
|
SignKey: signKey,
|
||||||
EncKey: encKey,
|
EncKey: encKey,
|
||||||
|
PeerId: peerId.String(),
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user