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

     1  package libkb
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
     8  )
     9  
    10  // leafSlot is a simple accessor that takes the given leaf, and the private/public type,
    11  // and returns the corresponding object.
    12  func leafSlot(leaf *MerkleGenericLeaf, typ keybase1.SeqType) *MerkleTriple {
    13  	if leaf == nil {
    14  		return nil
    15  	}
    16  	switch typ {
    17  	case keybase1.SeqType_PUBLIC:
    18  		return leaf.Public
    19  	case keybase1.SeqType_SEMIPRIVATE:
    20  		return leaf.Private
    21  	default:
    22  		return nil
    23  	}
    24  }
    25  
    26  // merkleSearchComparator is a comparator used to determine if the given leaf/root has overshot
    27  // the mark, or no. If overshoot, then return a true, otherwrise return a false. And error will
    28  // abort the attempt
    29  type merkleSearchComparator func(leaf *MerkleGenericLeaf, root *MerkleRoot) (bool, error)
    30  
    31  // lookup the max merkle root seqno from the server, or use a cached version if
    32  // possible (and it's less than a minute old).
    33  func lookupMaxMerkleSeqno(m MetaContext) (ret keybase1.Seqno, err error) {
    34  	defer m.Trace("lookupMaxMerkleSeqno", &err)()
    35  	cli := m.G().GetMerkleClient()
    36  	mr, err := cli.FetchRootFromServer(m, time.Minute)
    37  	if err != nil {
    38  		return ret, err
    39  	}
    40  	lastp := mr.Seqno()
    41  	if lastp == nil {
    42  		return ret, MerkleClientError{"unexpected nil max merkle seqno", merkleErrorBadRoot}
    43  	}
    44  	return *lastp, nil
    45  }
    46  
    47  // findFirstLeafWithComparer finds the first chronological leaf in the merkle tree for which
    48  // comparer(leaf, root) is true, where (leaf, root) is an historical (leaf,root) pair for this
    49  // id. It's assumed that that false values are all earlier in history than the true values. We're
    50  // looking for the point of transition. Start this search from the given prevRootSeqno, not from merkle seqno 1.
    51  // The algorithm is to jump forward, in exponentially larger jumps, until we find a root that has a leaf
    52  // that makes comparer true. Then, to binary search to find the exact [a,b] pair in which a makes the
    53  // comparer false, and b makes it true. The leaf and root that correspond to b are returned.
    54  func findFirstLeafWithComparer(m MetaContext, id keybase1.UserOrTeamID, comparator merkleSearchComparator, prevRootSeqno keybase1.Seqno) (leaf *MerkleGenericLeaf, root *MerkleRoot, err error) {
    55  	defer m.Trace(fmt.Sprintf("findFirstLeafWithComparer(%s,%d)", id, prevRootSeqno), &err)()
    56  
    57  	cli := m.G().GetMerkleClient()
    58  
    59  	if !cli.CanExamineHistoricalRoot(m, prevRootSeqno) {
    60  		return nil, nil, MerkleClientError{"can't operate on old merkle sequence number", merkleErrorOldTree}
    61  	}
    62  
    63  	low := prevRootSeqno
    64  	last, err := lookupMaxMerkleSeqno(m)
    65  	if err != nil {
    66  		return nil, nil, err
    67  	}
    68  	m.Debug("found max merkle root: %d", last)
    69  	var hi keybase1.Seqno
    70  	inc := keybase1.Seqno(1)
    71  
    72  	// First bump the hi pointer up to a merkle root that overshoots (or is equal to)
    73  	// the request chainSeqno. Don't go any higher than the last known Merkle seqno.
    74  	var found bool
    75  	var final bool
    76  	for hi = low + 1; !found && !final; hi += inc {
    77  		if hi > last {
    78  			hi = last
    79  			final = true
    80  		}
    81  		m.Debug("FFLWC: Expontential forward jump: trying %d", hi)
    82  		leaf, root, err = cli.LookupLeafAtSeqno(m, id, hi)
    83  		if err != nil {
    84  			return nil, nil, err
    85  		}
    86  		found, err = comparator(leaf, root)
    87  		if err != nil {
    88  			return nil, nil, err
    89  		}
    90  		if found || final {
    91  			// Still make sure we `break` so we don't wind up incrementing
    92  			// hi if we don't have to.
    93  			break
    94  		}
    95  		inc *= 2
    96  	}
    97  
    98  	if !found {
    99  		return nil, nil, MerkleClientError{fmt.Sprintf("given link can't be found even as high as Merkle Root %d", hi), merkleErrorNotFound}
   100  	}
   101  
   102  	m.Debug("FFLWC: Stopped at hi bookend; binary searching in [%d,%d]", low, hi)
   103  
   104  	// Now binary search between prevRootSeqno and the hi we just got
   105  	// to find the exact transition. Note that if we never enter this loop,
   106  	// we'll still have set leaf and root in the above loop. Interestingly, this is
   107  	// the most common case, since for most signatures, the next merkle root will
   108  	// contain the signature. In those cases, we don't even go into this loop
   109  	// (since hi = low + 1).
   110  	for hi-low > 1 {
   111  		mid := (hi + low) / 2
   112  		tmpLeaf, tmpRoot, err := cli.LookupLeafAtSeqno(m, id, mid)
   113  		if err != nil {
   114  			return nil, nil, err
   115  		}
   116  		found, err := comparator(tmpLeaf, tmpRoot)
   117  		if err != nil {
   118  			return nil, nil, err
   119  		}
   120  		if found {
   121  			hi = mid
   122  			leaf = tmpLeaf
   123  			root = tmpRoot
   124  		} else {
   125  			low = mid
   126  		}
   127  		m.Debug("FFLWC: Binary search: after update range is [%d,%d]", low, hi)
   128  	}
   129  	m.Debug("FFLWC: settling at final seqno: %d", hi)
   130  
   131  	return leaf, root, nil
   132  }
   133  
   134  // FindNextMerkleRootAfterRevoke loads the user for the given UID, and find the
   135  // next merkle root after the given key revocation happens. It uses the
   136  // parameter arg.Prev to figure out where to start looking and then keeps
   137  // searching forward until finding a leaf that matches arg.Loc.
   138  func FindNextMerkleRootAfterRevoke(m MetaContext, arg keybase1.FindNextMerkleRootAfterRevokeArg) (res keybase1.NextMerkleRootRes, err error) {
   139  
   140  	defer m.Trace(fmt.Sprintf("FindNextMerkleRootAfterRevoke(%+v)", arg), &err)()
   141  
   142  	var u *User
   143  	u, err = LoadUser(NewLoadUserArgWithMetaContext(m).WithUID(arg.Uid).WithPublicKeyOptional())
   144  	if err != nil {
   145  		return res, err
   146  	}
   147  
   148  	comparer := func(leaf *MerkleGenericLeaf, root *MerkleRoot) (bool, error) {
   149  		slot := leafSlot(leaf, keybase1.SeqType_PUBLIC)
   150  		if slot == nil {
   151  			return false, MerkleClientError{fmt.Sprintf("leaf at root %d returned with nil public part", *root.Seqno()), merkleErrorBadLeaf}
   152  		}
   153  		m.Debug("Comprator at Merkle root %d: found chain location is %d; searching for %d", *root.Seqno(), slot.Seqno, arg.Loc.Seqno)
   154  		return (slot.Seqno >= arg.Loc.Seqno), nil
   155  	}
   156  
   157  	leaf, root, err := findFirstLeafWithComparer(m, arg.Uid.AsUserOrTeam(), comparer, arg.Prev.Seqno)
   158  	if err != nil {
   159  		return res, err
   160  	}
   161  	if leaf == nil || root == nil {
   162  		return res, MerkleClientError{"no suitable leaf found", merkleErrorNoUpdates}
   163  	}
   164  	sigID := u.GetSigIDFromSeqno(leaf.Public.Seqno)
   165  	if sigID.IsNil() {
   166  		return res, MerkleClientError{fmt.Sprintf("unknown seqno in sigchain: %d", arg.Loc.Seqno), merkleErrorBadSeqno}
   167  	}
   168  	if !sigID.StripSuffix().Eq(leaf.Public.SigID) {
   169  		return res, MerkleClientError{fmt.Sprintf("sigID sent down by server didn't match: %s != %s", sigID.String(), string(leaf.Public.SigID)), merkleErrorBadSigID}
   170  	}
   171  	res.Res = &keybase1.MerkleRootV2{
   172  		HashMeta: root.HashMeta(),
   173  		Seqno:    *root.Seqno(),
   174  	}
   175  	m.Debug("res.Res: %+v", *res.Res)
   176  	return res, nil
   177  }
   178  
   179  func FindNextMerkleRootAfterReset(m MetaContext, arg keybase1.FindNextMerkleRootAfterResetArg) (res keybase1.NextMerkleRootRes, err error) {
   180  	defer m.Trace(fmt.Sprintf("FindNextMerkleRootAfterReset(%+v)", arg), &err)()
   181  
   182  	comparer := func(leaf *MerkleGenericLeaf, root *MerkleRoot) (bool, error) {
   183  		user := leaf.userExtras
   184  		if user == nil {
   185  			return false, MerkleClientError{fmt.Sprintf("for root %d, expected a user leaf, didn't get one", *root.Seqno()), merkleErrorBadLeaf}
   186  		}
   187  		if user.resets == nil {
   188  			return false, nil
   189  		}
   190  		return (user.resets.chainTail.Seqno >= arg.ResetSeqno), nil
   191  	}
   192  	leaf, root, err := findFirstLeafWithComparer(m, arg.Uid.AsUserOrTeam(), comparer, arg.Prev.Seqno)
   193  	if err != nil {
   194  		return res, err
   195  	}
   196  	if leaf == nil || root == nil {
   197  		return res, MerkleClientError{"no suitable leaf found", merkleErrorNoUpdates}
   198  	}
   199  	res.Res = &keybase1.MerkleRootV2{
   200  		HashMeta: root.HashMeta(),
   201  		Seqno:    *root.Seqno(),
   202  	}
   203  	m.Debug("res.Res: %+v", *res.Res)
   204  	return res, nil
   205  }
   206  
   207  func FindNextMerkleRootAfterTeamRemoval(m MetaContext, arg keybase1.FindNextMerkleRootAfterTeamRemovalArg) (res keybase1.NextMerkleRootRes, err error) {
   208  	defer m.Trace(fmt.Sprintf("FindNextMerkleRootAfterTeamRemoval(%+v)", arg), &err)()
   209  	comparer := func(leaf *MerkleGenericLeaf, root *MerkleRoot) (bool, error) {
   210  		var trip *MerkleTriple
   211  		if arg.IsPublic {
   212  			trip = leaf.Public
   213  		} else {
   214  			trip = leaf.Private
   215  		}
   216  		if trip == nil {
   217  			return false, MerkleClientError{fmt.Sprintf("No leaf found for team %v", arg.Team), merkleErrorNotFound}
   218  		}
   219  		return (trip.Seqno >= arg.TeamSigchainSeqno), nil
   220  	}
   221  	leaf, root, err := findFirstLeafWithComparer(m, arg.Team.AsUserOrTeam(), comparer, arg.Prev.Seqno)
   222  	if err != nil {
   223  		return res, err
   224  	}
   225  	if leaf == nil || root == nil {
   226  		return res, MerkleClientError{"no suitable leaf found", merkleErrorNoUpdates}
   227  	}
   228  	res.Res = &keybase1.MerkleRootV2{
   229  		HashMeta: root.HashMeta(),
   230  		Seqno:    *root.Seqno(),
   231  	}
   232  	m.Debug("res.Res: %+v", *res.Res)
   233  	return res, nil
   234  }
   235  
   236  func VerifyMerkleRootAndKBFS(m MetaContext, arg keybase1.VerifyMerkleRootAndKBFSArg) (err error) {
   237  
   238  	defer m.Trace(fmt.Sprintf("VerifyMerkleRootAndKBFS(%+v)", arg), &err)()
   239  
   240  	var mr *MerkleRoot
   241  	mr, err = m.G().GetMerkleClient().LookupRootAtSeqno(m, arg.Root.Seqno)
   242  	if err != nil {
   243  		return nil
   244  	}
   245  	if mr == nil {
   246  		return MerkleClientError{"no merkle root found", merkleErrorNotFound}
   247  	}
   248  
   249  	if !mr.HashMeta().Eq(arg.Root.HashMeta) {
   250  		return MerkleClientError{"wrong hash meta", merkleErrorHashMeta}
   251  	}
   252  
   253  	var received keybase1.KBFSRootHash
   254  	switch arg.ExpectedKBFSRoot.TreeID {
   255  	case keybase1.MerkleTreeID_KBFS_PUBLIC:
   256  		received = mr.payload.unpacked.Body.Kbfs.Public.Root
   257  	case keybase1.MerkleTreeID_KBFS_PRIVATE:
   258  		received = mr.payload.unpacked.Body.Kbfs.Private.Root
   259  	case keybase1.MerkleTreeID_KBFS_PRIVATETEAM:
   260  		received = mr.payload.unpacked.Body.Kbfs.PrivateTeam.Root
   261  	default:
   262  		return MerkleClientError{"unknown KBFS tree ID", merkleErrorKBFSBadTree}
   263  	}
   264  
   265  	if received == nil || arg.ExpectedKBFSRoot.Root == nil {
   266  		if received != nil || arg.ExpectedKBFSRoot.Root != nil {
   267  			return MerkleClientError{"KBFS hash mismatch; nil hash", merkleErrorKBFSMismatch}
   268  		}
   269  		return nil
   270  	}
   271  	if !received.Eq(arg.ExpectedKBFSRoot.Root) {
   272  		return MerkleClientError{"KBFS hash mismatch", merkleErrorKBFSMismatch}
   273  	}
   274  
   275  	return nil
   276  }
   277  
   278  // Verify that the given link has been posted to the merkle tree.
   279  // Used to detect a malicious server silently dropping sigchain link posts.
   280  func MerkleCheckPostedUserSig(mctx MetaContext, uid keybase1.UID,
   281  	seqno keybase1.Seqno, linkID LinkID) (err error) {
   282  	defer mctx.Trace(fmt.Sprintf("MerkleCheckPostedUserSig(%v, %v, %v)", uid, seqno, linkID.String()), &err)()
   283  	for _, forcePoll := range []bool{false, true} {
   284  		upak, _, err := mctx.G().GetUPAKLoader().LoadV2(
   285  			NewLoadUserArgWithMetaContext(mctx).WithPublicKeyOptional().
   286  				WithUID(uid).WithForcePoll(forcePoll))
   287  		if err != nil {
   288  			return err
   289  		}
   290  		if foundLinkID, found := upak.SeqnoLinkIDs[seqno]; found {
   291  			if foundLinkID.Eq(linkID.Export()) {
   292  				return nil
   293  			}
   294  		}
   295  	}
   296  	return fmt.Errorf("sigchain link not found at seqno %v", seqno)
   297  }