github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/loader2.go (about) 1 package teams 2 3 import ( 4 "crypto/sha256" 5 "encoding/base64" 6 "errors" 7 "fmt" 8 9 "golang.org/x/crypto/nacl/secretbox" 10 "golang.org/x/net/context" 11 12 "github.com/keybase/client/go/libkb" 13 "github.com/keybase/client/go/protocol/keybase1" 14 "github.com/keybase/client/go/teams/hidden" 15 "github.com/keybase/go-codec/codec" 16 ) 17 18 // loader2.go contains methods on TeamLoader. 19 // It would be normal for them to be in loader.go but: 20 // These functions do not call any functions in loader.go except for load2. 21 // They are here so that the files can be more manageable in size and 22 // people can work on loader.go and loader2.go simultaneously with less conflict. 23 24 // If links are needed in full that are stubbed in state, go out and get them from the server. 25 // Does not ask for any links above state's seqno, those will be fetched by getNewLinksFromServer. 26 func (l *TeamLoader) fillInStubbedLinks(mctx libkb.MetaContext, 27 me keybase1.UserVersion, teamID keybase1.TeamID, state *keybase1.TeamData, 28 needSeqnos []keybase1.Seqno, readSubteamID keybase1.TeamID, 29 proofSet *proofSetT, parentChildOperations []*parentChildOperation, lkc *loadKeyCache) ( 30 *keybase1.TeamData, *proofSetT, []*parentChildOperation, error) { 31 32 upperLimit := keybase1.Seqno(0) 33 if state != nil { 34 upperLimit = state.Chain.LastSeqno 35 } 36 37 // seqnos needed from the server 38 var requestSeqnos []keybase1.Seqno 39 for _, seqno := range needSeqnos { 40 linkIsAlreadyFilled := TeamSigChainState{inner: state.Chain}.IsLinkFilled(seqno) 41 if seqno <= upperLimit && !linkIsAlreadyFilled { 42 requestSeqnos = append(requestSeqnos, seqno) 43 } 44 } 45 if len(requestSeqnos) == 0 { 46 // early out 47 return state, proofSet, parentChildOperations, nil 48 } 49 50 teamUpdate, err := l.world.getLinksFromServer(mctx.Ctx(), state.Chain.Id, requestSeqnos, &readSubteamID) 51 if err != nil { 52 return state, proofSet, parentChildOperations, err 53 } 54 newLinks, err := teamUpdate.unpackLinks(mctx) 55 if err != nil { 56 return state, proofSet, parentChildOperations, err 57 } 58 59 parentsCache := make(parentChainCache) 60 for _, link := range newLinks { 61 if link.isStubbed() { 62 return state, proofSet, parentChildOperations, NewStubbedErrorWithNote( 63 link, "filling stubbed link") 64 } 65 66 var signer *SignerX 67 var fullVerifyCutoff keybase1.Seqno // Always fullVerify when inflating. No reasoning has been done on whether it could be skipped. 68 signer, err = l.verifyLink(mctx.Ctx(), teamID, state, me, link, fullVerifyCutoff, readSubteamID, 69 proofSet, lkc, parentsCache) 70 if err != nil { 71 return state, proofSet, parentChildOperations, err 72 } 73 74 if signer == nil || !signer.signer.Uid.Exists() { 75 return state, proofSet, parentChildOperations, fmt.Errorf("blank signer for full link: %v", signer) 76 } 77 78 state, err = l.inflateLink(mctx.Ctx(), state, link, *signer, me) 79 if err != nil { 80 return state, proofSet, parentChildOperations, err 81 } 82 83 if l.isParentChildOperation(mctx.Ctx(), link) { 84 pco, err := l.toParentChildOperation(mctx.Ctx(), link) 85 if err != nil { 86 return state, proofSet, parentChildOperations, err 87 } 88 parentChildOperations = append(parentChildOperations, pco) 89 } 90 } 91 92 return state, proofSet, parentChildOperations, nil 93 94 } 95 96 type getLinksLows struct { 97 // Latest seqno on file 98 Seqno keybase1.Seqno 99 // Latest PTK generation secret we have 100 PerTeamKey keybase1.PerTeamKeyGeneration 101 // Latest RKM semi-secret we have 102 ReaderKeyMask keybase1.PerTeamKeyGeneration 103 // Latest hidden chain seqno on file 104 HiddenChainSeqno keybase1.Seqno 105 // Ratcheted chain tail 106 HiddenChainRatchet keybase1.Seqno 107 } 108 109 // checkStubbed checks if it's OK if a link is stubbed. 110 func (l *TeamLoader) checkStubbed(ctx context.Context, arg load2ArgT, link *ChainLinkUnpacked) error { 111 if !link.isStubbed() { 112 return nil 113 } 114 if l.seqnosContains(arg.needSeqnos, link.Seqno()) { 115 return NewStubbedErrorWithNote(link, "Need seqno") 116 } 117 if arg.needAdmin || !link.outerLink.LinkType.TeamAllowStubWithAdminFlag(arg.needAdmin) { 118 return NewStubbedErrorWithNote(link, "Need admin privilege for this action") 119 } 120 return nil 121 } 122 123 func (l *TeamLoader) loadUserAndKeyFromLinkInner(ctx context.Context, 124 inner SCChainLinkPayload, lkc *loadKeyCache) ( 125 signerUV keybase1.UserVersion, key *keybase1.PublicKeyV2NaCl, linkMap linkMapT, err error) { 126 if !ShouldSuppressLogging(ctx) { 127 defer l.G().CTrace(ctx, fmt.Sprintf("TeamLoader#loadUserForSigVerification(%d)", int(inner.Seqno)), &err)() 128 } 129 keySection := inner.Body.Key 130 if keySection == nil { 131 return signerUV, nil, nil, libkb.NoUIDError{} 132 } 133 uid := keySection.UID 134 kid := keySection.KID 135 signerUV, key, linkMap, err = l.world.loadKeyV2(ctx, uid, kid, lkc) 136 if err != nil { 137 return signerUV, nil, nil, err 138 } 139 return signerUV, key, linkMap, nil 140 } 141 142 // Get the UV from a link but using server-trust and without verifying anything. 143 func (l *TeamLoader) loadUserAndKeyFromLinkInnerNoVerify(ctx context.Context, 144 link *ChainLinkUnpacked) (signerUV keybase1.UserVersion, err error) { 145 if !ShouldSuppressLogging(ctx) { 146 defer l.G().CTrace(ctx, fmt.Sprintf("TeamLoader#loadUserAndKeyFromLinkInnerNoVerify(%d)", int(link.inner.Seqno)), &err)() 147 } 148 keySection := link.inner.Body.Key 149 if keySection == nil { 150 return signerUV, libkb.NoUIDError{} 151 } 152 // Use the UID from the link body and EldestSeqno from the server-trust API response. 153 if link.source.EldestSeqno == 0 { 154 // We should never hit this case 155 return signerUV, fmt.Errorf("missing server hint for team sigchain link signer") 156 } 157 return NewUserVersion(keySection.UID, link.source.EldestSeqno), nil 158 } 159 160 func (l *TeamLoader) verifySignatureAndExtractKID(ctx context.Context, outer libkb.OuterLinkV2WithMetadata) (keybase1.KID, error) { 161 return outer.Verify(l.G().Log) 162 } 163 164 // These exceptional sigchain links are not checked dynamically. We assert that they are good. 165 var whitelistedTeamLinkSigsForKeyInUserSigchain = []keybase1.SigID{ 166 // For the privacy of the users involved the issue is described only vaguely here. 167 // See CORE-8233 for more details. 168 // This team had a rotate_key link signed seconds before the revocation of the key that signed the link. 169 // Due to a bug the signing device was allowed to be revoked with a signature that pointed to a merkle 170 // root prior to the team link signature. This makes it impossible for the client to independently 171 // verify that the team link was signed before the device was revoked. But it was, it's all good. 172 "e8279d7c73b8defab299094b73800262239e5a03812040ed381cc613a3db515622", 173 174 // See https://github.com/keybase/client/issues/17573; a server bug allowed a rotate after a revoke, which 175 // has been fixed in CORE-10942. 176 "070e6d737607109ba17d1d43419d950cde6d206b66c555c837566913a31ca59122", 177 178 // See https://github.com/keybase/client/issues/20503; a server bug allowed a team leave to interleave 179 // with a downgrade lease acquisition for a key revoke on a slow connection. The acquisition should have 180 // been blocked until the merkle tree reflected the leave, but the acquistion actually happened before the 181 // team leave transation was committed to the DB. The fix on the server is to check for leases before and 182 // after the team change is commited (in the same transaction). We were previously only checking before. 183 // It has been fixed in Y2K-891. 184 "c641d1246493cf04ec2c6141acdb569a457c02d577b392d4eb1872118c563c2822", 185 } 186 187 // These exceptional sigchain links are not checked dynamically. We assert that they are good. 188 var whitelistedTeamLinkSigsForAdminPermissionDemote = []keybase1.SigID{ 189 // A server bug allowed a change_membership to be posted that demoted an adminship 190 // that had just been referenced by a rotate_key that was still settling. 191 // Timeline: Before the ':' are merkle seqnos minus a base offset. 192 // 5: referenced by rotate_key. 193 // 6: referenced by change_membership. 194 // 7: rotate_key first appeared in the merkle tree. 195 // 9: change_membership first appeared in the merkle tree. 196 // The problem is that change_membership did not reference a merkle tree that included rotate_key. 197 // So the client can't prove (in the way it does) that rotate_key occurred before change_membership. 198 // See PICNIC-654 for more details. 199 "69cea033758d152c9736596f0a7e544444ec1944843172692db01be1c6fb6ee622", 200 } 201 202 func (l *TeamLoader) addProofsForKeyInUserSigchain(ctx context.Context, teamID keybase1.TeamID, link *ChainLinkUnpacked, uid keybase1.UID, key *keybase1.PublicKeyV2NaCl, userLinkMap linkMapT, proofSet *proofSetT) { 203 for _, okSigID := range whitelistedTeamLinkSigsForKeyInUserSigchain { 204 if link.SigID().Eq(okSigID) { 205 // This proof is whitelisted, so don't check it. 206 l.G().Log.CDebugf(ctx, "addProofsForKeyInUserSigchain: skipping exceptional link: %v", link.SigID()) 207 return 208 } 209 } 210 211 event1Link := newProofTerm(teamID.AsUserOrTeam(), link.SignatureMetadata(), nil) 212 event2Revoke := key.Base.Revocation 213 if event2Revoke != nil { 214 proofSet.AddNeededHappensBeforeProof(ctx, event1Link, newProofTerm(uid.AsUserOrTeam(), *event2Revoke, userLinkMap), "team link before user key revocation") 215 } 216 } 217 218 // Verify aspects of a link: 219 // - Signature must match the outer link 220 // - Signature must match the inner link if not stubbed 221 // - Was signed by a key valid for the user at the time of signing 222 // - Was signed by a user with permissions to make the link at the time of signing 223 // - Checks outer-inner match 224 // Some checks are deferred as entries in the returned proofSet 225 // Does not: 226 // - Apply the link nor modify state 227 // - Check the rest of the format of the inner link 228 // Returns the signer, or nil if the link was stubbed 229 func (l *TeamLoader) verifyLink(ctx context.Context, 230 teamID keybase1.TeamID, state *keybase1.TeamData, me keybase1.UserVersion, link *ChainLinkUnpacked, 231 fullVerifyCutoff keybase1.Seqno, readSubteamID keybase1.TeamID, proofSet *proofSetT, lkc *loadKeyCache, 232 parentsCache parentChainCache) (*SignerX, error) { 233 ctx, tbs := l.G().CTimeBuckets(ctx) 234 defer tbs.Record("TeamLoader.verifyLink")() 235 236 if link.isStubbed() { 237 return nil, nil 238 } 239 240 err := link.AssertInnerOuterMatch() 241 if err != nil { 242 return nil, err 243 } 244 245 if !teamID.Eq(link.innerTeamID) { 246 return nil, fmt.Errorf("team ID mismatch: %s != %s", teamID, link.innerTeamID) 247 } 248 249 signedByKID, err := l.verifySignatureAndExtractKID(ctx, *link.outerLink) 250 if err != nil { 251 return nil, err 252 } 253 254 // FullVerify all links except for `team.leave` links for which there is 255 // an admin link later in the chain. 256 // Such a link has effectively been verified for us by the admin who signed on top. 257 // This trick can be used on `team.leave` links because they do not add admins. 258 fullVerify := (link.LinkType() != libkb.SigchainV2TypeTeamLeave) || 259 (link.Seqno() >= fullVerifyCutoff) || 260 (link.source.EldestSeqno == 0) 261 262 var signerUV keybase1.UserVersion 263 if fullVerify { 264 signerUV, err = l.loadUserAndKeyFromLinkInnerAndVerify(ctx, teamID, state, link, signedByKID, proofSet, lkc) 265 if err != nil { 266 return nil, err 267 } 268 } else { 269 signerUV, err = l.loadUserAndKeyFromLinkInnerNoVerify(ctx, link) 270 if err != nil { 271 return nil, err 272 } 273 } 274 275 signer := SignerX{signer: signerUV} 276 277 // For a root team link, or a subteam_head, there is no reason to check adminship 278 // or writership (or readership) for the team. 279 if state == nil { 280 return &signer, nil 281 } 282 283 minRole := link.outerLink.LinkType.RequiresAtLeastRole() 284 linkType := link.outerLink.LinkType 285 // Note: If minRole is OWNER it will be treated as ADMIN here (weaker check). 286 if !ShouldSuppressLogging(ctx) { 287 l.G().Log.CDebugf(ctx, "verifyLink: %v minRole:%v", linkType, minRole) 288 } 289 290 switch minRole { 291 case keybase1.TeamRole_NONE: 292 // Anyone can make this link. These didn't exist at the time. 293 return &signer, nil 294 case keybase1.TeamRole_RESTRICTEDBOT: 295 err = l.verifyExplicitPermission(ctx, state, link, signerUV, keybase1.TeamRole_RESTRICTEDBOT) 296 if err == nil { 297 return &signer, err 298 } 299 if !ShouldSuppressLogging(ctx) { 300 l.G().Log.CDebugf(ctx, "verifyLink: %v not a %v: %v", linkType, keybase1.TeamRole_RESTRICTEDBOT, err) 301 } 302 // Fall through to a higher role check 303 fallthrough 304 case keybase1.TeamRole_BOT: 305 err = l.verifyExplicitPermission(ctx, state, link, signerUV, keybase1.TeamRole_BOT) 306 if err == nil { 307 return &signer, err 308 } 309 if !ShouldSuppressLogging(ctx) { 310 l.G().Log.CDebugf(ctx, "verifyLink: %v not a %v: %v", linkType, keybase1.TeamRole_BOT, err) 311 } 312 // Fall through to a higher role check 313 fallthrough 314 case keybase1.TeamRole_READER: 315 err = l.verifyExplicitPermission(ctx, state, link, signerUV, keybase1.TeamRole_READER) 316 if err == nil { 317 return &signer, err 318 } 319 if !ShouldSuppressLogging(ctx) { 320 l.G().Log.CDebugf(ctx, "verifyLink: %v not a %v: %v", linkType, keybase1.TeamRole_READER, err) 321 } 322 // Fall through to a higher role check 323 fallthrough 324 case keybase1.TeamRole_WRITER: 325 err = l.verifyExplicitPermission(ctx, state, link, signerUV, keybase1.TeamRole_WRITER) 326 if err == nil { 327 return &signer, err 328 } 329 if !ShouldSuppressLogging(ctx) { 330 l.G().Log.CDebugf(ctx, "verifyLink: %v not a %v: %v", linkType, keybase1.TeamRole_WRITER, err) 331 } 332 // Fall through to a higher role check 333 fallthrough 334 case keybase1.TeamRole_OWNER, keybase1.TeamRole_ADMIN: 335 // Check for admin permissions if they are not an on-chain reader/writer 336 // because they might be an implicit admin. 337 // Reassigns signer, might set implicitAdmin. 338 signer, err = l.verifyAdminPermissions(ctx, state, me, link, readSubteamID, signerUV, proofSet, parentsCache) 339 if !ShouldSuppressLogging(ctx) { 340 l.G().Log.CDebugf(ctx, "verifyLink: not a %v: %v", minRole, err) 341 } 342 return &signer, err 343 default: 344 return nil, fmt.Errorf("unrecognized role %v required for link", minRole) 345 } 346 } 347 348 func (l *TeamLoader) loadUserAndKeyFromLinkInnerAndVerify(ctx context.Context, teamID keybase1.TeamID, state *keybase1.TeamData, 349 link *ChainLinkUnpacked, signedByKID keybase1.KID, proofSet *proofSetT, lkc *loadKeyCache) (signer keybase1.UserVersion, err error) { 350 signer, key, linkMap, err := l.loadUserAndKeyFromLinkInner(ctx, *link.inner, lkc) 351 if err != nil { 352 return keybase1.UserVersion{}, err 353 } 354 if !signedByKID.Equal(key.Base.Kid) { 355 return keybase1.UserVersion{}, libkb.NewWrongKidError(signedByKID, key.Base.Kid) 356 } 357 l.addProofsForKeyInUserSigchain(ctx, teamID, link, signer.Uid, key, linkMap, proofSet) 358 return signer, nil 359 } 360 361 // Verify that the user had the explicit on-chain role just before this `link`. 362 func (l *TeamLoader) verifyExplicitPermission(ctx context.Context, state *keybase1.TeamData, 363 link *ChainLinkUnpacked, uv keybase1.UserVersion, atOrAbove keybase1.TeamRole) error { 364 return (TeamSigChainState{inner: state.Chain}).AssertWasRoleOrAboveAt(uv, atOrAbove, link.SigChainLocation().Sub1()) 365 } 366 367 type parentChainCache map[keybase1.TeamID]*keybase1.TeamData 368 369 // Does not return a full TeamData because it might get a subteam-reader version. 370 func (l *TeamLoader) walkUpToAdmin( 371 ctx context.Context, team *keybase1.TeamData, me keybase1.UserVersion, readSubteamID keybase1.TeamID, 372 uv keybase1.UserVersion, admin SCTeamAdmin, parentsCache parentChainCache) (*TeamSigChainState, error) { 373 374 target, err := admin.TeamID.ToTeamID() 375 if err != nil { 376 return nil, err 377 } 378 379 if t, ok := parentsCache[target]; ok { 380 return &TeamSigChainState{inner: t.Chain}, nil 381 } 382 383 for team != nil && !team.Chain.Id.Eq(target) { 384 parent := team.Chain.ParentID 385 if parent == nil { 386 return nil, NewAdminNotFoundError(admin) 387 } 388 if t, ok := parentsCache[*parent]; ok { 389 team = t 390 continue 391 } 392 arg := load2ArgT{ 393 teamID: *parent, 394 reason: "walkUpToAdmin", 395 me: me, 396 // Get the latest so that the linkmap is up to date for the proof order checker. 397 // But do it only once (hence the `parentsCache`) per team. 398 forceRepoll: true, 399 readSubteamID: &readSubteamID, 400 } 401 if target.Eq(*parent) { 402 arg.needSeqnos = []keybase1.Seqno{admin.Seqno} 403 } 404 load2Res, err := l.load2(ctx, arg) 405 if err != nil { 406 return nil, err 407 } 408 team = &load2Res.team 409 parentsCache[*parent] = team 410 } 411 if team == nil { 412 return nil, fmt.Errorf("teamloader fault: nil team after admin walk") 413 } 414 return &TeamSigChainState{inner: team.Chain}, nil 415 } 416 417 func (l *TeamLoader) addProofsForAdminPermission(ctx context.Context, teamID keybase1.TeamID, link *ChainLinkUnpacked, bookends proofTermBookends, proofSet *proofSetT) { 418 event1Promote := bookends.left 419 event2Link := newProofTerm(teamID.AsUserOrTeam(), link.SignatureMetadata(), nil) 420 event3Demote := bookends.right 421 proofSet.AddNeededHappensBeforeProof(ctx, event1Promote, event2Link, "became admin before team link") 422 if event3Demote != nil { 423 for _, okSigID := range whitelistedTeamLinkSigsForAdminPermissionDemote { 424 if link.SigID().Eq(okSigID) { 425 // This proof is whitelisted, so don't check it. 426 l.G().Log.CDebugf(ctx, "addProofsForAdminPermission: [demote] skipping exceptional link: %v", link.SigID()) 427 return 428 } 429 } 430 proofSet.AddNeededHappensBeforeProof(ctx, event2Link, *event3Demote, "team link before adminship demotion") 431 } 432 } 433 434 // Verify that a user has admin permissions. 435 // Because this uses the proofSet, if it is called may return success and fail later. 436 func (l *TeamLoader) verifyAdminPermissions(ctx context.Context, 437 state *keybase1.TeamData, me keybase1.UserVersion, link *ChainLinkUnpacked, readSubteamID keybase1.TeamID, 438 uv keybase1.UserVersion, proofSet *proofSetT, parentsCache parentChainCache) (SignerX, error) { 439 440 signer := SignerX{signer: uv} 441 explicitAdmin := link.inner.TeamAdmin() 442 teamChain := TeamSigChainState{inner: state.Chain} 443 444 // In the simple case, we don't ask for explicit adminship, so we have to be admins of 445 // the current chain at or before the signature in question. 446 if explicitAdmin == nil { 447 err := teamChain.AssertWasAdminAt(uv, link.SigChainLocation().Sub1()) 448 return signer, err 449 } 450 451 // The more complicated case is that there's an explicit admin permission given, perhaps 452 // of a parent team. 453 adminTeam, err := l.walkUpToAdmin(ctx, state, me, readSubteamID, uv, *explicitAdmin, parentsCache) 454 if err != nil { 455 return signer, err 456 } 457 adminBookends, err := adminTeam.assertBecameAdminAt(uv, explicitAdmin.SigChainLocation()) 458 if err != nil { 459 return signer, err 460 } 461 462 // This was an implicit admin action if the team from which admin-power was derived (adminTeam) 463 // is not the link's team (state). 464 if !adminTeam.GetID().Eq(teamChain.GetID()) { 465 signer.implicitAdmin = true 466 } 467 468 l.addProofsForAdminPermission(ctx, state.Chain.Id, link, adminBookends, proofSet) 469 return signer, nil 470 } 471 472 // Whether the chain link is of a (child-half) type 473 // that affects a parent and child chain in lockstep. 474 // So far these events: subteam create, and subteam rename 475 // Technically subteam delete is one of these too, but we don't 476 // bother because the subteam is rendered inaccessible. 477 func (l *TeamLoader) isParentChildOperation(ctx context.Context, 478 link *ChainLinkUnpacked) bool { 479 480 switch link.LinkType() { 481 case libkb.SigchainV2TypeTeamSubteamHead, libkb.SigchainV2TypeTeamRenameUpPointer: 482 return true 483 default: 484 return false 485 } 486 } 487 488 func (l *TeamLoader) toParentChildOperation(ctx context.Context, 489 link *ChainLinkUnpacked) (*parentChildOperation, error) { 490 491 if !l.isParentChildOperation(ctx, link) { 492 return nil, fmt.Errorf("link is not a parent-child operation: (seqno:%v, type:%v)", 493 link.Seqno(), link.LinkType()) 494 } 495 496 if link.isStubbed() { 497 return nil, fmt.Errorf("child half of parent-child operation cannot be stubbed: (seqno:%v, type:%v)", 498 link.Seqno(), link.LinkType()) 499 } 500 501 switch link.LinkType() { 502 case libkb.SigchainV2TypeTeamSubteamHead, libkb.SigchainV2TypeTeamRenameUpPointer: 503 if link.inner.Body.Team == nil { 504 return nil, fmt.Errorf("bad parent-child operation missing team section: (seqno:%v, type:%v)", 505 link.Seqno(), link.LinkType()) 506 } 507 if link.inner.Body.Team.Parent == nil { 508 return nil, fmt.Errorf("parent-child operation missing team parent: (seqno:%v, type:%v)", 509 link.Seqno(), link.LinkType()) 510 } 511 parentSeqno := link.inner.Body.Team.Parent.Seqno 512 if parentSeqno < 1 { 513 return nil, fmt.Errorf("bad parent-child up seqno: %v", parentSeqno) 514 } 515 if link.inner.Body.Team.Name == nil { 516 return nil, fmt.Errorf("parent-child operation %v missing new name", link.LinkType()) 517 } 518 newName, err := keybase1.TeamNameFromString((string)(*link.inner.Body.Team.Name)) 519 if err != nil { 520 return nil, fmt.Errorf("parent-child operation %v has invalid new name: %v", 521 link.LinkType(), *link.inner.Body.Team.Name) 522 } 523 return &parentChildOperation{ 524 parentSeqno: parentSeqno, 525 linkType: link.LinkType(), 526 newName: newName, 527 }, nil 528 default: 529 return nil, fmt.Errorf("unsupported parent-child operation: %v", link.LinkType()) 530 } 531 532 } 533 534 // Apply a new link to the sigchain state. 535 // `state` is moved into this function. There must exist no live references into it from now on. 536 // `signer` may be nil iff link is stubbed. 537 func (l *TeamLoader) applyNewLink(ctx context.Context, 538 state *keybase1.TeamData, hiddenChainState *keybase1.HiddenTeamChain, link *ChainLinkUnpacked, 539 signer *SignerX, me keybase1.UserVersion) (*keybase1.TeamData, error) { 540 ctx, tbs := l.G().CTimeBuckets(ctx) 541 defer tbs.Record("TeamLoader.applyNewLink")() 542 543 if !ShouldSuppressLogging(ctx) { 544 l.G().Log.CDebugf(ctx, "TeamLoader applying link seqno:%v", link.Seqno()) 545 } 546 547 var chainState *TeamSigChainState 548 var newState *keybase1.TeamData 549 if state == nil { 550 newState = &keybase1.TeamData{ 551 // Name is left blank until calculateName updates it. 552 // It shall not be blank by the time it is returned from load2. 553 Subversion: 2, // see storage/std.go for more info on this 554 Name: keybase1.TeamName{}, 555 PerTeamKeySeedsUnverified: make(map[keybase1.PerTeamKeyGeneration]keybase1.PerTeamKeySeedItem), 556 ReaderKeyMasks: make(map[keybase1.TeamApplication]map[keybase1.PerTeamKeyGeneration]keybase1.MaskB64), 557 } 558 } else { 559 chainState = &TeamSigChainState{inner: state.Chain, hidden: hiddenChainState} 560 newState = state 561 state = nil 562 } 563 564 newChainState, err := AppendChainLink(ctx, l.G(), me, chainState, link, signer) 565 if err != nil { 566 return nil, err 567 } 568 newState.Chain = newChainState.inner 569 570 return newState, nil 571 } 572 573 // Inflate a link that was stubbed with its non-stubbed data. 574 func (l *TeamLoader) inflateLink(ctx context.Context, 575 state *keybase1.TeamData, link *ChainLinkUnpacked, 576 signer SignerX, me keybase1.UserVersion) ( 577 *keybase1.TeamData, error) { 578 579 l.G().Log.CDebugf(ctx, "TeamLoader inflating link seqno:%v", link.Seqno()) 580 581 if state == nil { 582 // The only reason state would be nil is if this is link 1. 583 // But link 1 can't be stubbed. 584 return nil, NewInflateErrorWithNote(link, "no prior state") 585 } 586 587 newState := state.DeepCopy() // Clone the state and chain so that our parameters don't get consumed. 588 newChainState, err := InflateLink(ctx, l.G(), me, TeamSigChainState{inner: newState.Chain}, link, signer) 589 if err != nil { 590 return nil, err 591 } 592 newState.Chain = newChainState.inner 593 594 return &newState, nil 595 } 596 597 // Check that the parent-child operations appear in the parent sigchains. 598 func (l *TeamLoader) checkParentChildOperations(ctx context.Context, 599 me keybase1.UserVersion, loadingTeamID keybase1.TeamID, parentID *keybase1.TeamID, readSubteamID keybase1.TeamID, 600 parentChildOperations []*parentChildOperation, proofSet *proofSetT) error { 601 602 if len(parentChildOperations) == 0 { 603 return nil 604 } 605 if parentID == nil { 606 return fmt.Errorf("cannot check parent-child operations with no parent") 607 } 608 609 var needParentSeqnos []keybase1.Seqno 610 for _, pco := range parentChildOperations { 611 needParentSeqnos = append(needParentSeqnos, pco.parentSeqno) 612 } 613 614 parent, err := l.load2(ctx, load2ArgT{ 615 teamID: *parentID, 616 617 reason: "checkParentChildOperations-parent", 618 619 needAdmin: false, 620 needKeyGeneration: 0, 621 needApplicationsAtGenerations: nil, 622 needApplicationsAtGenerationsWithKBFS: nil, 623 wantMembers: nil, 624 wantMembersRole: keybase1.TeamRole_NONE, 625 forceFullReload: false, 626 forceRepoll: false, 627 staleOK: true, // stale is fine, as long as get those seqnos. 628 skipSeedCheck: true, 629 auditMode: keybase1.AuditMode_SKIP, 630 631 needSeqnos: needParentSeqnos, 632 readSubteamID: &readSubteamID, 633 634 me: me, 635 }) 636 if err != nil { 637 return fmt.Errorf("error loading parent: %v", err) 638 } 639 640 parentChain := TeamSigChainState{inner: parent.team.Chain} 641 642 for _, pco := range parentChildOperations { 643 err = l.checkOneParentChildOperation(ctx, pco, loadingTeamID, &parentChain) 644 if err != nil { 645 return err 646 } 647 } 648 649 // Give a more up-to-date linkmap to the ordering checker for the parent. 650 // Without this it could fail if the parent is new. 651 // Because the team linkmap in the proof objects is stale. 652 proofSet.SetTeamLinkMap(ctx, parentChain.inner.Id, parentChain.inner.LinkIDs) 653 654 return nil 655 } 656 657 func (l *TeamLoader) checkOneParentChildOperation(ctx context.Context, 658 pco *parentChildOperation, teamID keybase1.TeamID, parent *TeamSigChainState) error { 659 660 switch pco.linkType { 661 case libkb.SigchainV2TypeTeamSubteamHead: 662 return parent.SubteamRenameOccurred(teamID, pco.newName, pco.parentSeqno) 663 case libkb.SigchainV2TypeTeamRenameUpPointer: 664 return parent.SubteamRenameOccurred(teamID, pco.newName, pco.parentSeqno) 665 } 666 return fmt.Errorf("unrecognized parent-child operation could not be checked: %v", pco.linkType) 667 } 668 669 // Check all the proofs and ordering constraints in proofSet 670 func (l *TeamLoader) checkProofs(ctx context.Context, 671 state *keybase1.TeamData, proofSet *proofSetT) error { 672 673 if state == nil { 674 return fmt.Errorf("teamloader fault: nil team for proof ordering check") 675 } 676 // Give the most up-to-date linkmap to the ordering checker. 677 // Without this it would fail in some cases when the team is on the left. 678 // Because the team linkmap in the proof objects is stale. 679 proofSet.SetTeamLinkMap(ctx, state.Chain.Id, state.Chain.LinkIDs) 680 if !proofSet.checkRequired() { 681 return nil 682 } 683 return proofSet.check(ctx, l.world, teamEnv.ProofSetParallel) 684 } 685 686 func (l *TeamLoader) unboxKBFSCryptKeys(ctx context.Context, key keybase1.TeamApplicationKey, 687 keysetHash keybase1.TeamEncryptedKBFSKeysetHash, encryptedKeyset string) ([]keybase1.CryptKey, error) { 688 689 // Check hash 690 sbytes := sha256.Sum256([]byte(encryptedKeyset)) 691 if !keysetHash.SecureEqual(keybase1.TeamEncryptedKBFSKeysetHashFromBytes(sbytes[:])) { 692 return nil, errors.New("encrypted TLF upgrade does not match sigchain hash") 693 } 694 695 // Decode 696 packed, err := base64.StdEncoding.DecodeString(encryptedKeyset) 697 if err != nil { 698 return nil, err 699 } 700 var keysetRecord keybase1.TeamEncryptedKBFSKeyset 701 mh := codec.MsgpackHandle{WriteExt: true} 702 decoder := codec.NewDecoderBytes(packed, &mh) 703 if err = decoder.Decode(&keysetRecord); err != nil { 704 return nil, err 705 } 706 707 // Decrypt 708 var encKey [libkb.NaclSecretBoxKeySize]byte = key.Material() 709 var nonce [libkb.NaclDHNonceSize]byte 710 if len(keysetRecord.N) != libkb.NaclDHNonceSize { 711 return nil, libkb.DecryptBadNonceError{} 712 } 713 copy(nonce[:], keysetRecord.N) 714 plain, ok := secretbox.Open(nil, keysetRecord.E, &nonce, &encKey) 715 if !ok { 716 return nil, libkb.DecryptOpenError{} 717 } 718 719 // Decode again 720 var cryptKeys []keybase1.CryptKey 721 decoder = codec.NewDecoderBytes(plain, &mh) 722 if err = decoder.Decode(&cryptKeys); err != nil { 723 return nil, err 724 } 725 726 return cryptKeys, nil 727 } 728 729 // AddKBFSCryptKeys mutates `state` 730 func (l *TeamLoader) addKBFSCryptKeys(mctx libkb.MetaContext, team Teamer, upgrades []keybase1.TeamGetLegacyTLFUpgrade) error { 731 m := make(map[keybase1.TeamApplication][]keybase1.CryptKey) 732 for _, upgrade := range upgrades { 733 key, err := ApplicationKeyAtGeneration(mctx, team, upgrade.AppType, upgrade.TeamGeneration) 734 if err != nil { 735 return err 736 } 737 738 chainInfo, ok := team.MainChain().Chain.TlfLegacyUpgrade[upgrade.AppType] 739 if !ok { 740 return errors.New("legacy tlf upgrade payload present without chain link") 741 } 742 if chainInfo.TeamGeneration != upgrade.TeamGeneration { 743 return fmt.Errorf("legacy tlf upgrade team generation mismatch: %d != %d", 744 chainInfo.TeamGeneration, upgrade.TeamGeneration) 745 } 746 747 cryptKeys, err := l.unboxKBFSCryptKeys(mctx.Ctx(), key, chainInfo.KeysetHash, upgrade.EncryptedKeyset) 748 if err != nil { 749 return err 750 } 751 if chainInfo.LegacyGeneration != cryptKeys[len(cryptKeys)-1].KeyGeneration { 752 return fmt.Errorf("legacy tlf upgrade legacy generation mismatch: %d != %d", 753 chainInfo.LegacyGeneration, cryptKeys[len(cryptKeys)-1].KeyGeneration) 754 } 755 756 m[upgrade.AppType] = cryptKeys 757 } 758 team.MainChain().TlfCryptKeys = m 759 return nil 760 } 761 762 // Add data to the state that is not included in the sigchain: 763 // - per team keys 764 // - reader key masks 765 // Checks that the off-chain data ends up exactly in sync with the chain, generation-wise. 766 // Does _not_ check that keys match the sigchain. 767 // Mutates `state` 768 func (l *TeamLoader) addSecrets(mctx libkb.MetaContext, 769 team Teamer, me keybase1.UserVersion, box *TeamBox, prevs map[keybase1.PerTeamKeyGeneration]prevKeySealedEncoded, 770 readerKeyMasks []keybase1.ReaderKeyMask) error { 771 772 state := team.MainChain() 773 774 latestReceivedGen, seeds, err := l.unboxPerTeamSecrets(mctx, box, prevs) 775 if err != nil { 776 return err 777 } 778 mctx.Debug("TeamLoader#addSecrets at %d", latestReceivedGen) 779 780 // Earliest generation received. 781 earliestReceivedGen := latestReceivedGen - keybase1.PerTeamKeyGeneration(len(seeds)-1) 782 783 stateWrapper := newTeamSigChainState(team) 784 785 // Latest generation from the sigchain or the hidden chain... 786 latestChainGen := stateWrapper.GetLatestGeneration() 787 788 mctx.Debug("TeamLoader.addSecrets: received:%v->%v nseeds:%v nprevs:%v", 789 earliestReceivedGen, latestReceivedGen, len(seeds), len(prevs)) 790 791 // Check that each key matches the chain. 792 for i, seed := range seeds { 793 gen := int(latestReceivedGen) + i + 1 - len(seeds) 794 if gen < 1 { 795 return fmt.Errorf("gen < 1") 796 } 797 798 ptkGen := keybase1.PerTeamKeyGeneration(gen) 799 800 chainKey, err := stateWrapper.GetPerTeamKeyAtGeneration(ptkGen) 801 if err != nil { 802 return err 803 } 804 805 // Add it to the snapshot 806 state.PerTeamKeySeedsUnverified[ptkGen] = keybase1.PerTeamKeySeedItem{ 807 Seed: seed, 808 Generation: ptkGen, 809 Seqno: chainKey.Seqno, 810 } 811 } 812 813 // Make sure there is not a gap between the latest local key and the earliest received key. 814 if earliestReceivedGen > keybase1.PerTeamKeyGeneration(1) { 815 // We should have the seed for the generation preceeding the earliest received. 816 checkGen := earliestReceivedGen - 1 817 if _, ok := state.PerTeamKeySeedsUnverified[earliestReceivedGen-1]; !ok { 818 return fmt.Errorf("gap in per-team-keys: latestRecvd:%v earliestRecvd:%v missing:%v", 819 latestReceivedGen, earliestReceivedGen, checkGen) 820 } 821 } 822 823 role, err := stateWrapper.GetUserRole(me) 824 if err != nil { 825 role = keybase1.TeamRole_NONE 826 } 827 if role.IsBotOrAbove() { 828 // Insert all reader key masks 829 // Then scan to make sure there are no gaps in generations and no missing application masks. 830 checkMaskGens := make(map[keybase1.PerTeamKeyGeneration]bool) 831 for _, rkm := range readerKeyMasks { 832 if rkm.Generation < 1 { 833 return fmt.Errorf("reader key mask has generation: %v < 0", rkm.Generation) 834 } 835 if _, ok := state.ReaderKeyMasks[rkm.Application]; !ok { 836 state.ReaderKeyMasks[rkm.Application] = make( 837 map[keybase1.PerTeamKeyGeneration]keybase1.MaskB64) 838 } 839 state.ReaderKeyMasks[rkm.Application][rkm.Generation] = rkm.Mask 840 841 checkMaskGens[rkm.Generation] = true 842 if rkm.Generation > 1 { 843 // Check for the previous rkm to make sure there are no gaps 844 checkMaskGens[rkm.Generation-1] = true 845 } 846 } 847 mctx.Debug("TeamLoader.addSecrets: loop1") 848 // Check that we are all the way up to date 849 checkMaskGens[latestChainGen] = true 850 for gen := range checkMaskGens { 851 err = l.checkReaderKeyMaskCoverage(mctx, state, gen) 852 if err != nil { 853 return err 854 } 855 } 856 mctx.Debug("TeamLoader.addSecrets: loop2") 857 } else { 858 // Discard all cached reader key masks if we are not an explicit member of the team. 859 state.ReaderKeyMasks = make(map[keybase1.TeamApplication]map[keybase1.PerTeamKeyGeneration]keybase1.MaskB64) 860 861 // Also we shouldn't have gotten any from the server. 862 if len(readerKeyMasks) > 0 { 863 mctx.Warning("TeamLoader got %v reader-key-masks but not an explicit member", 864 len(readerKeyMasks)) 865 } 866 } 867 return nil 868 } 869 870 // Check that the RKMs for a generation are covered for all apps. 871 func (l *TeamLoader) checkReaderKeyMaskCoverage(mctx libkb.MetaContext, 872 state *keybase1.TeamData, gen keybase1.PerTeamKeyGeneration) error { 873 874 for _, app := range keybase1.TeamApplicationMap { 875 switch app { 876 case keybase1.TeamApplication_STELLAR_RELAY, keybase1.TeamApplication_KVSTORE: 877 // TODO CORE-7718 Allow clients to be missing these RKMs for now. 878 // Will need a team cache bust to repair. 879 continue 880 } 881 if _, ok := state.ReaderKeyMasks[app]; !ok { 882 return NewMissingReaderKeyMaskError(gen, app) 883 } 884 if _, ok := state.ReaderKeyMasks[app][gen]; !ok { 885 return NewMissingReaderKeyMaskError(gen, app) 886 } 887 } 888 889 return nil 890 } 891 892 // Unbox per team keys 893 // Does not check that the keys match the chain 894 // TODO: return the signer and have the caller check it. Not critical because the public half is checked anyway. 895 // Returns the generation of the box (the greatest generation), 896 // and a list of the seeds in ascending generation order. 897 func (l *TeamLoader) unboxPerTeamSecrets(mctx libkb.MetaContext, 898 box *TeamBox, prevs map[keybase1.PerTeamKeyGeneration]prevKeySealedEncoded) (keybase1.PerTeamKeyGeneration, []keybase1.PerTeamKeySeed, error) { 899 900 return unboxPerTeamSecrets(mctx, l.world, box, prevs) 901 } 902 903 func unboxPerTeamSecrets(m libkb.MetaContext, world LoaderContext, box *TeamBox, prevs map[keybase1.PerTeamKeyGeneration]prevKeySealedEncoded) (keybase1.PerTeamKeyGeneration, []keybase1.PerTeamKeySeed, error) { 904 905 if box == nil { 906 return 0, nil, fmt.Errorf("no key box from server") 907 } 908 909 userKey, err := world.perUserEncryptionKey(m.Ctx(), box.PerUserKeySeqno) 910 911 if err != nil { 912 return 0, nil, err 913 } 914 secret1, err := box.Open(userKey) 915 if err != nil { 916 return 0, nil, fmt.Errorf("opening key box: %v", err) 917 } 918 919 // Secrets starts as descending 920 secrets := []keybase1.PerTeamKeySeed{secret1} 921 922 // The generation to work on opening 923 openGeneration := box.Generation - keybase1.PerTeamKeyGeneration(1) 924 925 // Walk down generations until 926 // - the map is exhausted 927 // - if malformed, the map has a gap 928 // - reach generation 0 929 for { 930 if int(openGeneration) == 0 || int(openGeneration) < 0 { 931 break 932 } 933 // Prevs is keyed by the generation that can decrypt, not the generation contained. 934 prev, ok := prevs[openGeneration+1] 935 if !ok { 936 break 937 } 938 secret, err := decryptPrevSingle(m.Ctx(), prev, secrets[len(secrets)-1]) 939 if err != nil { 940 return box.Generation, nil, fmt.Errorf("opening prev gen %v: %v", openGeneration, err) 941 } 942 secrets = append(secrets, *secret) 943 openGeneration-- 944 } 945 946 // Reverse the list 947 // After this secrets is ascending 948 // https://github.com/golang/go/wiki/SliceTricks#reversing 949 for i := len(secrets)/2 - 1; i >= 0; i-- { 950 // opp is the index of the opposite element 951 opp := len(secrets) - 1 - i 952 secrets[i], secrets[opp] = secrets[opp], secrets[i] 953 } 954 955 return box.Generation, secrets, nil 956 } 957 958 // Whether the snapshot has fully loaded, non-stubbed, all of the links. 959 func (l *TeamLoader) checkNeededSeqnos(ctx context.Context, 960 state *keybase1.TeamData, needSeqnos []keybase1.Seqno) error { 961 962 if len(needSeqnos) == 0 { 963 return nil 964 } 965 if state == nil { 966 return fmt.Errorf("nil team does not contain needed seqnos") 967 } 968 969 for _, seqno := range needSeqnos { 970 if (TeamSigChainState{inner: state.Chain}).HasStubbedSeqno(seqno) { 971 return fmt.Errorf("needed seqno is stubbed: %v", seqno) 972 } 973 } 974 return nil 975 } 976 977 // Calculates the latest name of the team. 978 // The last part will be as up to date as the sigchain in state. 979 // The mid-team parts can be as old as the cache time, unless staleOK is false in which case they will be fetched. 980 func (l *TeamLoader) calculateName(ctx context.Context, 981 state *keybase1.TeamData, me keybase1.UserVersion, readSubteamID keybase1.TeamID, staleOK bool) (newName keybase1.TeamName, err error) { 982 983 chain := TeamSigChainState{inner: state.Chain} 984 if !chain.IsSubteam() { 985 return chain.inner.RootAncestor, nil 986 } 987 988 // Load the parent. The parent load will recalculate its own name, 989 // so this name recalculation is recursive. 990 parent, err := l.load2(ctx, load2ArgT{ 991 teamID: *chain.GetParentID(), 992 reason: "calculateName", 993 staleOK: staleOK, 994 readSubteamID: &readSubteamID, 995 me: me, 996 }) 997 if err != nil { 998 return newName, err 999 } 1000 1001 // Swap out the parent name as the base of this name. 1002 // Check that the root ancestor name and depth still match the subteam chain. 1003 1004 newName, err = parent.team.Name.Append(string(chain.LatestLastNamePart())) 1005 if err != nil { 1006 return newName, fmt.Errorf("invalid new subteam name: %v", err) 1007 } 1008 1009 if !newName.RootAncestorName().Eq(chain.inner.RootAncestor) { 1010 return newName, fmt.Errorf("subteam changed root ancestor: %v -> %v", 1011 chain.inner.RootAncestor, newName.RootAncestorName()) 1012 } 1013 1014 if newName.Depth() != chain.inner.NameDepth { 1015 return newName, fmt.Errorf("subteam changed depth: %v -> %v", chain.inner.NameDepth, newName.Depth()) 1016 } 1017 1018 return newName, nil 1019 } 1020 1021 // computeSeedChecks looks at the PerTeamKeySeedsUnverified for the given team and adds the 1022 // PerTeamSeedChecks to the sequence. We make the assumption that, potentially, all such links are 1023 // null because it's a legacy team. OR only the new links are null since they were just added. 1024 // In either case, after this function runs, all seeds get seed checks computed. 1025 func (l *TeamLoader) computeSeedChecks(ctx context.Context, state *keybase1.TeamData) (err error) { 1026 mctx := libkb.NewMetaContext(ctx, l.G()) 1027 defer mctx.Trace(fmt.Sprintf("TeamLoader#computeSeedChecks(%s)", state.ID()), &err)() 1028 1029 latestChainGen := keybase1.PerTeamKeyGeneration(len(state.PerTeamKeySeedsUnverified)) 1030 err = computeSeedChecks( 1031 ctx, 1032 state.ID(), 1033 latestChainGen, 1034 func(g keybase1.PerTeamKeyGeneration) (*keybase1.PerTeamSeedCheck, keybase1.PerTeamKeySeed, error) { 1035 ptksu, ok := state.PerTeamKeySeedsUnverified[g] 1036 if !ok { 1037 return nil, keybase1.PerTeamKeySeed{}, fmt.Errorf("unexpected nil PerTeamKeySeedsUnverified at %d", g) 1038 } 1039 return ptksu.Check, ptksu.Seed, nil 1040 }, 1041 func(g keybase1.PerTeamKeyGeneration, check keybase1.PerTeamSeedCheck) { 1042 ptksu := state.PerTeamKeySeedsUnverified[g] 1043 ptksu.Check = &check 1044 state.PerTeamKeySeedsUnverified[g] = ptksu 1045 }, 1046 ) 1047 return err 1048 } 1049 1050 // consumeRatchets finds the hidden chain ratchets in the given link (if it's not stubbed), and adds them 1051 // into the hidden.LoaderPackage via the AddRatchets call. This call, in turn, attempts to unblind the ratchet 1052 // and then checks the ratchets against current state and ratchets. Thus, it can fail in many ways if the server 1053 // is buggy or dishonest. 1054 func consumeRatchets(mctx libkb.MetaContext, hiddenPackage *hidden.LoaderPackage, link *ChainLinkUnpacked) (err error) { 1055 if link.isStubbed() { 1056 return nil 1057 } 1058 err = hiddenPackage.AddRatchets(mctx, link.inner.Ratchets(), link.inner.Ctime, keybase1.RatchetType_MAIN) 1059 return err 1060 } 1061 1062 func checkPTKGenerationNotOnHiddenChain(mctx libkb.MetaContext, hiddenPackage *hidden.LoaderPackage, link *ChainLinkUnpacked) (err error) { 1063 gen := link.PTKGeneration() 1064 if gen == keybase1.PerTeamKeyGeneration(0) { 1065 return nil 1066 } 1067 return hiddenPackage.CheckNoPTK(mctx, gen) 1068 }