github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/service/wot.go (about)

     1  package service
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/keybase/client/go/engine"
     7  	"github.com/keybase/client/go/libkb"
     8  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
     9  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    10  	"golang.org/x/net/context"
    11  )
    12  
    13  type WebOfTrustHandler struct {
    14  	*BaseHandler
    15  	libkb.Contextified
    16  }
    17  
    18  func NewWebOfTrustHandler(xp rpc.Transporter, g *libkb.GlobalContext) *WebOfTrustHandler {
    19  	return &WebOfTrustHandler{
    20  		BaseHandler:  NewBaseHandler(g, xp),
    21  		Contextified: libkb.NewContextified(g),
    22  	}
    23  }
    24  
    25  func (h *WebOfTrustHandler) WotVouch(ctx context.Context, arg keybase1.WotVouchArg) (err error) {
    26  	ctx = libkb.WithLogTag(ctx, "WOT")
    27  	mctx := libkb.NewMetaContext(ctx, h.G())
    28  	defer mctx.Trace(fmt.Sprintf("WotVouch(%v,%v)", arg.Username, arg.GuiID), &err)()
    29  
    30  	// This wotVouch RPC does not run Identify.
    31  	// Because it relies on the previous Identify used to display the vouchee's profile.
    32  	// We must guard against a malicious server doing a last-minute reset of the vouchee to trick the voucher
    33  	// client into signing a statement for the post-reset user.
    34  	// This is guarded by locking on to the eldestSeqno from the guiID of the Identify on the profile screen.
    35  	state, err := mctx.G().Identify3State.Get(arg.GuiID)
    36  	if err != nil {
    37  		return err
    38  	}
    39  	if state == nil {
    40  		return fmt.Errorf("missing identify state")
    41  	}
    42  	outcome := state.Outcome()
    43  	if outcome == nil {
    44  		return fmt.Errorf("missing identify outcome")
    45  	}
    46  	if outcome.Username != libkb.NewNormalizedUsername(arg.Username) {
    47  		return fmt.Errorf("username mismatch: %v != %v", outcome.Username, libkb.NewNormalizedUsername(arg.Username))
    48  	}
    49  	mctx.Debug("vouchee from identify outcome: uid:%v eldestSeqno:%v", outcome.UID, outcome.EldestSeqno)
    50  
    51  	return engine.RunEngine2(mctx, engine.NewWotVouch(h.G(), &engine.WotVouchArg{
    52  		Vouchee: keybase1.UserVersion{
    53  			Uid:         outcome.UID,
    54  			EldestSeqno: outcome.EldestSeqno,
    55  		},
    56  		Confidence: arg.Confidence,
    57  		VouchText:  arg.VouchText,
    58  	}))
    59  }
    60  
    61  func (h *WebOfTrustHandler) WotVouchCLI(ctx context.Context, arg keybase1.WotVouchCLIArg) (err error) {
    62  	ctx = libkb.WithLogTag(ctx, "WOT")
    63  	mctx := libkb.NewMetaContext(ctx, h.G())
    64  	defer mctx.Trace(fmt.Sprintf("WotVouchCLI(%v)", arg.Assertion), &err)()
    65  
    66  	upak, _, err := h.G().GetUPAKLoader().Load(libkb.NewLoadUserArg(h.G()).WithName(arg.Assertion))
    67  	if err != nil {
    68  		return err
    69  	}
    70  
    71  	eng := engine.NewResolveThenIdentify2(mctx.G(), &keybase1.Identify2Arg{
    72  		Uid:              upak.GetUID(),
    73  		UserAssertion:    arg.Assertion,
    74  		NeedProofSet:     true,
    75  		UseDelegateUI:    true,
    76  		Reason:           keybase1.IdentifyReason{Reason: fmt.Sprintf("Vouch for %v", arg.Assertion)},
    77  		IdentifyBehavior: keybase1.TLFIdentifyBehavior_CLI,
    78  	})
    79  	err = engine.RunEngine2(mctx.WithUIs(libkb.UIs{
    80  		IdentifyUI: h.NewRemoteIdentifyUI(arg.SessionID, mctx.G()),
    81  	}), eng)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	idRes, err := eng.Result(mctx)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	if idRes == nil {
    90  		return fmt.Errorf("missing identify result")
    91  	}
    92  	if idRes.TrackBreaks != nil {
    93  		mctx.Debug("WotVouch TrackBreaks: %+v", idRes.TrackBreaks)
    94  		return libkb.TrackingBrokeError{}
    95  	}
    96  	return engine.RunEngine2(mctx, engine.NewWotVouch(h.G(), &engine.WotVouchArg{
    97  		Vouchee:    idRes.Upk.Current.ToUserVersion(),
    98  		Confidence: arg.Confidence,
    99  		VouchText:  arg.VouchText,
   100  	}))
   101  }
   102  
   103  func (h *WebOfTrustHandler) WotFetchVouches(ctx context.Context, arg keybase1.WotFetchVouchesArg) (res []keybase1.WotVouch, err error) {
   104  	ctx = libkb.WithLogTag(ctx, "WOT")
   105  	mctx := libkb.NewMetaContext(ctx, h.G())
   106  	return libkb.FetchWotVouches(mctx, libkb.FetchWotVouchesArg{Vouchee: arg.Vouchee, Voucher: arg.Voucher})
   107  }
   108  
   109  func (h *WebOfTrustHandler) WotReact(ctx context.Context, arg keybase1.WotReactArg) error {
   110  	ctx = libkb.WithLogTag(ctx, "WOT")
   111  	mctx := libkb.NewMetaContext(ctx, h.G())
   112  
   113  	upak, _, err := h.G().GetUPAKLoader().Load(libkb.NewLoadUserArg(h.G()).WithName(arg.Voucher))
   114  	if err != nil {
   115  		return err
   116  	}
   117  	expectedVoucher := upak.Base.ToUserVersion()
   118  	myVouches, err := libkb.FetchWotVouches(mctx, libkb.FetchWotVouchesArg{}) // get vouches for me
   119  	if err != nil {
   120  		return err
   121  	}
   122  	wildcardSigID := arg.AllowEmptySigID && arg.SigID.IsNil()
   123  	var reactingVouch *keybase1.WotVouch
   124  	for _, attestation := range myVouches {
   125  		if attestation.Voucher.Eq(expectedVoucher) && (wildcardSigID || attestation.VouchProof.Eq(arg.SigID)) {
   126  			reactingVouch = &attestation
   127  			break
   128  		}
   129  	}
   130  	if reactingVouch == nil {
   131  		mctx.Debug("WotReact could not find attestation for %v '%v'", expectedVoucher, arg.SigID)
   132  		return fmt.Errorf("could not find attestation")
   133  	}
   134  
   135  	switch reactingVouch.Status {
   136  	case keybase1.WotStatusType_NONE:
   137  		return fmt.Errorf("something is wrong with this attestation; please ask %s to recreate it", arg.Voucher)
   138  	case keybase1.WotStatusType_REJECTED:
   139  		return fmt.Errorf("cannot react to an attestation that was previously rejected")
   140  	case keybase1.WotStatusType_REVOKED:
   141  		return fmt.Errorf("cannot react to an attestation that was previously revoked")
   142  	case keybase1.WotStatusType_ACCEPTED:
   143  		if arg.Reaction == keybase1.WotReactionType_ACCEPT {
   144  			return fmt.Errorf("already accepted")
   145  		}
   146  		// rejected a previously accepted vouch, which is fine
   147  	case keybase1.WotStatusType_PROPOSED:
   148  		// expected happy path
   149  	default:
   150  		return fmt.Errorf("unknown status on web-of-trust attestation: %v", reactingVouch.Status)
   151  	}
   152  
   153  	earg := &engine.WotReactArg{
   154  		Voucher:  expectedVoucher,
   155  		Proof:    reactingVouch.VouchProof,
   156  		Reaction: arg.Reaction,
   157  	}
   158  	eng := engine.NewWotReact(h.G(), earg)
   159  	return engine.RunEngine2(mctx, eng)
   160  }
   161  
   162  func (h *WebOfTrustHandler) DismissWotNotifications(ctx context.Context, arg keybase1.DismissWotNotificationsArg) (err error) {
   163  	mctx := libkb.NewMetaContext(ctx, h.G())
   164  	defer mctx.Trace("DismissWotNotifications", &err)()
   165  
   166  	return libkb.DismissWotNotifications(mctx, arg.Voucher, arg.Vouchee)
   167  }