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 }