github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/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 }