github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/wot_vouch.go (about) 1 // Copyright 2020 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package engine 5 6 import ( 7 "errors" 8 "fmt" 9 10 "github.com/keybase/client/go/libkb" 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 jsonw "github.com/keybase/go-jsonw" 13 ) 14 15 type WotVouchArg struct { 16 Vouchee keybase1.UserVersion 17 Confidence keybase1.Confidence 18 VouchText string 19 } 20 21 // WotVouch is an engine. 22 type WotVouch struct { 23 arg *WotVouchArg 24 libkb.Contextified 25 } 26 27 // NewWotVouch creates a WotVouch engine. 28 func NewWotVouch(g *libkb.GlobalContext, arg *WotVouchArg) *WotVouch { 29 return &WotVouch{ 30 arg: arg, 31 Contextified: libkb.NewContextified(g), 32 } 33 } 34 35 // Name is the unique engine name. 36 func (e *WotVouch) Name() string { 37 return "WotVouch" 38 } 39 40 // GetPrereqs returns the engine prereqs. 41 func (e *WotVouch) Prereqs() Prereqs { 42 return Prereqs{Device: true} 43 } 44 45 // RequiredUIs returns the required UIs. 46 func (e *WotVouch) RequiredUIs() []libkb.UIKind { 47 return []libkb.UIKind{} 48 } 49 50 // SubConsumers returns the other UI consumers for this engine. 51 func (e *WotVouch) SubConsumers() []libkb.UIConsumer { 52 return nil 53 } 54 55 func getSigIDToRevoke(mctx libkb.MetaContext, vouchee *libkb.User) (toRevoke *keybase1.SigID, err error) { 56 voucherUsername := mctx.ActiveDevice().Username(mctx).String() 57 vouches, err := libkb.FetchWotVouches(mctx, libkb.FetchWotVouchesArg{Voucher: voucherUsername, Vouchee: vouchee.GetName()}) 58 if err != nil { 59 return nil, err 60 } 61 var unrevokedVouches []keybase1.WotVouch 62 for _, vouch := range vouches { 63 if vouch.Status != keybase1.WotStatusType_REVOKED { 64 unrevokedVouches = append(unrevokedVouches, vouch) 65 } 66 } 67 switch { 68 case len(unrevokedVouches) > 1: 69 return nil, fmt.Errorf("there should be at most one existing vouch to revoke, but there are %d", len(unrevokedVouches)) 70 case len(unrevokedVouches) == 1: 71 return &unrevokedVouches[0].VouchProof, nil 72 default: 73 return nil, nil 74 } 75 } 76 77 // Run starts the engine. 78 func (e *WotVouch) Run(mctx libkb.MetaContext) error { 79 ctx := mctx.Ctx() 80 g := mctx.G() 81 luArg := libkb.NewLoadUserArgWithMetaContext(mctx).WithUID(e.arg.Vouchee.Uid).WithStubMode(libkb.StubModeUnstubbed) 82 them, err := libkb.LoadUser(luArg) 83 if err != nil { 84 return err 85 } 86 87 if them.GetCurrentEldestSeqno() != e.arg.Vouchee.EldestSeqno { 88 mctx.Debug("eldest seqno mismatch: loaded %v != %v caller", them.GetCurrentEldestSeqno(), e.arg.Vouchee.EldestSeqno) 89 return errors.New("vouchee has reset, make sure you still know them") 90 } 91 92 if e.arg.Confidence.UsernameVerifiedVia == "" { 93 return errors.New("missing UsernameVerifiedVia") 94 } 95 if _, found := keybase1.UsernameVerificationTypeMap[string(e.arg.Confidence.UsernameVerifiedVia)]; !found { 96 return fmt.Errorf("unrecognized UsernameVerificationTypeMap value '%v'", e.arg.Confidence.UsernameVerifiedVia) 97 } 98 99 if e.arg.Confidence.UsernameVerifiedVia == keybase1.UsernameVerificationType_PROOFS { 100 if len(e.arg.Confidence.Proofs) == 0 { 101 return errors.New("vouching with proofs requires proofs list") 102 } 103 } else { 104 if len(e.arg.Confidence.Proofs) > 0 { 105 return errors.New("vouching with proof list requires proof type") 106 } 107 } 108 109 statement := jsonw.NewDictionary() 110 if err := statement.SetKey("user", them.ToWotStatement()); err != nil { 111 return err 112 } 113 confidenceJw, err := jsonw.WrapperFromObject(e.arg.Confidence) 114 if err != nil { 115 return err 116 } 117 if err := statement.SetKey("confidence", confidenceJw); err != nil { 118 return err 119 } 120 if err := statement.SetKey("vouch_text", jsonw.NewString(e.arg.VouchText)); err != nil { 121 return err 122 } 123 expansions, sum, err := libkb.EmbedExpansionObj(statement) 124 if err != nil { 125 return err 126 } 127 128 signingKey, err := mctx.G().ActiveDevice.SigningKey() 129 if err != nil { 130 return err 131 } 132 133 sigIDToRevoke, err := getSigIDToRevoke(mctx, them) 134 if err != nil { 135 return err 136 } 137 var lease *libkb.Lease 138 var merkleRoot *libkb.MerkleRoot 139 if sigIDToRevoke != nil { 140 lease, merkleRoot, err = libkb.RequestDowngradeLeaseBySigIDs(ctx, g, []keybase1.SigID{*sigIDToRevoke}) 141 if err != nil { 142 return err 143 } 144 defer func() { 145 // not sure if this is necessary or not 146 err := libkb.CancelDowngradeLease(ctx, g, lease.LeaseID) 147 if err != nil { 148 g.Log.CWarningf(ctx, "Failed to cancel downgrade lease: %s", err.Error()) 149 } 150 }() 151 } 152 153 sigVersion := libkb.KeybaseSignatureV2 154 var inner []byte 155 var sig string 156 157 // ForcePoll is required. 158 var proof *libkb.ProofMetadataRes 159 var linkID libkb.LinkID 160 err = mctx.G().GetFullSelfer().WithSelfForcePoll(ctx, func(me *libkb.User) (err error) { 161 if me.GetUID() == e.arg.Vouchee.Uid { 162 return libkb.InvalidArgumentError{Msg: "can't vouch for yourself"} 163 } 164 proof, err = me.WotVouchProof(mctx, signingKey, sigVersion, sum, merkleRoot, sigIDToRevoke) 165 if err != nil { 166 return err 167 } 168 inner, err = proof.J.Marshal() 169 if err != nil { 170 return err 171 } 172 sig, _, linkID, err = libkb.MakeSig( 173 mctx, 174 signingKey, 175 libkb.LinkTypeWotVouch, 176 inner, 177 libkb.SigHasRevokes(sigIDToRevoke != nil), 178 keybase1.SeqType_PUBLIC, 179 libkb.SigIgnoreIfUnsupported(true), 180 me, 181 sigVersion, 182 ) 183 184 return err 185 }) 186 if err != nil { 187 return err 188 } 189 190 item := libkb.SigMultiItem{ 191 Sig: sig, 192 SigningKID: signingKey.GetKID(), 193 Type: string(libkb.LinkTypeWotVouch), 194 SeqType: keybase1.SeqType_PUBLIC, 195 SigInner: string(inner), 196 Version: sigVersion, 197 Expansions: expansions, 198 } 199 200 payload := make(libkb.JSONPayload) 201 payload["sigs"] = []interface{}{item} 202 if lease != nil { 203 payload["downgrade_lease_id"] = lease.LeaseID 204 } 205 if _, err := e.G().API.PostJSON(mctx, libkb.APIArg{ 206 Endpoint: "sig/multi", 207 SessionType: libkb.APISessionTypeREQUIRED, 208 JSONPayload: payload, 209 }); err != nil { 210 return err 211 } 212 213 me, err := libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(mctx)) 214 if err != nil { 215 return err 216 } 217 err = libkb.MerkleCheckPostedUserSig(mctx, me.GetUID(), proof.Seqno, linkID) 218 if err != nil { 219 return err 220 } 221 222 voucherUsername := mctx.ActiveDevice().Username(mctx).String() 223 mctx.G().NotifyRouter.HandleWebOfTrustChanged(voucherUsername) 224 return libkb.DismissWotNotifications(mctx, voucherUsername, them.GetName()) 225 }