1// Copyright 2015 Keybase, Inc. All rights reserved. Use of 2// this source code is governed by the included BSD license. 3 4// 5// Similar to libkb/kbsigs.go, but for teams sigs. 6// 7package teams 8 9import ( 10 "encoding/hex" 11 "errors" 12 "fmt" 13 14 "golang.org/x/net/context" 15 16 "github.com/davecgh/go-spew/spew" 17 "github.com/keybase/client/go/libkb" 18 keybase1 "github.com/keybase/client/go/protocol/keybase1" 19 "github.com/keybase/client/go/sig3" 20 "github.com/keybase/client/go/teams/hidden" 21 jsonw "github.com/keybase/go-jsonw" 22) 23 24// metaContext returns a GlobalContext + a TODO context, since we're not 25// threading through contexts through this library. In the future, we should 26// fix this. 27func metaContext(g *libkb.GlobalContext) libkb.MetaContext { 28 return libkb.NewMetaContextTODO(g) 29} 30 31func TeamRootSig(g *libkb.GlobalContext, me libkb.UserForSignatures, key libkb.GenericKey, teamSection SCTeamSection, merkleRoot libkb.MerkleRoot) (*jsonw.Wrapper, error) { 32 ret, err := libkb.ProofMetadata{ 33 SigningUser: me, 34 Eldest: me.GetEldestKID(), 35 LinkType: libkb.LinkTypeTeamRoot, 36 SigningKey: key, 37 Seqno: 1, 38 SigVersion: libkb.KeybaseSignatureV2, 39 SeqType: seqTypeForTeamPublicness(teamSection.Public), 40 MerkleRoot: &merkleRoot, 41 }.ToJSON(metaContext(g)) 42 if err != nil { 43 return nil, err 44 } 45 46 teamSectionJSON, err := jsonw.WrapperFromObject(teamSection) 47 if err != nil { 48 return nil, err 49 } 50 err = teamSectionJSON.SetValueAtPath("per_team_key.reverse_sig", jsonw.NewNil()) 51 if err != nil { 52 return nil, err 53 } 54 55 body := ret.AtKey("body") 56 err = body.SetKey("team", teamSectionJSON) 57 if err != nil { 58 return nil, err 59 } 60 61 return ret, nil 62} 63 64func NewImplicitTeamName() (res keybase1.TeamName, err error) { 65 dat, err := libkb.RandBytes(keybase1.ImplicitSuffixLengthBytes) 66 if err != nil { 67 return res, err 68 } 69 res, err = keybase1.TeamNameFromString(fmt.Sprintf("%s%s", keybase1.ImplicitTeamPrefix, hex.EncodeToString(dat))) 70 return res, err 71} 72 73func NewSubteamSig(mctx libkb.MetaContext, me libkb.UserForSignatures, key libkb.GenericKey, parentTeam *TeamSigChainState, subteamName keybase1.TeamName, subteamID keybase1.TeamID, admin *SCTeamAdmin) (*jsonw.Wrapper, *hidden.Ratchet, error) { 74 g := mctx.G() 75 prevLinkID, err := libkb.ImportLinkID(parentTeam.GetLatestLinkID()) 76 if err != nil { 77 return nil, nil, err 78 } 79 ret, err := libkb.ProofMetadata{ 80 SigningUser: me, 81 Eldest: me.GetEldestKID(), 82 LinkType: libkb.LinkTypeNewSubteam, 83 SigningKey: key, 84 SigVersion: libkb.KeybaseSignatureV2, 85 SeqType: seqTypeForTeamPublicness(parentTeam.IsPublic()), // children are as public as their parent 86 Seqno: parentTeam.GetLatestSeqno() + 1, 87 PrevLinkID: prevLinkID, 88 }.ToJSON(metaContext(g)) 89 if err != nil { 90 return nil, nil, err 91 } 92 93 entropy, err := makeSCTeamEntropy() 94 if err != nil { 95 return nil, nil, err 96 } 97 98 ratchet, err := parentTeam.makeHiddenRatchet(mctx) 99 if err != nil { 100 return nil, nil, err 101 } 102 103 teamSection := SCTeamSection{ 104 ID: (SCTeamID)(parentTeam.GetID()), 105 Subteam: &SCSubteam{ 106 ID: (SCTeamID)(subteamID), 107 Name: (SCTeamName)(subteamName.String()), 108 }, 109 Admin: admin, 110 Entropy: entropy, 111 Ratchets: ratchet.ToTeamSection(), 112 } 113 teamSectionJSON, err := jsonw.WrapperFromObject(teamSection) 114 if err != nil { 115 return nil, nil, err 116 } 117 err = ret.SetValueAtPath("body.team", teamSectionJSON) 118 if err != nil { 119 return nil, nil, err 120 } 121 122 return ret, ratchet, nil 123} 124 125func SubteamHeadSig(g *libkb.GlobalContext, me libkb.UserForSignatures, key libkb.GenericKey, subteamTeamSection SCTeamSection, merkleRoot libkb.MerkleRoot) (*jsonw.Wrapper, error) { 126 ret, err := libkb.ProofMetadata{ 127 SigningUser: me, 128 Eldest: me.GetEldestKID(), 129 LinkType: libkb.LinkTypeSubteamHead, 130 SigningKey: key, 131 Seqno: 1, 132 SigVersion: libkb.KeybaseSignatureV2, 133 SeqType: seqTypeForTeamPublicness(subteamTeamSection.Public), 134 MerkleRoot: &merkleRoot, 135 }.ToJSON(metaContext(g)) 136 if err != nil { 137 return nil, err 138 } 139 140 // Note that the team section here is expected to have its Parent 141 // subsection filled out by the caller, unlike TeamRootSig. 142 teamSectionJSON, err := jsonw.WrapperFromObject(subteamTeamSection) 143 if err != nil { 144 return nil, err 145 } 146 err = teamSectionJSON.SetValueAtPath("per_team_key.reverse_sig", jsonw.NewNil()) 147 if err != nil { 148 return nil, err 149 } 150 151 body := ret.AtKey("body") 152 err = body.SetKey("team", teamSectionJSON) 153 if err != nil { 154 return nil, err 155 } 156 157 return ret, nil 158} 159 160func RenameSubteamSig(g *libkb.GlobalContext, me libkb.UserForSignatures, key libkb.GenericKey, parentTeam *TeamSigChainState, teamSection SCTeamSection) (*jsonw.Wrapper, error) { 161 prev, err := parentTeam.GetLatestLibkbLinkID() 162 if err != nil { 163 return nil, err 164 } 165 ret, err := libkb.ProofMetadata{ 166 SigningUser: me, 167 Eldest: me.GetEldestKID(), 168 LinkType: libkb.LinkTypeRenameSubteam, 169 SigningKey: key, 170 Seqno: parentTeam.GetLatestSeqno() + 1, 171 PrevLinkID: prev, 172 SigVersion: libkb.KeybaseSignatureV2, 173 SeqType: seqTypeForTeamPublicness(teamSection.Public), 174 }.ToJSON(metaContext(g)) 175 if err != nil { 176 return nil, err 177 } 178 179 teamSectionJSON, err := jsonw.WrapperFromObject(teamSection) 180 if err != nil { 181 return nil, err 182 } 183 184 body := ret.AtKey("body") 185 err = body.SetKey("team", teamSectionJSON) 186 if err != nil { 187 return nil, err 188 } 189 190 return ret, nil 191} 192 193func RenameUpPointerSig(g *libkb.GlobalContext, me libkb.UserForSignatures, key libkb.GenericKey, subteam *TeamSigChainState, teamSection SCTeamSection) (*jsonw.Wrapper, error) { 194 prev, err := subteam.GetLatestLibkbLinkID() 195 if err != nil { 196 return nil, err 197 } 198 ret, err := libkb.ProofMetadata{ 199 SigningUser: me, 200 Eldest: me.GetEldestKID(), 201 LinkType: libkb.LinkTypeRenameUpPointer, 202 SigningKey: key, 203 Seqno: subteam.GetLatestSeqno() + 1, 204 PrevLinkID: prev, 205 SigVersion: libkb.KeybaseSignatureV2, 206 SeqType: seqTypeForTeamPublicness(teamSection.Public), 207 }.ToJSON(metaContext(g)) 208 if err != nil { 209 return nil, err 210 } 211 212 teamSectionJSON, err := jsonw.WrapperFromObject(teamSection) 213 if err != nil { 214 return nil, err 215 } 216 217 body := ret.AtKey("body") 218 err = body.SetKey("team", teamSectionJSON) 219 if err != nil { 220 return nil, err 221 } 222 223 return ret, nil 224} 225 226// 15 random bytes, followed by the byte 0x25, encoded as hex 227func NewSubteamID(public bool) keybase1.TeamID { 228 var useSuffix byte = keybase1.SUB_TEAMID_PRIVATE_SUFFIX 229 if public { 230 useSuffix = keybase1.SUB_TEAMID_PUBLIC_SUFFIX 231 } 232 idBytes, err := libkb.RandBytesWithSuffix(16, useSuffix) 233 if err != nil { 234 panic("RandBytes failed: " + err.Error()) 235 } 236 return keybase1.TeamID(hex.EncodeToString(idBytes)) 237} 238 239func NewInviteID() SCTeamInviteID { 240 b, err := libkb.RandBytesWithSuffix(16, libkb.InviteIDTag) 241 if err != nil { 242 panic("RandBytes failed: " + err.Error()) 243 } 244 return SCTeamInviteID(hex.EncodeToString(b)) 245} 246 247func ChangeSig(g *libkb.GlobalContext, me libkb.UserForSignatures, prev libkb.LinkID, seqno keybase1.Seqno, key libkb.GenericKey, teamSection SCTeamSection, 248 linkType libkb.LinkType, merkleRoot *libkb.MerkleRoot) (*jsonw.Wrapper, error) { 249 if teamSection.PerTeamKey != nil { 250 if teamSection.PerTeamKey.ReverseSig != "" { 251 return nil, errors.New("ChangeMembershipSig called with PerTeamKey.ReverseSig already set") 252 } 253 } 254 255 ret, err := libkb.ProofMetadata{ 256 LinkType: linkType, 257 SigningUser: me, 258 Eldest: me.GetEldestKID(), 259 SigningKey: key, 260 Seqno: seqno, 261 PrevLinkID: prev, 262 SigVersion: libkb.KeybaseSignatureV2, 263 SeqType: seqTypeForTeamPublicness(teamSection.Public), 264 MerkleRoot: merkleRoot, 265 }.ToJSON(metaContext(g)) 266 if err != nil { 267 return nil, err 268 } 269 270 teamSectionJSON, err := jsonw.WrapperFromObject(teamSection) 271 if err != nil { 272 return nil, err 273 } 274 275 body := ret.AtKey("body") 276 err = body.SetKey("team", teamSectionJSON) 277 if err != nil { 278 return nil, err 279 } 280 281 return ret, nil 282} 283 284func makeSCTeamEntropy() (SCTeamEntropy, error) { 285 entropy, err := libkb.LinkEntropy() 286 if err != nil { 287 return SCTeamEntropy(""), err 288 } 289 return SCTeamEntropy(entropy), nil 290} 291 292func seqTypeForTeamPublicness(public bool) keybase1.SeqType { 293 if public { 294 return keybase1.SeqType_PUBLIC 295 } 296 return keybase1.SeqType_SEMIPRIVATE 297} 298 299func precheckLinkToPost(ctx context.Context, g *libkb.GlobalContext, 300 sigMultiItem libkb.SigMultiItem, state *TeamSigChainState, 301 me keybase1.UserVersion) (err error) { 302 return precheckLinksToPost(ctx, g, []libkb.SigMultiItem{sigMultiItem}, state, me) 303} 304 305func appendChainLinkSig3(ctx context.Context, g *libkb.GlobalContext, 306 sig libkb.Sig3, state *TeamSigChainState, 307 me keybase1.UserVersion) (err error) { 308 309 mctx := libkb.NewMetaContext(ctx, g) 310 311 if len(sig.Outer) == 0 || len(sig.Sig) == 0 { 312 return NewPrecheckStructuralError("got a stubbed v3 link on post, which isn't allowed", nil) 313 } 314 315 hp := hidden.NewLoaderPackageForPrecheck(mctx, state.GetID(), state.hidden) 316 ex := sig3.ExportJSON{ 317 Inner: sig.Inner, 318 Outer: sig.Outer, 319 Sig: sig.Sig, 320 } 321 err = hp.Update(mctx, []sig3.ExportJSON{ex}, keybase1.Seqno(0)) 322 if err != nil { 323 return err 324 } 325 mctx.Debug("appendChainLinkSig3 success for %s", sig.Outer) 326 return nil 327} 328 329func precheckLinksToPost(ctx context.Context, g *libkb.GlobalContext, 330 sigMultiItems []libkb.SigMultiItem, state *TeamSigChainState, 331 me keybase1.UserVersion) (err error) { 332 _, err = precheckLinksToState(ctx, g, sigMultiItems, state, me) 333 return err 334} 335 336func precheckLinksToState(ctx context.Context, g *libkb.GlobalContext, 337 sigMultiItems []libkb.SigMultiItem, state *TeamSigChainState, 338 me keybase1.UserVersion) (newState *TeamSigChainState, err error) { 339 340 defer g.CTrace(ctx, "precheckLinksToState", &err)() 341 342 isAdmin := true 343 if state != nil { 344 role, err := state.GetUserRole(me) 345 if err != nil { 346 role = keybase1.TeamRole_NONE 347 } 348 isAdmin = role.IsAdminOrAbove() 349 350 // As an optimization, AppendChainLink consumes its state. 351 // We don't consume our state parameter. 352 // So clone state before we pass it along to be consumed. 353 state = state.DeepCopyToPtr() 354 } 355 356 signer := SignerX{ 357 signer: me, 358 implicitAdmin: !isAdmin, 359 } 360 361 for i, sigItem := range sigMultiItems { 362 363 if sigItem.Sig3 != nil { 364 err = appendChainLinkSig3(ctx, g, *sigItem.Sig3, state, me) 365 if err != nil { 366 g.Log.CDebugf(ctx, "precheckLinksToState: link (sig3) %v/%v rejected: %v", i+1, len(sigMultiItems), err) 367 return nil, NewPrecheckAppendError(err) 368 } 369 continue 370 } 371 372 outerLink, err := libkb.DecodeOuterLinkV2(sigItem.Sig) 373 if err != nil { 374 return nil, NewPrecheckStructuralError("unpack outer", err) 375 } 376 377 link1 := SCChainLink{ 378 Seqno: outerLink.Seqno, 379 Sig: sigItem.Sig, 380 Payload: sigItem.SigInner, 381 UID: me.Uid, 382 Version: 2, 383 } 384 link2, err := unpackChainLink(&link1) 385 if err != nil { 386 return nil, NewPrecheckStructuralError("unpack link", err) 387 } 388 389 if link2.isStubbed() { 390 return nil, NewPrecheckStructuralError("link missing inner", nil) 391 } 392 393 newState, err := AppendChainLink(ctx, g, me, state, link2, &signer) 394 if err != nil { 395 if link2.inner != nil && link2.inner.Body.Team != nil && link2.inner.Body.Team.Members != nil { 396 g.Log.CDebugf(ctx, "precheckLinksToState: link %v/%v rejected: %v", i+1, len(sigMultiItems), spew.Sprintf("%v", *link2.inner.Body.Team.Members)) 397 } else { 398 g.Log.CDebugf(ctx, "precheckLinksToState: link %v/%v rejected", i+1, len(sigMultiItems)) 399 } 400 return nil, NewPrecheckAppendError(err) 401 } 402 state = &newState 403 } 404 405 return state, nil 406} 407