Improved separation of concerns, added more robust tests
This commit is contained in:
parent
0177e10c15
commit
419700f2c3
|
@ -1,102 +0,0 @@
|
||||||
package pairing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha256"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"github.com/golang/protobuf/proto"
|
|
||||||
udpp2p "github.com/schollz/peerdiscovery"
|
|
||||||
|
|
||||||
"github.com/status-im/status-go/protocol/protobuf"
|
|
||||||
"github.com/status-im/status-go/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
pk = []byte{0xbf, 0x3b, 0x37, 0x04, 0x30, 0x04, 0x32, 0x15, 0x72, 0xb0, 0x7f, 0x56, 0x72, 0x30, 0xae, 0x5b, 0x41, 0xf4, 0x4b, 0x42, 0x4a, 0xa2, 0x33, 0x53, 0x76, 0xed, 0x7a, 0xb9, 0x2d, 0x40, 0x37, 0x73}
|
|
||||||
k = &ecdsa.PrivateKey{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
k = server.ToECDSA(pk)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sign(h *protobuf.LocalPairingPeerHello, k *ecdsa.PrivateKey) error {
|
|
||||||
dHash := sha256.Sum256(append(h.PeerId, []byte(h.DeviceName+h.DeviceType)...))
|
|
||||||
s, err := ecdsa.SignASN1(rand.Reader, k, dHash[:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
h.Signature = s
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func verify(h *protobuf.LocalPairingPeerHello, k *ecdsa.PrivateKey) bool {
|
|
||||||
dHash := sha256.Sum256(append(h.PeerId, []byte(h.DeviceName+h.DeviceType)...))
|
|
||||||
return ecdsa.VerifyASN1(&k.PublicKey, dHash[:], h.Signature)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeDevicePayload(name, deviceType string, k *ecdsa.PrivateKey) (*protobuf.LocalPairingPeerHello, error) {
|
|
||||||
h := new(protobuf.LocalPairingPeerHello)
|
|
||||||
|
|
||||||
randId := make([]byte, 32)
|
|
||||||
_, err := rand.Read(randId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
h.PeerId = randId
|
|
||||||
h.DeviceName = name
|
|
||||||
h.DeviceType = deviceType
|
|
||||||
|
|
||||||
err = sign(h, k)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return h, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type UDPNotifier struct {
|
|
||||||
notifyOutput func(*protobuf.LocalPairingPeerHello)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UDPNotifier) notify(d udpp2p.Discovered) {
|
|
||||||
h := new(protobuf.LocalPairingPeerHello)
|
|
||||||
err := proto.Unmarshal(d.Payload, h)
|
|
||||||
if err != nil {
|
|
||||||
// TODO add logging rather than dumping
|
|
||||||
spew.Dump(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ok := verify(h, k)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
u.notifyOutput(h)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UDPNotifier) makeUDPP2PSettings(h *protobuf.LocalPairingPeerHello) (*udpp2p.Settings, error) {
|
|
||||||
mh, err := proto.Marshal(h)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &udpp2p.Settings{
|
|
||||||
Limit: 4,
|
|
||||||
AllowSelf: true,
|
|
||||||
Notify: u.notify,
|
|
||||||
Payload: mh,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Search() {
|
|
||||||
discoveries, _ := udpp2p.Discover(udpp2p.Settings{Limit: 1, AllowSelf: true})
|
|
||||||
for _, d := range discoveries {
|
|
||||||
fmt.Printf("discovered '%s'\n", d.Address)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package peers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
|
||||||
|
udpp2p "github.com/schollz/peerdiscovery"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/protocol/protobuf"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LocalPairingPeerHello struct {
|
||||||
|
protobuf.LocalPairingPeerHello
|
||||||
|
Discovered udpp2p.Discovered
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocalPairingPeerHello(id []byte, name, deviceType string, k *ecdsa.PrivateKey) (*LocalPairingPeerHello, error) {
|
||||||
|
h := new(LocalPairingPeerHello)
|
||||||
|
|
||||||
|
h.PeerId = id
|
||||||
|
h.DeviceName = name
|
||||||
|
h.DeviceType = deviceType
|
||||||
|
|
||||||
|
err := h.sign(k)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LocalPairingPeerHello) hash() []byte {
|
||||||
|
dHash := sha256.Sum256(append(h.PeerId, []byte(h.DeviceName+h.DeviceType)...))
|
||||||
|
return dHash[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LocalPairingPeerHello) sign(k *ecdsa.PrivateKey) error {
|
||||||
|
s, err := ecdsa.SignASN1(rand.Reader, k, h.hash())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Signature = s
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LocalPairingPeerHello) verify(k *ecdsa.PublicKey) bool {
|
||||||
|
return ecdsa.VerifyASN1(k, h.hash(), h.Signature)
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package peers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
udpp2p "github.com/schollz/peerdiscovery"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// pk Yes this is an actual ECDSA **private** key committed to a public repository visible to anyone.
|
||||||
|
// DO NOT use this key for anything other than signing udp "hellos". The key's value is in giving other Status
|
||||||
|
// installations CONFIDENCE, NOT proof, that the sender of the UDP pings is another Status device.
|
||||||
|
// We do not rely on UDP message information to orchestrate connections or swap secrets. The use case is purely
|
||||||
|
// to make preflight checks which ADVISE the application and the user.
|
||||||
|
//
|
||||||
|
// A signature is more robust and flexible than an application identifier, and serves the same role as an ID, while
|
||||||
|
// securing the payload against tampering.
|
||||||
|
pk = []byte{0xbf, 0x3b, 0x37, 0x04, 0x30, 0x04, 0x32, 0x15, 0x72, 0xb0, 0x7f, 0x56, 0x72, 0x30, 0xae, 0x5b, 0x41, 0xf4, 0x4b, 0x42, 0x4a, 0xa2, 0x33, 0x53, 0x76, 0xed, 0x7a, 0xb9, 0x2d, 0x40, 0x37, 0x73}
|
||||||
|
k = &ecdsa.PrivateKey{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
k = server.ToECDSA(pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UDPNotifier struct {
|
||||||
|
logger *zap.Logger
|
||||||
|
id []byte
|
||||||
|
notifyOutput func(*LocalPairingPeerHello)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUDPNotifier(logger *zap.Logger, outputFunc func(*LocalPairingPeerHello)) (*UDPNotifier, error) {
|
||||||
|
randId := make([]byte, 32)
|
||||||
|
_, err := rand.Read(randId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
n := new(UDPNotifier)
|
||||||
|
n.logger = logger
|
||||||
|
n.id = randId
|
||||||
|
n.notifyOutput = outputFunc
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UDPNotifier) MakePayload(deviceName string) (*LocalPairingPeerHello, error) {
|
||||||
|
return NewLocalPairingPeerHello(u.id, deviceName, runtime.GOOS, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UDPNotifier) notify(d udpp2p.Discovered) {
|
||||||
|
h := new(LocalPairingPeerHello)
|
||||||
|
err := proto.Unmarshal(d.Payload, &h.LocalPairingPeerHello)
|
||||||
|
if err != nil {
|
||||||
|
u.logger.Error("notify unmarshalling of payload failed", zap.Error(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := h.verify(&k.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
u.logger.Error("verification of unmarshalled payload failed", zap.Any("LocalPairingPeerHello", h))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Discovered = d
|
||||||
|
u.notifyOutput(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UDPNotifier) MakeUDPP2PSettings(deviceName string) (*udpp2p.Settings, error) {
|
||||||
|
if u.notifyOutput == nil {
|
||||||
|
return nil, fmt.Errorf("UDPNotifier has no notiftOutput function defined")
|
||||||
|
}
|
||||||
|
|
||||||
|
h, err := u.MakePayload(deviceName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mh, err := proto.Marshal(&h.LocalPairingPeerHello)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &udpp2p.Settings{
|
||||||
|
Notify: u.notify,
|
||||||
|
Payload: mh,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Search() {
|
||||||
|
discoveries, _ := udpp2p.Discover(udpp2p.Settings{Limit: 1, AllowSelf: true})
|
||||||
|
for _, d := range discoveries {
|
||||||
|
fmt.Printf("discovered '%s'\n", d.Address)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
package peers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
udpp2p "github.com/schollz/peerdiscovery"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/server"
|
||||||
|
"github.com/status-im/status-go/server/servertest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUDPPeerDiscoverySuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(UDPPeerDiscoverySuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
type UDPPeerDiscoverySuite struct {
|
||||||
|
suite.Suite
|
||||||
|
servertest.TestLoggerComponents
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UDPPeerDiscoverySuite) SetupSuite() {
|
||||||
|
s.SetupLoggerComponents()
|
||||||
|
}
|
||||||
|
|
||||||
|
type testSignalLogger struct {
|
||||||
|
log map[string]map[string]bool
|
||||||
|
lock sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTestSignalLogger() *testSignalLogger {
|
||||||
|
tsl := new(testSignalLogger)
|
||||||
|
tsl.log = make(map[string]map[string]bool)
|
||||||
|
return tsl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testSignalLogger) testSignal(h *LocalPairingPeerHello) {
|
||||||
|
t.lock.Lock()
|
||||||
|
defer t.lock.Unlock()
|
||||||
|
if _, ok := t.log[h.Discovered.Address]; !ok {
|
||||||
|
t.log[h.Discovered.Address] = make(map[string]bool)
|
||||||
|
}
|
||||||
|
t.log[h.Discovered.Address][h.DeviceName] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UDPPeerDiscoverySuite) Test() {
|
||||||
|
n, err := server.GetDeviceName()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
tsl := NewTestSignalLogger()
|
||||||
|
|
||||||
|
u1, err := NewUDPNotifier(s.Logger, tsl.testSignal)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
u2, err := NewUDPNotifier(s.Logger, tsl.testSignal)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
n1 := n + " - device 1"
|
||||||
|
n2 := n + " - device 2"
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
settings, err := u1.MakeUDPP2PSettings(n1)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
settings.TimeLimit = 2 * time.Second
|
||||||
|
settings.Limit = 4
|
||||||
|
settings.AllowSelf = true
|
||||||
|
|
||||||
|
_, err = udpp2p.Discover(*settings)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
settings, err := u2.MakeUDPP2PSettings(n2)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
settings.TimeLimit = 2 * time.Second
|
||||||
|
settings.Limit = 4
|
||||||
|
settings.AllowSelf = true
|
||||||
|
|
||||||
|
_, err = udpp2p.Discover(*settings)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
s.Require().Len(tsl.log, 2)
|
||||||
|
for _, address := range tsl.log {
|
||||||
|
s.Require().Len(address, 2)
|
||||||
|
|
||||||
|
for device := range address {
|
||||||
|
if !(device == n1 || device == n2) {
|
||||||
|
s.Require().Failf("unknown device name", device)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,69 +0,0 @@
|
||||||
package pairing
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
udpp2p "github.com/schollz/peerdiscovery"
|
|
||||||
|
|
||||||
"github.com/status-im/status-go/protocol/protobuf"
|
|
||||||
"github.com/status-im/status-go/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
func testSignal(h *protobuf.LocalPairingPeerHello) {
|
|
||||||
fmt.Printf("peer message - %s : %s\n", h.DeviceName, h.DeviceType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test(t *testing.T) {
|
|
||||||
n, err := server.GetDeviceName()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
d1, err := makeDevicePayload(n+" - device 1", runtime.GOOS, k)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
d2, err := makeDevicePayload(n+" - device 2", runtime.GOOS, k)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
u := new(UDPNotifier)
|
|
||||||
u.notifyOutput = testSignal
|
|
||||||
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
s, err := u.makeUDPP2PSettings(d1)
|
|
||||||
if err != nil {
|
|
||||||
t.Error("1 -", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = udpp2p.Discover(*s)
|
|
||||||
if err != nil {
|
|
||||||
t.Error("1 -", err)
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
s, err := u.makeUDPP2PSettings(d2)
|
|
||||||
if err != nil {
|
|
||||||
t.Error("2 -", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = udpp2p.Discover(*s)
|
|
||||||
if err != nil {
|
|
||||||
t.Error("2 -", err)
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
|
"github.com/status-im/status-go/logutils"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -12,8 +13,6 @@ import (
|
||||||
"github.com/btcsuite/btcutil/base58"
|
"github.com/btcsuite/btcutil/base58"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/status-im/status-go/logutils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
Loading…
Reference in New Issue