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