status-go/server/server_pairing.go

201 lines
4.5 KiB
Go
Raw Normal View History

package server
import (
"crypto/ecdsa"
2022-08-31 11:44:12 +00:00
"crypto/elliptic"
2022-08-19 12:45:50 +00:00
"crypto/rand"
"crypto/tls"
"encoding/json"
"fmt"
"net"
2022-08-31 11:44:12 +00:00
"time"
2022-08-19 12:45:50 +00:00
"github.com/gorilla/sessions"
2022-10-21 12:15:39 +00:00
"github.com/status-im/status-go/logutils"
"github.com/status-im/status-go/multiaccounts"
)
type PairingServer struct {
Server
PayloadManager
pk *ecdsa.PublicKey
ek []byte
mode Mode
2022-08-19 12:45:50 +00:00
cookieStore *sessions.CookieStore
}
type Config struct {
2022-07-01 15:37:53 +00:00
// Connection fields
PK *ecdsa.PublicKey
EK []byte
Cert *tls.Certificate
Hostname string
Mode Mode
2022-07-01 15:37:53 +00:00
// Payload management fields
*PairingPayloadManagerConfig
}
2022-08-19 12:45:50 +00:00
func makeCookieStore() (*sessions.CookieStore, error) {
auth := make([]byte, 64)
_, err := rand.Read(auth)
if err != nil {
return nil, err
}
enc := make([]byte, 32)
_, err = rand.Read(enc)
if err != nil {
return nil, err
}
return sessions.NewCookieStore(auth, enc), nil
}
2022-06-28 23:08:57 +00:00
// NewPairingServer returns a *PairingServer init from the given *Config
2022-07-01 15:37:53 +00:00
func NewPairingServer(config *Config) (*PairingServer, error) {
2022-10-21 12:15:39 +00:00
logger := logutils.ZapLogger().Named("PairingServer")
pm, err := NewPairingPayloadManager(config.EK, config.PairingPayloadManagerConfig, logger)
if err != nil {
return nil, err
}
2022-08-19 12:45:50 +00:00
cs, err := makeCookieStore()
if err != nil {
return nil, err
}
return &PairingServer{Server: NewServer(
config.Cert,
config.Hostname,
nil,
2022-10-21 12:15:39 +00:00
logger,
),
2022-07-01 15:37:53 +00:00
pk: config.PK,
ek: config.EK,
2022-07-01 15:37:53 +00:00
mode: config.Mode,
2022-08-19 12:45:50 +00:00
PayloadManager: pm,
cookieStore: cs,
}, nil
}
// MakeConnectionParams generates a *ConnectionParams based on the Server's current state
func (s *PairingServer) MakeConnectionParams() (*ConnectionParams, error) {
netIP := net.ParseIP(s.hostname)
if netIP == nil {
return nil, fmt.Errorf("invalid ip address given '%s'", s.hostname)
}
netIP4 := netIP.To4()
if netIP4 != nil {
netIP = netIP4
}
return NewConnectionParams(netIP, s.MustGetPort(), s.pk, s.ek, s.mode), nil
}
func (s *PairingServer) StartPairing() error {
switch s.mode {
case Receiving:
return s.startReceivingAccountData()
case Sending:
return s.startSendingAccountData()
default:
return fmt.Errorf("invalid server mode '%d'", s.mode)
}
}
func (s *PairingServer) startReceivingAccountData() error {
2022-08-19 12:45:50 +00:00
s.SetHandlers(HandlerPatternMap{
pairingReceive: handlePairingReceive(s),
pairingChallenge: handlePairingChallenge(s),
})
return s.Start()
}
func (s *PairingServer) startSendingAccountData() error {
err := s.Mount()
if err != nil {
return err
}
2022-08-19 12:45:50 +00:00
s.SetHandlers(HandlerPatternMap{
pairingSend: challengeMiddleware(s, handlePairingSend(s)),
pairingChallenge: handlePairingChallenge(s),
})
return s.Start()
}
2022-08-31 11:44:12 +00:00
// MakeFullPairingServer generates a fully configured and randomly seeded PairingServer
func MakeFullPairingServer(db *multiaccounts.Database, mode Mode, storeConfig PairingPayloadSourceConfig) (*PairingServer, error) {
2022-08-31 11:44:12 +00:00
tlsKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, err
}
AESKey := make([]byte, 32)
_, err = rand.Read(AESKey)
if err != nil {
return nil, err
}
outboundIP, err := GetOutboundIP()
if err != nil {
return nil, err
}
tlsCert, _, err := GenerateCertFromKey(tlsKey, time.Now(), outboundIP.String())
if err != nil {
return nil, err
}
return NewPairingServer(&Config{
// Things that can be generated, and CANNOT come from the app client (well they could be this is better)
2022-08-31 11:44:12 +00:00
PK: &tlsKey.PublicKey,
EK: AESKey,
Cert: &tlsCert,
Hostname: outboundIP.String(),
// Things that can't be generated, but DO come from the app client
2022-08-31 11:44:12 +00:00
Mode: mode,
PairingPayloadManagerConfig: &PairingPayloadManagerConfig{
// Things that can't be generated, but DO NOT come from app client
2022-08-31 11:44:12 +00:00
DB: db,
// Things that can't be generated, but DO come from the app client
PairingPayloadSourceConfig: storeConfig,
2022-08-31 11:44:12 +00:00
},
})
}
// StartUpPairingServer generates a PairingServer, starts the pairing server in the correct mode
// and returns the ConnectionParams string to allow a PairingClient to make a successful connection.
func StartUpPairingServer(db *multiaccounts.Database, mode Mode, configJSON string) (string, error) {
var conf PairingPayloadSourceConfig
err := json.Unmarshal([]byte(configJSON), &conf)
if err != nil {
return "", err
}
ps, err := MakeFullPairingServer(db, mode, conf)
if err != nil {
return "", err
}
err = ps.StartPairing()
if err != nil {
return "", err
}
cp, err := ps.MakeConnectionParams()
if err != nil {
return "", err
}
return cp.ToString(), nil
}