github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/teams/hidden/loader.go (about) 1 package hidden 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/keybase/client/go/libkb" 8 "github.com/keybase/client/go/protocol/keybase1" 9 "github.com/keybase/client/go/sig3" 10 ) 11 12 const ( 13 MaxDelayInCommittingHiddenLinks = 30 * 24 * time.Hour 14 ) 15 16 // LoaderPackage contains a snapshot of the hidden team chain, used during the process of loading a team. 17 // It additionally can have new chain links loaded from the server, since it might need to be queried 18 // in the process of loading the team as if the new links were already committed to the data store. 19 type LoaderPackage struct { 20 id keybase1.TeamID 21 encKID keybase1.KID 22 encKIDGen keybase1.PerTeamKeyGeneration 23 data *keybase1.HiddenTeamChain 24 newData *keybase1.HiddenTeamChain 25 expectedPrev *keybase1.LinkTriple 26 rbks *RatchetBlindingKeySet 27 allNewRatchets map[keybase1.Seqno]keybase1.LinkTripleAndTime 28 newRatchetSet keybase1.HiddenTeamChainRatchetSet 29 role keybase1.TeamRole 30 lastCommittedSeqno keybase1.Seqno 31 disableHiddenChainData bool 32 } 33 34 // NewLoaderPackage creates a loader package that can work in the FTL of slow team loading settings. As a preliminary, 35 // it loads any stored hidden team data for the team from local storage. The getter function is used to get a recent PTK 36 // for this team, which is needed to poll the Merkle Tree endpoint when asking "does a hidden team chain exist for this team?" 37 func NewLoaderPackage(mctx libkb.MetaContext, id keybase1.TeamID, 38 getter func() (keybase1.KID, keybase1.PerTeamKeyGeneration, keybase1.TeamRole, error)) (ret *LoaderPackage, err error) { 39 encKID, gen, role, err := getter() 40 if err != nil { 41 return nil, err 42 } 43 ret = newLoaderPackage(id, encKID, gen, role) 44 err = ret.Load(mctx) 45 if err != nil { 46 return nil, err 47 } 48 return ret, nil 49 } 50 51 // NewLoaderPackageForPrecheck makes a loader package just for the purposes of prechecking a link we're about to send 52 // up to the server. It doesn't bother to load the team from storage. 53 func NewLoaderPackageForPrecheck(mctx libkb.MetaContext, id keybase1.TeamID, data *keybase1.HiddenTeamChain) *LoaderPackage { 54 return &LoaderPackage{ 55 id: id, 56 data: data, 57 } 58 } 59 60 // newLoaderPackage creates an object used to load the hidden team chain along with the 61 // slow or fast team loader. It manages internal state during the loading process. Pass an 62 // encryption KID from the main chain for authentication purposes, that we can prove to the server 63 // that we've previously seen data for this team (and therefor we're allowed to know whether or not 64 // the team has a hidden chain (but nothing more)). 65 func newLoaderPackage(id keybase1.TeamID, e keybase1.KID, g keybase1.PerTeamKeyGeneration, role keybase1.TeamRole) *LoaderPackage { 66 return &LoaderPackage{id: id, encKID: e, encKIDGen: g, role: role} 67 } 68 69 // Load in data from storage for this chain. We're going to make a deep copy so that 70 // we don't worry about mutating the object in the storage layer's memory LRU. 71 func (l *LoaderPackage) Load(mctx libkb.MetaContext) (err error) { 72 tmp, err := mctx.G().GetHiddenTeamChainManager().Load(mctx, l.id) 73 if err != nil { 74 return err 75 } 76 if tmp == nil { 77 return nil 78 } 79 cp := tmp.DeepCopy() 80 l.data = &cp 81 return err 82 } 83 84 // IsStale returns true if we got a gregor hint from the server that there is a new link and we haven't 85 // pulled it down yet from the server. 86 func (l *LoaderPackage) IsStale() bool { 87 if l.data == nil { 88 return false 89 } 90 return l.data.IsStale() 91 } 92 93 // checkPrev checks the earliest chainlink in the update against previously fetched chainlinks. 94 // It requires the prev to be there and to not clash. 95 func (l *LoaderPackage) checkPrev(mctx libkb.MetaContext, first sig3.Generic) (err error) { 96 q := first.Seqno() 97 prev := first.Prev() 98 if (q == keybase1.Seqno(1)) != (prev == nil) { 99 return NewLoaderError("bad link; seqno=%d, prev=%v (want 1 and nil or >1 and non-nil)", q, prev) 100 } 101 if q == keybase1.Seqno(1) { 102 return nil 103 } 104 if l.data == nil { 105 return NewLoaderError("didn't get prior data and update was for a chain middle") 106 } 107 link, ok := l.data.Outer[q-1] 108 if !ok { 109 return NewLoaderError("previous link wasn't found") 110 } 111 if !link.Eq(prev.Export()) { 112 return NewLoaderError("prev mismatch at %d", q) 113 } 114 115 // We check prevs again when we commit this change to the hidden team chain manager. It's not 116 // strictly required, but it seems a good safeguard against future programming bugs. So 117 // store it away here. 118 l.expectedPrev = &keybase1.LinkTriple{ 119 Seqno: q - 1, 120 LinkID: link, 121 SeqType: keybase1.SeqType_TEAM_PRIVATE_HIDDEN, 122 } 123 124 return nil 125 } 126 127 // checkExpectedHighSeqno enforces that the links we got down from the server 128 // (links) are at or surpass the sequence number ther server promised through 129 // the ratchet sets and the maxUncommittedSeqnoPromised obtained through the 130 // merkle/path api call. We look at both the loaded and the received downloaded 131 // ratchets for this check. 132 func (l *LoaderPackage) checkExpectedHighSeqno(mctx libkb.MetaContext, links []sig3.Generic, maxUncommittedSeqnoPromised keybase1.Seqno) (err error) { 133 if !l.HiddenChainDataEnabled() { 134 mctx.Debug("skipping checkExpectedHighSeqno, since we didn't ask for any data") 135 return nil 136 } 137 last := l.LastSeqno() 138 max := l.MaxRatchet() 139 if max < maxUncommittedSeqnoPromised { 140 max = maxUncommittedSeqnoPromised 141 } 142 if max <= last { 143 return nil 144 } 145 if len(links) > 0 && links[len(links)-1].Seqno() >= max { 146 return nil 147 } 148 return libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorServerWitholdingLinks, 149 "Server promised a hidden chain up to %d, but never received; is it withholding?", max) 150 } 151 152 // checkLoadedRatchet checks the given loaded ratchet against the consumed update and verifies a (seqno, linkID) match 153 // for that ratchet. 154 func (l *LoaderPackage) checkLoadedRatchet(mctx libkb.MetaContext, update *keybase1.HiddenTeamChain, ratchet keybase1.LinkTripleAndTime) (err error) { 155 q := ratchet.Triple.Seqno 156 link, ok := update.Outer[q] 157 if ok && !link.Eq(ratchet.Triple.LinkID) { 158 return NewLoaderError("update data failed to match ratchet %+v v %s", ratchet, link) 159 } 160 return nil 161 } 162 163 // checkLoadedRatchetSet checks the hidden chain update against the ratchet set that we loaded from storage before 164 // we brought the updated down from the server. It will not check against ratchets that came down with the update 165 // (in the visible chain). This works by checking the update for validity against each type of ratchet 166 // (and there are 3: self, main, and blinded tree). 167 func (l *LoaderPackage) checkLoadedRatchetSet(mctx libkb.MetaContext, update *keybase1.HiddenTeamChain) (err error) { 168 if l.data == nil { 169 return nil 170 } 171 for _, r := range l.data.RatchetSet.Flat() { 172 err = l.checkLoadedRatchet(mctx, update, r) 173 if err != nil { 174 return err 175 } 176 } 177 return nil 178 } 179 180 // CheckPTKsForDuplicates checks that the new per-team-keys don't duplicate keys we've gotten along the 181 // visible chain, via the given getter. 182 func (l *LoaderPackage) CheckPTKsForDuplicates(mctx libkb.MetaContext, getter func(g keybase1.PerTeamKeyGeneration) bool) error { 183 if l.newData == nil { 184 return nil 185 } 186 for k := range l.newData.ReaderPerTeamKeys { 187 if getter(k) { 188 return newRepeatPTKGenerationError(k, "clashes a previously-loaded visible rotation") 189 } 190 } 191 return nil 192 } 193 194 func (l *LoaderPackage) CheckNoPTK(mctx libkb.MetaContext, g keybase1.PerTeamKeyGeneration) (err error) { 195 var found bool 196 if l.newData != nil { 197 _, found = l.newData.ReaderPerTeamKeys[g] 198 } 199 if l.data != nil && !found { 200 _, found = l.data.ReaderPerTeamKeys[g] 201 } 202 if found { 203 return newRepeatPTKGenerationError(g, "clashes a previously-loaded hidden rotation") 204 } 205 return nil 206 } 207 208 func (l *LoaderPackage) UpdateTeamMetadata(encKID keybase1.KID, encKIDGen keybase1.PerTeamKeyGeneration, role keybase1.TeamRole) { 209 l.encKID = encKID 210 l.encKIDGen = encKIDGen 211 l.role = role 212 } 213 214 // Update combines the preloaded data with any downloaded updates from the server, and stores 215 // the result local to this object. 216 func (l *LoaderPackage) Update(mctx libkb.MetaContext, update []sig3.ExportJSON, maxUncommittedSeqnoPromised keybase1.Seqno) (err error) { 217 defer mctx.Trace(fmt.Sprintf("LoaderPackage#Update(%s, %d)", l.id, len(update)), &err)() 218 mctx.G().GetVDebugLog().CLogf(mctx.Ctx(), libkb.VLog1, 219 "LoaderPackage#Update pre: %s", l.data.LinkAndKeySummary()) 220 221 var data *keybase1.HiddenTeamChain 222 data, err = l.updatePrecheck(mctx, update, maxUncommittedSeqnoPromised) 223 if err != nil { 224 return err 225 } 226 err = l.mergeData(mctx, data) 227 if err != nil { 228 return err 229 } 230 231 if l.newData != nil && l.lastCommittedSeqno > l.newData.LastCommittedSeqno { 232 l.newData.LastCommittedSeqno = l.lastCommittedSeqno 233 } 234 235 // If we received a new uncommitted link. 236 if l.newData != nil && l.newData.Last > 0 { 237 if l.newData.LinkReceiptTimes == nil { 238 l.newData.LinkReceiptTimes = make(map[keybase1.Seqno]keybase1.Time) 239 } 240 if _, found := l.newData.LinkReceiptTimes[l.data.Last]; !found && l.newData.Last > l.LastCommittedSeqno() { 241 mctx.Debug("Adding seqno %v to LinkReceiptTimes", l.newData.Last) 242 l.newData.LinkReceiptTimes[l.newData.Last] = keybase1.ToTime(mctx.G().Clock().Now()) 243 } 244 } 245 246 mctx.G().GetVDebugLog().CLogf(mctx.Ctx(), libkb.VLog1, 247 "LoaderPackage#Update post: %s", l.data.LinkAndKeySummary()) 248 return nil 249 } 250 251 // checkNewLinksAgainstNewRatchtets checks a link sent down with the hidden update against the ratchets sent down 252 // with the visible team update. It makes sure they match up. 253 func (l *LoaderPackage) checkNewLinkAgainstNewRatchets(mctx libkb.MetaContext, q keybase1.Seqno, h keybase1.LinkID) (err error) { 254 if l.allNewRatchets == nil { 255 return nil 256 } 257 found, ok := l.allNewRatchets[q] 258 if !ok { 259 return nil 260 } 261 if !found.Triple.LinkID.Eq(h) { 262 return NewLoaderError("link ID at %d fails to check against ratchet: %s != %s", q, found.Triple.LinkID, h) 263 } 264 return nil 265 } 266 267 // checkNewLinksAgainstNewRatchets checks all links in the update sent down from the server against all racthets 268 // sent down in the same update. 269 func (l *LoaderPackage) checkNewLinksAgainstNewRatchets(mctx libkb.MetaContext, update *keybase1.HiddenTeamChain) error { 270 for k, v := range update.Outer { 271 err := l.checkNewLinkAgainstNewRatchets(mctx, k, v) 272 if err != nil { 273 return err 274 } 275 } 276 return nil 277 } 278 279 // VerifyOldChainLinksAreCommitted checks that uncommitted links which we previously got from the 280 // server have indeed been included in the blind tree (the server has a short 281 // grace period to do this to account for potential downtime) 282 func (l *LoaderPackage) VerifyOldChainLinksAreCommitted(mctx libkb.MetaContext, newCommittedSeqno keybase1.Seqno) error { 283 mctx.Debug("VerifyOldChainLinksAreCommitted at time %v", mctx.G().Clock().Now()) 284 if l.data == nil || l.data.LinkReceiptTimes == nil { 285 return nil 286 } 287 for s, t := range l.data.LinkReceiptTimes { 288 if s <= newCommittedSeqno { 289 continue 290 } 291 if mctx.G().Clock().Since(t.Time()) > MaxDelayInCommittingHiddenLinks { 292 return libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorOldLinkNotYetCommitted, 293 "Link for seqno %v was added %v ago and has not been included in the blind tree yet.", s, mctx.G().Clock().Since(t.Time())) 294 } 295 } 296 return nil 297 } 298 299 // updatePrecheck runs a series of cryptographic validations on the update sent down from the server, to ensure that 300 // it can be accepted and used during the team loading process. It also converts the raw export Sig3 links into a 301 // HiddenTeamChain, which can be eventually merged with the existing hidden chain state for this team. 302 func (l *LoaderPackage) updatePrecheck(mctx libkb.MetaContext, update []sig3.ExportJSON, maxUncommittedSeqnoPromised keybase1.Seqno) (ret *keybase1.HiddenTeamChain, err error) { 303 var links []sig3.Generic 304 links, err = importChain(mctx, update) 305 if err != nil { 306 return nil, err 307 } 308 309 err = sig3.CheckLinkSequence(links) 310 if err != nil { 311 return nil, err 312 } 313 314 err = l.checkExpectedHighSeqno(mctx, links, maxUncommittedSeqnoPromised) 315 if err != nil { 316 return nil, err 317 } 318 319 if len(links) == 0 { 320 mctx.Debug("short-circuiting since no update") 321 return nil, nil 322 } 323 324 err = l.checkPrev(mctx, links[0]) 325 if err != nil { 326 return nil, err 327 } 328 329 data, err := l.toHiddenTeamChain(mctx, links) 330 if err != nil { 331 return nil, err 332 } 333 334 err = l.checkLoadedRatchetSet(mctx, data) 335 if err != nil { 336 return nil, err 337 } 338 339 err = l.checkNewLinksAgainstNewRatchets(mctx, data) 340 if err != nil { 341 return nil, err 342 } 343 344 return data, nil 345 } 346 347 // lastRotator returns the last user/KID combination to have signed a rotation into this hidden team chain. 348 // Or nil if the chain is empty. 349 func (l *LoaderPackage) lastRotator(mctx libkb.MetaContext, typ keybase1.PTKType) *keybase1.Signer { 350 if l.data == nil { 351 return nil 352 } 353 last, ok := l.data.LastPerTeamKeys[typ] 354 if !ok { 355 return nil 356 } 357 inner, ok := l.data.Inner[last] 358 if !ok { 359 return nil 360 } 361 return &inner.Signer 362 } 363 364 // LastReaderKeyRotator returns a signer object that signifies the last KID/UID pair to sign 365 // a reader PTK into this chain. 366 func (l *LoaderPackage) LastReaderKeyRotator(mctx libkb.MetaContext) *keybase1.Signer { 367 return l.lastRotator(mctx, keybase1.PTKType_READER) 368 } 369 370 // mergeData takes the data from the update and merges it with the last load of this hidden team chain 371 // from local storage. The result is just in memory, not stored to disk yet. That happens in Commit(). 372 func (l *LoaderPackage) mergeData(mctx libkb.MetaContext, newData *keybase1.HiddenTeamChain) (err error) { 373 374 if newData == nil && (!l.newRatchetSet.IsEmpty() || l.lastCommittedSeqno > 0) { 375 newData = keybase1.NewHiddenTeamChain(l.id) 376 } 377 if !l.newRatchetSet.IsEmpty() { 378 newData.RatchetSet.Merge(l.newRatchetSet) 379 } 380 381 if l.lastCommittedSeqno > 0 && newData.LastCommittedSeqno < l.lastCommittedSeqno { 382 newData.LastCommittedSeqno = l.lastCommittedSeqno 383 } 384 385 l.newData = newData 386 387 if l.data == nil { 388 l.data = newData 389 return nil 390 } 391 if newData != nil { 392 _, err = l.data.Merge(*newData) 393 if err != nil { 394 return err 395 } 396 } 397 return nil 398 } 399 400 func (l *LoaderPackage) toHiddenTeamChain(mctx libkb.MetaContext, links []sig3.Generic) (ret *keybase1.HiddenTeamChain, err error) { 401 ret = keybase1.NewHiddenTeamChain(l.id) 402 ret.Public = l.id.IsPublic() 403 for _, link := range links { 404 err = populateLink(mctx, ret, link) 405 if err != nil { 406 return nil, err 407 } 408 } 409 return ret, nil 410 } 411 412 func checkUpdateAgainstSeed(mctx libkb.MetaContext, getSeed func(keybase1.PerTeamKeyGeneration) *keybase1.PerTeamSeedCheck, update keybase1.HiddenTeamChainLink) (err error) { 413 readerKey, ok := update.Ptk[keybase1.PTKType_READER] 414 if !ok { 415 // No reader key found in link, so no need to check it. 416 return nil 417 } 418 gen := readerKey.Ptk.Gen 419 check := getSeed(gen) 420 if check == nil { 421 return NewLoaderError("seed check at generation %d wasn't found", gen) 422 } 423 hash, err := check.Hash() 424 if err != nil { 425 return err 426 } 427 if readerKey.Check.Version != keybase1.PerTeamSeedCheckVersion_V1 { 428 return NewLoaderError("can only handle seed check version 1; got %d", readerKey.Check.Version) 429 } 430 if check.Version != keybase1.PerTeamSeedCheckVersion_V1 { 431 return NewLoaderError("can only handle seed check version 1; got computed check %s", check.Version) 432 } 433 if !hash.Eq(readerKey.Check) { 434 return NewLoaderError("wrong seed check at generation %d", gen) 435 } 436 return nil 437 } 438 439 func (l *LoaderPackage) CheckUpdatesAgainstSeedsWithMap(mctx libkb.MetaContext, seeds map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKeySeedItem) (err error) { 440 return l.CheckUpdatesAgainstSeeds(mctx, func(g keybase1.PerTeamKeyGeneration) *keybase1.PerTeamSeedCheck { 441 item, ok := seeds[g] 442 if !ok { 443 return nil 444 } 445 return item.Check 446 }) 447 } 448 449 // CheckUpdatesAgainstSeeds checks the update inside this loader package against unverified team seeds. It 450 // enforces equality and will error out if not. Through this check, a client can convince itself that the 451 // recent keyers knew the old keys. 452 func (l *LoaderPackage) CheckUpdatesAgainstSeeds(mctx libkb.MetaContext, f func(keybase1.PerTeamKeyGeneration) *keybase1.PerTeamSeedCheck) (err error) { 453 defer mctx.Trace("LoaderPackage#CheckUpdatesAgainstSeeds", &err)() 454 // RESTRICTEDBOTs are excluded since they do not have any seed access 455 if l.newData == nil || l.role.IsRestrictedBot() { 456 return nil 457 } 458 for _, update := range l.newData.Inner { 459 err = checkUpdateAgainstSeed(mctx, f, update) 460 if err != nil { 461 return err 462 } 463 } 464 return nil 465 } 466 467 // LastSeqno returns the last seqno when the preloaded sequence and the update are taken together. 468 func (l *LoaderPackage) LastSeqno() keybase1.Seqno { 469 if l.data == nil { 470 return keybase1.Seqno(0) 471 } 472 return l.data.Last 473 } 474 475 // LastFullSeqno returns the last seqno before the end of the chain, or before an unstubbed 476 // hole is found (as a result of FTL). 477 func (l *LoaderPackage) LastFullSeqno() keybase1.Seqno { 478 if l.data == nil { 479 return keybase1.Seqno(0) 480 } 481 return l.data.LastFullPopulateIfUnset() 482 } 483 484 // MaxRatchet returns the greatest sequence number across all ratchets in the loaded data and also 485 // in the data from the recent update from the server. 486 func (l *LoaderPackage) MaxRatchet() (ret keybase1.Seqno) { 487 if l.data != nil { 488 ret = l.data.RatchetSet.Max() 489 } 490 tmp := l.newRatchetSet.Max() 491 if tmp > ret { 492 ret = tmp 493 } 494 return ret 495 } 496 497 // LastCommittedSeqno returns the greatest sequence number which we have seen 498 // committed by the server, to prevent rollbacks to the blind tree sigchain. It 499 // does not include the seqno in the update which was recently received from the 500 // server. It returns 0 if we have never seen a non empty link committed to the 501 // blind tree before. 502 func (l *LoaderPackage) LastCommittedSeqno() (ret keybase1.Seqno) { 503 if l.newData != nil && l.newData.LastCommittedSeqno > ret { 504 ret = l.newData.LastCommittedSeqno 505 } 506 if l.lastCommittedSeqno > ret { 507 ret = l.lastCommittedSeqno 508 } 509 if l.data != nil && l.data.LastCommittedSeqno > ret { 510 ret = l.data.LastCommittedSeqno 511 } 512 return ret 513 } 514 515 func (l *LoaderPackage) SetLastCommittedSeqno(mctx libkb.MetaContext, lcs keybase1.Seqno) error { 516 last := l.LastCommittedSeqno() 517 if lcs >= last { 518 l.lastCommittedSeqno = lcs 519 return nil 520 } 521 // this should never happen, as we already test for this condition inside CheckHiddenMerklePathResponseAndAddRatchets 522 return libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorRollbackCommittedSeqno, 523 "Tries to set a LastCommittedSeqno %v smaller than the one we know about: %v", lcs, last) 524 } 525 526 func (l *LoaderPackage) CheckHiddenMerklePathResponseAndAddRatchets(mctx libkb.MetaContext, hiddenResp *libkb.MerkleHiddenResponse) (hiddenIsFresh bool, err error) { 527 528 oldHiddenTailSeqno := l.LastSeqno() 529 oldCommittedHiddenTailSeqno := l.LastCommittedSeqno() 530 lastCommittedHiddenTailSeqno := oldCommittedHiddenTailSeqno 531 532 switch hiddenResp.RespType { 533 case libkb.MerkleHiddenResponseTypeNONE: 534 return false, NewLoaderError("Logic error in CheckHiddenMerklePathResponseAndAddRatchets: should not call this function with a NONE response.") 535 case libkb.MerkleHiddenResponseTypeFLAGOFF: 536 mctx.Debug("Skipping CheckHiddenMerklePathResponseAndAddRatchets as feature flag is off") 537 return true, nil 538 case libkb.MerkleHiddenResponseTypeOK: 539 newCommittedHiddenTail := hiddenResp.CommittedHiddenTail 540 newCommittedHiddenTailSeqno := newCommittedHiddenTail.Seqno 541 lastCommittedHiddenTailSeqno = newCommittedHiddenTailSeqno 542 543 // ensure the server is self consistent in its answer 544 if hiddenResp.UncommittedSeqno < newCommittedHiddenTailSeqno { 545 return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorInconsistentUncommittedSeqno, 546 "The server claims that the lastHiddenSeqno for this team (seqno %v) is smaller than the one in the blind merkle update it sent (%v)", hiddenResp.UncommittedSeqno, newCommittedHiddenTailSeqno) 547 } 548 // prevent rollbacks in the blind tree 549 if newCommittedHiddenTailSeqno < oldCommittedHiddenTailSeqno { 550 return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorRollbackCommittedSeqno, 551 "Server rollback of the blind merkle tree leaf: we had previously seen a leaf at seqno %v, but this update contains a leaf at seqno %v", oldCommittedHiddenTailSeqno, newCommittedHiddenTailSeqno) 552 } 553 // add ratchet to ensure consistency 554 err = l.AddUnblindedRatchet(mctx, newCommittedHiddenTail, int(mctx.G().Clock().Now().Unix()), keybase1.RatchetType_BLINDED) 555 if err != nil { 556 return false, err 557 } 558 err = l.SetLastCommittedSeqno(mctx, newCommittedHiddenTailSeqno) 559 if err != nil { 560 return false, err 561 } 562 case libkb.MerkleHiddenResponseTypeABSENCEPROOF: 563 if oldCommittedHiddenTailSeqno > 0 { 564 return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorUnexpectedAbsenceProof, 565 "Server claimed (and proved) there are no committed hidden chain links in the chain, but we had previously seen a committed link with seqno %v", oldCommittedHiddenTailSeqno) 566 } 567 default: 568 return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorInvalidHiddenResponseType, 569 "Unrecognized response type: %v", hiddenResp.RespType) 570 } 571 572 if err := l.VerifyOldChainLinksAreCommitted(mctx, lastCommittedHiddenTailSeqno); err != nil { 573 return false, err 574 } 575 576 if oldHiddenTailSeqno == hiddenResp.UncommittedSeqno { 577 hiddenIsFresh = true 578 } else if oldHiddenTailSeqno < hiddenResp.UncommittedSeqno { 579 hiddenIsFresh = false 580 } else { 581 return false, libkb.NewHiddenMerkleError(libkb.HiddenMerkleErrorRollbackUncommittedSeqno, 582 "The server indicated that the last hidden link has Seqno %v, but we knew of a link with seqno %v already!", hiddenResp.UncommittedSeqno, oldHiddenTailSeqno) 583 } 584 585 return hiddenIsFresh, nil 586 } 587 588 // HasReaderPerTeamKeyAtGeneration returns true if the LoaderPackage has a sigchain entry for 589 // the PTK at the given generation. Whether in the preloaded data or the update. 590 func (l *LoaderPackage) HasReaderPerTeamKeyAtGeneration(gen keybase1.PerTeamKeyGeneration) bool { 591 // RESTRICTEDBOTs are excluded since they do not have any PTK access 592 if l.data == nil || l.role.IsRestrictedBot() { 593 return false 594 } 595 _, ok := l.data.ReaderPerTeamKeys[gen] 596 return ok 597 } 598 599 // Commit the update from the server to main HiddenTeamChain storage. 600 func (l *LoaderPackage) Commit(mctx libkb.MetaContext) error { 601 if l.newData == nil { 602 mctx.Debug("LoaderPackage#Commit: nil newData for team %s", l.id) 603 return nil 604 } 605 mctx.Debug("LoaderPackage#Commit: %s", l.newData.Summary()) 606 err := mctx.G().GetHiddenTeamChainManager().Advance(mctx, *l.newData, l.expectedPrev) 607 return err 608 } 609 610 // ChainData returns the merge of the preloaded hidden chain data and the recently downloaded chain update. 611 func (l *LoaderPackage) ChainData() *keybase1.HiddenTeamChain { 612 return l.data 613 } 614 615 // MaxReaderTeamKeyGeneration returns the highest Reader PTK generation from the preloaded and hidden 616 // data. 617 func (l *LoaderPackage) MaxReaderPerTeamKeyGeneration() keybase1.PerTeamKeyGeneration { 618 // RESTRICTEDBOTs are excluded since they do not have any PTK access 619 if l.data == nil || l.role.IsRestrictedBot() { 620 return keybase1.PerTeamKeyGeneration(0) 621 } 622 return l.data.MaxReaderPerTeamKeyGeneration() 623 } 624 625 func (l *LoaderPackage) RatchetBlindingKeySet() *RatchetBlindingKeySet { 626 return l.rbks 627 } 628 629 func (l *LoaderPackage) SetRatchetBlindingKeySet(r *RatchetBlindingKeySet) { 630 l.rbks = r 631 } 632 633 // AddRatchets calls AddRatchet on each SCTeamRatchet in v. 634 func (l *LoaderPackage) AddRatchets(mctx libkb.MetaContext, v []SCTeamRatchet, ctime int, typ keybase1.RatchetType) (err error) { 635 for _, r := range v { 636 err := l.AddRatchet(mctx, r, ctime, typ) 637 if err != nil { 638 return err 639 } 640 } 641 return nil 642 } 643 644 // AddRatchet is called whenever we pull a ratchet out of a visible team link. The first thing we'll need to 645 // do is to make sure that we can look the unblinded ratchet up using the blinding keys we got down from the 646 // server. Then we'll check the ratchets again the old (loaded) and new (downloaded) data. Finally, we'll 647 // ensure that this ratchet doesn't clash another ratchet that came down in this update. If all checks work, 648 // then add this ratchet to the set of all new ratchets, and also the max ratchet set that we're keeping locally. 649 func (l *LoaderPackage) AddRatchet(mctx libkb.MetaContext, r SCTeamRatchet, ctime int, typ keybase1.RatchetType) (err error) { 650 tail := l.rbks.Get(r) 651 if tail == nil { 652 return NewLoaderError("missing unblind for ratchet %s", r.String()) 653 } 654 return l.AddUnblindedRatchet(mctx, tail, ctime, typ) 655 } 656 657 func (l *LoaderPackage) AddUnblindedRatchet(mctx libkb.MetaContext, tail *sig3.Tail, ctime int, typ keybase1.RatchetType) (err error) { 658 ratchet := keybase1.LinkTripleAndTime{ 659 Triple: tail.Export(), 660 Time: keybase1.TimeFromSeconds(int64(ctime)), 661 } 662 err = checkRatchet(mctx, l.data, ratchet) 663 if err != nil { 664 return err 665 } 666 err = checkRatchet(mctx, l.newData, ratchet) 667 if err != nil { 668 return err 669 } 670 if l.allNewRatchets == nil { 671 l.allNewRatchets = make(map[keybase1.Seqno]keybase1.LinkTripleAndTime) 672 } 673 q := ratchet.Triple.Seqno 674 found, ok := l.allNewRatchets[q] 675 if ok && !found.Triple.LinkID.Eq(ratchet.Triple.LinkID) { 676 return NewLoaderError("ratchet for seqno %d contradicts another ratchet", q) 677 } 678 if !ok { 679 l.allNewRatchets[q] = ratchet 680 } 681 l.newRatchetSet.Add(typ, ratchet) 682 return nil 683 } 684 685 func (l *LoaderPackage) checkParentPointer(mctx libkb.MetaContext, getter func(q keybase1.Seqno) (keybase1.LinkID, bool), parentPointer keybase1.LinkTriple, fullLoad bool) (err error) { 686 q := parentPointer.Seqno 687 link, ok := getter(q) 688 switch { 689 case !ok && fullLoad: 690 return newParentPointerError(q, "link wasn't found in parent chain") 691 case !ok && !fullLoad: 692 return nil 693 } 694 if !link.Eq(parentPointer.LinkID) { 695 return newParentPointerError(q, "link ID mismatch") 696 } 697 if parentPointer.SeqType != keybase1.SeqType_SEMIPRIVATE { 698 return newParentPointerError(q, "wrong chain type") 699 } 700 return nil 701 } 702 703 // CheckParentPointersOnFullLoad looks at all of the new hidden links we got down and makes sure that they 704 // the point to loaded links in the visible chain. Because it's a full load, the pointers must land. They 705 // can dangle on FTL loads, for instance. 706 func (l *LoaderPackage) CheckParentPointersOnFullLoad(mctx libkb.MetaContext, team *keybase1.TeamData) (err error) { 707 if l.newData == nil { 708 return nil 709 } 710 getter := func(q keybase1.Seqno) (ret keybase1.LinkID, found bool) { 711 if team == nil { 712 return ret, false 713 } 714 ret, found = team.Chain.LinkIDs[q] 715 return ret, found 716 } 717 for _, v := range l.newData.Inner { 718 if err := l.checkParentPointer(mctx, getter, v.ParentChain, true /* full load */); err != nil { 719 return err 720 } 721 } 722 return nil 723 } 724 725 // CheckParentPointersOnFastLoad looks at all of the new hidden links we got down and makes sure that they 726 // the point to loaded links in the visible chain. Because it's a fast load, the pointers can dangle. 727 func (l *LoaderPackage) CheckParentPointersOnFastLoad(mctx libkb.MetaContext, team *keybase1.FastTeamData) (err error) { 728 if l.newData == nil { 729 return nil 730 } 731 getter := func(q keybase1.Seqno) (ret keybase1.LinkID, found bool) { 732 if team == nil { 733 return ret, false 734 } 735 ret, found = team.Chain.LinkIDs[q] 736 return ret, found 737 } 738 for _, v := range l.newData.Inner { 739 if err := l.checkParentPointer(mctx, getter, v.ParentChain, false /* full load */); err != nil { 740 return err 741 } 742 } 743 return nil 744 } 745 746 // DisableHiddenChainData tells the LoaderPackage to disable reading and consuming 747 // of hidden chain data. On if we are a subteam reader, or if we are feature-flagged off 748 func (l *LoaderPackage) DisableHiddenChainData() { 749 l.disableHiddenChainData = true 750 } 751 752 // HiddenChainDataEnabled is true if we are doing a full hidden chain load (and off if we're skipping 753 // due to the above). 754 func (l *LoaderPackage) HiddenChainDataEnabled() bool { 755 return !l.disableHiddenChainData 756 }