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  }