github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/stellar/airdrop/reg.go (about)

     1  package stellar
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"errors"
     8  	"fmt"
     9  	"net"
    10  	"time"
    11  
    12  	"github.com/keybase/client/go/libkb"
    13  	"github.com/keybase/client/go/msgpack"
    14  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    15  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    16  	"golang.org/x/crypto/nacl/box"
    17  )
    18  
    19  // An experimental airdrop registration protocol, we may or may not decide to use this.
    20  
    21  type dialFunc func(m libkb.MetaContext) (net.Conn, error)
    22  
    23  type Client struct {
    24  	uid      keybase1.UID
    25  	dialFunc dialFunc
    26  }
    27  
    28  func NewClient() *Client {
    29  	ret := &Client{}
    30  	ret.dialFunc = ret.dial
    31  	return ret
    32  }
    33  
    34  func (a *Client) dial(m libkb.MetaContext) (conn net.Conn, err error) {
    35  	defer m.Trace("airdrop.Client#dial", &err)()
    36  	uri, tls, err := a.getURIAndTLS(m)
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  	conn, err = uri.DialWithConfig(tls)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	return conn, err
    45  }
    46  
    47  func (a *Client) getURIAndTLS(m libkb.MetaContext) (uri *rpc.FMPURI, tlsConfig *tls.Config, err error) {
    48  	defer m.Trace("airdrop.Client#getURIAndTLS", &err)()
    49  
    50  	rm := m.G().Env.GetRunMode()
    51  	s, found := libkb.MpackAPIServerLookup[rm]
    52  	if !found {
    53  		return nil, nil, fmt.Errorf("URI not found for run mode: %s", rm)
    54  	}
    55  	uri, err = rpc.ParseFMPURI(s)
    56  	if err != nil {
    57  		return nil, nil, err
    58  	}
    59  	if !uri.UseTLS() {
    60  		return uri, nil, nil
    61  	}
    62  	var ok bool
    63  	var ca []byte
    64  	ca, ok = libkb.GetBundledCAsFromHost(uri.Host)
    65  	if !ok {
    66  		return nil, nil, fmt.Errorf("No CA found for URI %s", s)
    67  	}
    68  	certs := x509.NewCertPool()
    69  	if !certs.AppendCertsFromPEM(ca) {
    70  		return nil, nil, errors.New("Unable to load root certificates")
    71  	}
    72  	tlsConfig = &tls.Config{
    73  		RootCAs:    certs,
    74  		ServerName: uri.Host,
    75  	}
    76  	return uri, tlsConfig, nil
    77  }
    78  
    79  func (a *Client) connect(m libkb.MetaContext) (cli keybase1.AirdropClient, xp rpc.Transporter, err error) {
    80  	conn, err := a.dialFunc(m)
    81  	if err != nil {
    82  		return cli, nil, err
    83  	}
    84  
    85  	xp = libkb.NewTransportFromSocket(m.G(), conn, keybase1.NetworkSource_REMOTE)
    86  	genericCli := rpc.NewClient(xp, libkb.NewContextifiedErrorUnwrapper(m.G()), nil)
    87  	return keybase1.AirdropClient{Cli: genericCli}, xp, nil
    88  }
    89  
    90  type sharedKey [32]byte
    91  
    92  func precomputeKey(privKey libkb.NaclDHKeyPair, pubKey keybase1.BinaryKID) (sharedKey sharedKey, err error) {
    93  	serverPublicKey, err := libkb.BinaryKIDToRawNaCl(pubKey)
    94  	if err != nil {
    95  		return sharedKey, ErrBadKID
    96  	}
    97  	var serverPublicKeyBuf [32]byte
    98  	copy(serverPublicKeyBuf[:], serverPublicKey)
    99  	box.Precompute((*[32]byte)(&sharedKey), &serverPublicKeyBuf, (*[32]byte)(privKey.Private))
   100  	return sharedKey, nil
   101  }
   102  
   103  func (a *Client) round1(m libkb.MetaContext, cli keybase1.AirdropClient) (sharedKey sharedKey, uid keybase1.UID, kid keybase1.BinaryKID, err error) {
   104  	uid, myEncKey := m.G().ActiveDevice.UIDAndEncryptionKey()
   105  	if uid.IsNil() {
   106  		return sharedKey, uid, kid, errors.New("cannot register if logged out")
   107  	}
   108  	kid = myEncKey.GetBinaryKID()
   109  	arg := keybase1.Reg1Arg{
   110  		Uid: uid,
   111  		Kid: kid,
   112  	}
   113  	res, err := cli.Reg1(m.Ctx(), arg)
   114  	if err != nil {
   115  		return sharedKey, uid, kid, err
   116  	}
   117  	myEncKeyDH, ok := myEncKey.(libkb.NaclDHKeyPair)
   118  	if !ok {
   119  		return sharedKey, uid, kid, errors.New("got wrong type of secret key back")
   120  	}
   121  	sharedKey, err = precomputeKey(myEncKeyDH, res)
   122  	if err != nil {
   123  		return sharedKey, uid, kid, err
   124  	}
   125  	return sharedKey, uid, kid, nil
   126  }
   127  
   128  func (a *Client) round2(m libkb.MetaContext, cli keybase1.AirdropClient, sharedKey sharedKey, uid keybase1.UID, kid keybase1.BinaryKID) (err error) {
   129  	plaintext := keybase1.AirdropDetails{
   130  		Time: keybase1.ToTime(m.G().Clock().Now()),
   131  		Uid:  uid,
   132  		Kid:  kid,
   133  		Vid:  libkb.VID(m, a.uid),
   134  		Vers: libkb.HeaderVersion(),
   135  	}
   136  	b, err := msgpack.Encode(&plaintext)
   137  	if err != nil {
   138  		return err
   139  	}
   140  	var nonce [24]byte
   141  	ctext := box.SealAfterPrecomputation(nil, b, &nonce, (*[32]byte)(&sharedKey))
   142  	err = cli.Reg2(m.Ctx(), ctext)
   143  	return err
   144  }
   145  
   146  func (a *Client) Register(m libkb.MetaContext) (err error) {
   147  	m = m.WithLogTag("AIRDROP")
   148  	cli, xp, err := a.connect(m)
   149  	if err != nil {
   150  		return err
   151  	}
   152  	defer xp.Close()
   153  	symKey, uid, kid, err := a.round1(m, cli)
   154  	if err != nil {
   155  		return err
   156  	}
   157  	err = a.round2(m, cli, symKey, uid, kid)
   158  	return err
   159  }
   160  
   161  type state int
   162  
   163  const (
   164  	stateNone state = 0
   165  	state1    state = 1
   166  	state2    state = 2
   167  )
   168  
   169  type RequestProcessor interface {
   170  	Reg1(ctx context.Context, uid keybase1.UID, kid keybase1.BinaryKID, err error)
   171  	Reg2(ctx context.Context, details keybase1.AirdropDetails, err error)
   172  	Close(ctx context.Context, err error)
   173  }
   174  
   175  type RequestHandler struct {
   176  	serverKey libkb.NaclDHKeyPair
   177  	uid       keybase1.UID
   178  	userKey   keybase1.BinaryKID
   179  	state     state
   180  	xp        rpc.Transporter
   181  	proc      RequestProcessor
   182  	sharedKey sharedKey
   183  }
   184  
   185  func HandleRequest(ctx context.Context, xp rpc.Transporter, srv *rpc.Server, p RequestProcessor) (err error) {
   186  	defer func() {
   187  		if err != nil {
   188  			xp.Close()
   189  		}
   190  	}()
   191  	var kp libkb.NaclDHKeyPair
   192  	kp, err = libkb.GenerateNaclDHKeyPair()
   193  	if err != nil {
   194  		return err
   195  	}
   196  	arh := &RequestHandler{
   197  		state:     stateNone,
   198  		xp:        xp,
   199  		serverKey: kp,
   200  		proc:      p,
   201  	}
   202  	prot := keybase1.AirdropProtocol(arh)
   203  	err = srv.Register(prot)
   204  	if err != nil {
   205  		return err
   206  	}
   207  	go arh.Run(ctx, xp, srv)
   208  	return nil
   209  
   210  }
   211  
   212  func (a *RequestHandler) Run(ctx context.Context, xp rpc.Transporter, srv *rpc.Server) {
   213  	var err error
   214  	select {
   215  	case <-srv.Run():
   216  	case <-time.After(time.Minute):
   217  		err = ErrTimeout
   218  	case <-ctx.Done():
   219  		err = ErrCanceled
   220  	}
   221  	xp.Close()
   222  	a.proc.Close(ctx, err)
   223  }
   224  
   225  func (a *RequestHandler) Reg1(ctx context.Context, arg keybase1.Reg1Arg) (ret keybase1.BinaryKID, err error) {
   226  	defer func() {
   227  		a.proc.Reg1(ctx, arg.Uid, arg.Kid, err)
   228  	}()
   229  	if a.state != stateNone {
   230  		return ret, ErrWrongState
   231  	}
   232  	a.state = state1
   233  	a.uid = arg.Uid
   234  	a.userKey = arg.Kid
   235  	ret = a.serverKey.Public.GetBinaryKID()
   236  	a.sharedKey, err = precomputeKey(a.serverKey, a.userKey)
   237  	if err != nil {
   238  		return ret, err
   239  	}
   240  	return ret, err
   241  }
   242  
   243  var ErrWrongState = errors.New("wrong state")
   244  var ErrCannotDecrypt = errors.New("cannot decrypt")
   245  var ErrWrongUID = errors.New("wrong UID")
   246  var ErrWrongKID = errors.New("wrong KID")
   247  var ErrBadKID = errors.New("bad KID")
   248  var ErrTimeout = errors.New("request timed out")
   249  var ErrCanceled = errors.New("canceled")
   250  
   251  func (a *RequestHandler) Reg2(ctx context.Context, ctext []byte) (err error) {
   252  	var nonce [24]byte
   253  	var details keybase1.AirdropDetails
   254  	defer func() {
   255  		a.proc.Reg2(ctx, details, err)
   256  	}()
   257  	if a.state != state1 {
   258  		err = ErrWrongState
   259  		return err
   260  	}
   261  	a.state = state2
   262  	plaintext, ok := box.OpenAfterPrecomputation(nil, ctext, &nonce, (*[32]byte)(&a.sharedKey))
   263  	if !ok {
   264  		err = ErrCannotDecrypt
   265  		return err
   266  	}
   267  	err = msgpack.Decode(&details, plaintext)
   268  	if err != nil {
   269  		return err
   270  	}
   271  	if !details.Uid.Equal(a.uid) {
   272  		err = ErrWrongUID
   273  		return err
   274  	}
   275  	if !details.Kid.Equal(a.userKey) {
   276  		err = ErrWrongKID
   277  		return err
   278  	}
   279  	return nil
   280  }