github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/teams/create.go (about)

     1  package teams
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"golang.org/x/net/context"
     8  
     9  	"github.com/keybase/client/go/engine"
    10  	"github.com/keybase/client/go/libkb"
    11  	"github.com/keybase/client/go/protocol/keybase1"
    12  	"github.com/keybase/client/go/teams/hidden"
    13  	jsonw "github.com/keybase/go-jsonw"
    14  )
    15  
    16  func CreateImplicitTeam(ctx context.Context, g *libkb.GlobalContext, impTeam keybase1.ImplicitTeamDisplayName) (res keybase1.TeamID, teamName keybase1.TeamName, err error) {
    17  	defer g.CTrace(ctx, "CreateImplicitTeam", &err)()
    18  
    19  	teamName, err = NewImplicitTeamName()
    20  	if err != nil {
    21  		return res, teamName, err
    22  	}
    23  	teamID := teamName.ToTeamID(impTeam.IsPublic)
    24  
    25  	merkleRoot, err := g.MerkleClient.FetchRootFromServer(libkb.NewMetaContext(ctx, g), libkb.TeamMerkleFreshnessForAdmin)
    26  	if err != nil {
    27  		return res, teamName, err
    28  	}
    29  
    30  	perUserKeyUpgradeSoft(ctx, g, "create-implicit-team")
    31  
    32  	me, err := loadMeForSignatures(ctx, g)
    33  	if err != nil {
    34  		return res, teamName, err
    35  	}
    36  
    37  	// Load all the Keybase users
    38  	loadUsernameList := func(usernames []string) (res []*keybase1.UserPlusKeysV2, err error) {
    39  		for _, username := range usernames {
    40  			arg := libkb.NewLoadUserArg(g).WithName(username).WithNetContext(ctx).WithPublicKeyOptional()
    41  			upak, _, err := g.GetUPAKLoader().LoadV2(arg)
    42  			if err != nil {
    43  				g.Log.CDebugf(ctx, "CreateImplicitTeam: failed to load user: %s msg: %s", username, err)
    44  				return res, err
    45  			}
    46  			res = append(res, &upak.Current)
    47  		}
    48  		return res, nil
    49  	}
    50  
    51  	ownerUPAKs, err := loadUsernameList(impTeam.Writers.KeybaseUsers)
    52  	if err != nil {
    53  		return res, teamName, err
    54  	}
    55  	readerUPAKs, err := loadUsernameList(impTeam.Readers.KeybaseUsers)
    56  	if err != nil {
    57  		return res, teamName, err
    58  	}
    59  
    60  	var owners []SCTeamMember
    61  	var readers []SCTeamMember
    62  	var ownerInvites []SCTeamInvite
    63  	var readerInvites []SCTeamInvite
    64  
    65  	// Form secret boxes and make invites for KB users with no PUKs
    66  	secretboxRecipients := make(map[keybase1.UserVersion]keybase1.PerUserKey)
    67  
    68  	// Form secret boxes for KB users with PUKs, and invites for those without
    69  	for _, upak := range ownerUPAKs {
    70  		uv := upak.ToUserVersion()
    71  		puk := upak.GetLatestPerUserKey()
    72  		if puk == nil {
    73  			// Add this person as an invite if they do not have a puk
    74  			ownerInvites = append(ownerInvites, SCTeamInvite{
    75  				Type: "keybase",
    76  				Name: uv.TeamInviteName(),
    77  				ID:   NewInviteID(),
    78  			})
    79  		} else {
    80  			secretboxRecipients[uv] = *puk
    81  			owners = append(owners, SCTeamMember(uv))
    82  		}
    83  	}
    84  	for _, upak := range readerUPAKs {
    85  		uv := upak.ToUserVersion()
    86  		puk := upak.GetLatestPerUserKey()
    87  		if puk == nil {
    88  			// Add this person as an invite if they do not have a puk
    89  			readerInvites = append(readerInvites, SCTeamInvite{
    90  				Type: "keybase",
    91  				Name: uv.TeamInviteName(),
    92  				ID:   NewInviteID(),
    93  			})
    94  		} else {
    95  			secretboxRecipients[uv] = *puk
    96  			readers = append(readers, SCTeamMember(uv))
    97  		}
    98  	}
    99  
   100  	members := SCTeamMembers{
   101  		Owners:         &[]SCTeamMember{},
   102  		Admins:         &[]SCTeamMember{},
   103  		Writers:        &[]SCTeamMember{},
   104  		Readers:        &[]SCTeamMember{},
   105  		Bots:           &[]SCTeamMember{},
   106  		RestrictedBots: &[]SCTeamMember{},
   107  	}
   108  	if len(owners) > 0 {
   109  		members.Owners = &owners
   110  	}
   111  	if len(readers) > 0 {
   112  		members.Readers = &readers
   113  	}
   114  
   115  	// Add invites for assertions
   116  	for _, assertion := range impTeam.Writers.UnresolvedUsers {
   117  		ownerInvites = append(ownerInvites, SCTeamInvite{
   118  			Type: assertion.TeamInviteType(),
   119  			Name: assertion.TeamInviteName(),
   120  			ID:   NewInviteID(),
   121  		})
   122  	}
   123  
   124  	for _, assertion := range impTeam.Readers.UnresolvedUsers {
   125  		readerInvites = append(readerInvites, SCTeamInvite{
   126  			Type: assertion.TeamInviteType(),
   127  			Name: assertion.TeamInviteName(),
   128  			ID:   NewInviteID(),
   129  		})
   130  	}
   131  
   132  	invites := &SCTeamInvites{
   133  		Owners:  nil,
   134  		Admins:  nil,
   135  		Writers: nil,
   136  		Readers: nil,
   137  	}
   138  	if len(ownerInvites) > 0 {
   139  		invites.Owners = &ownerInvites
   140  	}
   141  	if len(readerInvites) > 0 {
   142  		invites.Readers = &readerInvites
   143  	}
   144  
   145  	// Post the team
   146  	return teamID, teamName,
   147  		makeSigAndPostRootTeam(ctx, g, me, members, invites, secretboxRecipients, teamName.String(),
   148  			teamID, impTeam.IsPublic, true, nil, *merkleRoot)
   149  }
   150  
   151  func makeSigAndPostRootTeam(ctx context.Context, g *libkb.GlobalContext, me libkb.UserForSignatures, members SCTeamMembers,
   152  	invites *SCTeamInvites, secretboxRecipients map[keybase1.UserVersion]keybase1.PerUserKey, name string,
   153  	teamID keybase1.TeamID, public, implicit bool, settings *SCTeamSettings, merkleRoot libkb.MerkleRoot) (err error) {
   154  	mctx := libkb.NewMetaContext(ctx, g)
   155  	defer g.Trace("makeSigAndPostRootTeam", &err)()
   156  	mctx.Debug("makeSigAndPostRootTeam get device keys")
   157  	deviceSigningKey, err := mctx.G().ActiveDevice.SigningKey()
   158  	if err != nil {
   159  		return err
   160  	}
   161  	deviceEncryptionKey, err := mctx.G().ActiveDevice.EncryptionKey()
   162  	if err != nil {
   163  		return err
   164  	}
   165  
   166  	// These boxes will get posted along with the sig below.
   167  	m, err := NewTeamKeyManager(mctx.G(), teamID)
   168  	if err != nil {
   169  		return err
   170  	}
   171  	secretboxes, err := m.SharedSecretBoxes(mctx, deviceEncryptionKey, secretboxRecipients)
   172  	if err != nil {
   173  		return err
   174  	}
   175  
   176  	perTeamSigningKey, err := m.SigningKey()
   177  	if err != nil {
   178  		return err
   179  	}
   180  	perTeamEncryptionKey, err := m.EncryptionKey()
   181  	if err != nil {
   182  		return err
   183  	}
   184  
   185  	mctx.Debug("makeSigAndPostRootTeam make sigs")
   186  	teamSection, err := makeRootTeamSection(name, teamID, members, invites, perTeamSigningKey.GetKID(),
   187  		perTeamEncryptionKey.GetKID(), public, implicit, settings)
   188  	if err != nil {
   189  		return err
   190  	}
   191  
   192  	err = addSummaryHash(&teamSection, secretboxes)
   193  	if err != nil {
   194  		return err
   195  	}
   196  
   197  	// At this point the team section has every field filled out except the
   198  	// reverse sig. Now we'll wrap it into a full sig, marshal it to JSON, and
   199  	// sign it, *twice*. The first time with the per-team signing key, to
   200  	// produce the reverse sig, and the second time with the device signing
   201  	// key, after the reverse sig has been written in.
   202  	sigBodyBeforeReverse, err := TeamRootSig(g, me, deviceSigningKey, teamSection, merkleRoot)
   203  	if err != nil {
   204  		return err
   205  	}
   206  
   207  	// Note that this (sigchain-v1-style) reverse sig is made with the derived *per-team* signing key.
   208  	reverseSig, _, _, err := libkb.SignJSON(sigBodyBeforeReverse, perTeamSigningKey)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	// Update the team section to include the reverse sig, sign it again, and
   214  	// make a sigchain-v2-style sig out of it. Doing it this way, instead of
   215  	// generating it twice with different parameters, makes it less likely to
   216  	// accidentally capture different global state (like ctime and merkle
   217  	// seqno).
   218  	sigBodyAfterReverse := sigBodyBeforeReverse
   219  	err = sigBodyAfterReverse.SetValueAtPath("body.team.per_team_key.reverse_sig", jsonw.NewString(reverseSig))
   220  	if err != nil {
   221  		return err
   222  	}
   223  
   224  	sigJSONAfterReverse, err := sigBodyAfterReverse.Marshal()
   225  	if err != nil {
   226  		return err
   227  	}
   228  	seqType := seqTypeForTeamPublicness(public)
   229  	v2Sig, _, _, err := libkb.MakeSigchainV2OuterSig(
   230  		mctx,
   231  		deviceSigningKey,
   232  		libkb.LinkTypeTeamRoot,
   233  		1, /* seqno */
   234  		sigJSONAfterReverse,
   235  		nil, /* prevLinkID */
   236  		libkb.SigHasRevokes(false),
   237  		seqType,
   238  		libkb.SigIgnoreIfUnsupported(false),
   239  		nil,
   240  	)
   241  	if err != nil {
   242  		return err
   243  	}
   244  
   245  	sigMultiItem := libkb.SigMultiItem{
   246  		Sig:        v2Sig,
   247  		SigningKID: deviceSigningKey.GetKID(),
   248  		Type:       string(libkb.LinkTypeTeamRoot),
   249  		SeqType:    seqType,
   250  		SigInner:   string(sigJSONAfterReverse),
   251  		TeamID:     teamID,
   252  		PublicKeys: &libkb.SigMultiItemPublicKeys{
   253  			Encryption: perTeamEncryptionKey.GetKID(),
   254  			Signing:    perTeamSigningKey.GetKID(),
   255  		},
   256  		Version: libkb.KeybaseSignatureV2,
   257  	}
   258  
   259  	err = precheckLinkToPost(ctx, g, sigMultiItem, nil, me.ToUserVersion())
   260  	if err != nil {
   261  		mctx.Debug("cannot post link (precheck): %v", err)
   262  		return err
   263  	}
   264  
   265  	mctx.Debug("makeSigAndPostRootTeam post sigs")
   266  	payload := make(libkb.JSONPayload)
   267  	payload["sigs"] = []interface{}{sigMultiItem}
   268  	payload["per_team_key"] = secretboxes
   269  
   270  	_, err = mctx.G().API.PostJSON(mctx, libkb.APIArg{
   271  		Endpoint:    "sig/multi",
   272  		SessionType: libkb.APISessionTypeREQUIRED,
   273  		JSONPayload: payload,
   274  	})
   275  	if err != nil {
   276  		return err
   277  	}
   278  	mctx.Debug("makeSigAndPostRootTeam created team: %v", teamID)
   279  	return nil
   280  }
   281  
   282  func CreateRootTeam(ctx context.Context, g *libkb.GlobalContext, nameString string, settings keybase1.TeamSettings) (res *keybase1.TeamID, err error) {
   283  	defer g.CTrace(ctx, "CreateRootTeam", &err)()
   284  
   285  	perUserKeyUpgradeSoft(ctx, g, "create-root-team")
   286  
   287  	name, err := keybase1.TeamNameFromString(nameString)
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  	if !name.IsRootTeam() {
   292  		return nil, fmt.Errorf("cannot create root team with subteam name: %v", nameString)
   293  	}
   294  
   295  	merkleRoot, err := g.MerkleClient.FetchRootFromServer(libkb.NewMetaContext(ctx, g), libkb.TeamMerkleFreshnessForAdmin)
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  
   300  	g.Log.CDebugf(ctx, "CreateRootTeam load me")
   301  	me, err := loadMeForSignatures(ctx, g)
   302  	if err != nil {
   303  		return nil, err
   304  	}
   305  
   306  	ownerLatest := me.GetLatestPerUserKey()
   307  	if ownerLatest == nil {
   308  		return nil, errors.New("can't create a new team without having provisioned a per-user key")
   309  	}
   310  	secretboxRecipients := map[keybase1.UserVersion]keybase1.PerUserKey{
   311  		me.ToUserVersion(): *ownerLatest,
   312  	}
   313  
   314  	members := SCTeamMembers{
   315  		Owners:         &[]SCTeamMember{SCTeamMember(me.ToUserVersion())},
   316  		Admins:         &[]SCTeamMember{},
   317  		Writers:        &[]SCTeamMember{},
   318  		Readers:        &[]SCTeamMember{},
   319  		Bots:           &[]SCTeamMember{},
   320  		RestrictedBots: &[]SCTeamMember{},
   321  	}
   322  
   323  	var scSettings *SCTeamSettings
   324  	if settings.Open {
   325  		settingsTemp, err := CreateTeamSettings(settings.Open, settings.JoinAs)
   326  		if err != nil {
   327  			return nil, err
   328  		}
   329  		scSettings = &settingsTemp
   330  	}
   331  
   332  	teamID := name.ToPrivateTeamID()
   333  
   334  	err = makeSigAndPostRootTeam(ctx, g, me, members, nil,
   335  		secretboxRecipients, name.String(), teamID, false, false, scSettings, *merkleRoot)
   336  	return &teamID, err
   337  }
   338  
   339  func CreateSubteam(ctx context.Context, g *libkb.GlobalContext, subteamBasename string,
   340  	parentName keybase1.TeamName, addSelfAs keybase1.TeamRole) (ret *keybase1.TeamID, err error) {
   341  	defer g.CTrace(ctx, "CreateSubteam", &err)()
   342  	mctx := libkb.NewMetaContext(ctx, g)
   343  
   344  	subteamName, err := parentName.Append(subteamBasename)
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  
   349  	// Assume private
   350  	public := false
   351  	subteamID := NewSubteamID(public)
   352  
   353  	err = ForceMerkleRootUpdateByTeamID(mctx, parentName.ToPrivateTeamID())
   354  	if err != nil {
   355  		return nil, err
   356  	}
   357  	merkleRoot, err := g.MerkleClient.FetchRootFromServer(libkb.NewMetaContext(ctx, g), libkb.TeamMerkleFreshnessForAdmin)
   358  	if err != nil {
   359  		return nil, err
   360  	}
   361  
   362  	perUserKeyUpgradeSoft(ctx, mctx.G(), "create-subteam")
   363  
   364  	me, err := loadMeForSignatures(ctx, mctx.G())
   365  	if err != nil {
   366  		return nil, err
   367  	}
   368  
   369  	deviceSigningKey, err := mctx.G().ActiveDevice.SigningKey()
   370  	if err != nil {
   371  		return nil, err
   372  	}
   373  
   374  	parentTeam, err := GetForTeamManagementByStringName(ctx, g, parentName.String(), true)
   375  	if err != nil {
   376  		return nil, err
   377  	}
   378  
   379  	admin, err := parentTeam.getAdminPermission(ctx)
   380  	if err != nil {
   381  		return nil, err
   382  	}
   383  
   384  	if err := parentTeam.ForceMerkleRootUpdate(ctx); err != nil {
   385  		return nil, err
   386  	}
   387  
   388  	// Subteam creation involves two links, one in the parent team's chain, and
   389  	// one to start the new subteam chain. The start of the new subteam chain
   390  	// (type "team.subteam_head") is very similar to the "team.root" sig that
   391  	// starts a root team, and so making that link is very similar to what the
   392  	// CreateTeamEngine does.
   393  
   394  	newSubteamSig, ratchet, err := generateNewSubteamSigForParentChain(mctx, me, deviceSigningKey, parentTeam.chain(), subteamName, subteamID, admin)
   395  	if err != nil {
   396  		return nil, err
   397  	}
   398  
   399  	// subteam needs to be keyed for all admins above it
   400  	allParentAdmins, err := parentTeam.AllAdmins(ctx)
   401  	if err != nil {
   402  		return nil, err
   403  	}
   404  
   405  	subteamHeadSig, secretboxes, err := generateHeadSigForSubteamChain(ctx, g, me,
   406  		deviceSigningKey, parentTeam.chain(), subteamName, subteamID, admin,
   407  		allParentAdmins, addSelfAs, *merkleRoot)
   408  	if err != nil {
   409  		return nil, err
   410  	}
   411  
   412  	err = precheckLinkToPost(ctx, g, *newSubteamSig, parentTeam.chain(), me.ToUserVersion())
   413  	if err != nil {
   414  		return nil, fmt.Errorf("cannot post link (precheck new subteam): %v", err)
   415  	}
   416  
   417  	err = precheckLinkToPost(ctx, g, *subteamHeadSig, nil, me.ToUserVersion())
   418  	if err != nil {
   419  		return nil, fmt.Errorf("cannot post link (precheck subteam head): %v", err)
   420  	}
   421  
   422  	payload := make(libkb.JSONPayload)
   423  	payload["sigs"] = []interface{}{newSubteamSig, subteamHeadSig}
   424  	payload["per_team_key"] = secretboxes
   425  	ratchet.AddToJSONPayload(payload)
   426  
   427  	_, err = g.API.PostJSON(mctx, libkb.APIArg{
   428  		Endpoint:    "sig/multi",
   429  		SessionType: libkb.APISessionTypeREQUIRED,
   430  		JSONPayload: payload,
   431  	})
   432  	if err != nil {
   433  		return nil, err
   434  	}
   435  
   436  	return &subteamID, nil
   437  }
   438  
   439  func makeRootTeamSection(teamName string, teamID keybase1.TeamID, members SCTeamMembers, invites *SCTeamInvites,
   440  	perTeamSigningKID keybase1.KID, perTeamEncryptionKID keybase1.KID, public bool, implicit bool, settings *SCTeamSettings) (SCTeamSection, error) {
   441  	teamSection := SCTeamSection{
   442  		Name:     (*SCTeamName)(&teamName),
   443  		ID:       (SCTeamID)(teamID),
   444  		Public:   public,
   445  		Implicit: implicit,
   446  		PerTeamKey: &SCPerTeamKey{
   447  			Generation: 1,
   448  			SigKID:     perTeamSigningKID,
   449  			EncKID:     perTeamEncryptionKID,
   450  		},
   451  		Members: &members,
   452  		Invites: invites,
   453  	}
   454  
   455  	teamSection.Settings = settings
   456  
   457  	// At this point the team section has every field filled out except the
   458  	// reverse sig. Now we'll wrap it into a full sig, marshal it to JSON, and
   459  	// sign it, *twice*. The first time with the per-team signing key, to
   460  	// produce the reverse sig, and the second time with the device signing
   461  	// key, after the reverse sig has been written in.
   462  
   463  	return teamSection, nil
   464  }
   465  
   466  func generateNewSubteamSigForParentChain(m libkb.MetaContext, me libkb.UserForSignatures, signingKey libkb.GenericKey, parentTeam *TeamSigChainState, subteamName keybase1.TeamName, subteamID keybase1.TeamID, admin *SCTeamAdmin) (item *libkb.SigMultiItem, r *hidden.Ratchet, err error) {
   467  	newSubteamSigBody, ratchet, err := NewSubteamSig(m, me, signingKey, parentTeam, subteamName, subteamID, admin)
   468  	if err != nil {
   469  		return nil, nil, err
   470  	}
   471  	newSubteamSigJSON, err := newSubteamSigBody.Marshal()
   472  	if err != nil {
   473  		return nil, nil, err
   474  	}
   475  
   476  	prevLinkID, err := libkb.ImportLinkID(parentTeam.GetLatestLinkID())
   477  	if err != nil {
   478  		return nil, nil, err
   479  	}
   480  	seqType := seqTypeForTeamPublicness(parentTeam.IsPublic())
   481  
   482  	v2Sig, _, _, err := libkb.MakeSigchainV2OuterSig(
   483  		m,
   484  		signingKey,
   485  		libkb.LinkTypeNewSubteam,
   486  		parentTeam.GetLatestSeqno()+1,
   487  		newSubteamSigJSON,
   488  		prevLinkID,
   489  		libkb.SigHasRevokes(false),
   490  		seqType,
   491  		libkb.SigIgnoreIfUnsupported(false),
   492  		nil,
   493  	)
   494  	if err != nil {
   495  		return nil, nil, err
   496  	}
   497  
   498  	item = &libkb.SigMultiItem{
   499  		Sig:        v2Sig,
   500  		SigningKID: signingKey.GetKID(),
   501  		Type:       string(libkb.LinkTypeNewSubteam),
   502  		SeqType:    seqType,
   503  		SigInner:   string(newSubteamSigJSON),
   504  		TeamID:     parentTeam.GetID(),
   505  		Version:    libkb.KeybaseSignatureV2,
   506  	}
   507  	return item, ratchet, nil
   508  }
   509  
   510  func generateHeadSigForSubteamChain(ctx context.Context, g *libkb.GlobalContext, me libkb.UserForSignatures,
   511  	signingKey libkb.GenericKey, parentTeam *TeamSigChainState, subteamName keybase1.TeamName,
   512  	subteamID keybase1.TeamID, admin *SCTeamAdmin, allParentAdmins []keybase1.UserVersion,
   513  	addSelfAs keybase1.TeamRole, merkleRoot libkb.MerkleRoot) (item *libkb.SigMultiItem, boxes *PerTeamSharedSecretBoxes, err error) {
   514  	deviceEncryptionKey, err := g.ActiveDevice.EncryptionKey()
   515  	if err != nil {
   516  		return
   517  	}
   518  
   519  	members := SCTeamMembers{
   520  		Owners:         &[]SCTeamMember{},
   521  		Admins:         &[]SCTeamMember{},
   522  		Writers:        &[]SCTeamMember{},
   523  		Readers:        &[]SCTeamMember{},
   524  		Bots:           &[]SCTeamMember{},
   525  		RestrictedBots: &[]SCTeamMember{},
   526  	}
   527  
   528  	memSet := newMemberSet()
   529  	_, err = memSet.loadGroup(ctx, g, allParentAdmins, storeMemberKindRecipient, true /* force poll */)
   530  	if err != nil {
   531  		return nil, nil, err
   532  	}
   533  
   534  	if addSelfAs != keybase1.TeamRole_NONE {
   535  		meUV := me.ToUserVersion()
   536  		memList := []SCTeamMember{SCTeamMember(meUV)}
   537  		switch addSelfAs {
   538  		case keybase1.TeamRole_BOT:
   539  			members.Bots = &memList
   540  		case keybase1.TeamRole_READER:
   541  			members.Readers = &memList
   542  		case keybase1.TeamRole_WRITER:
   543  			members.Writers = &memList
   544  		case keybase1.TeamRole_ADMIN:
   545  			members.Admins = &memList
   546  		case keybase1.TeamRole_OWNER:
   547  			return nil, nil, errors.New("Cannot add self as owner to a subteam")
   548  		case keybase1.TeamRole_RESTRICTEDBOT:
   549  			return nil, nil, errors.New("Cannot add self as restricted bot to a subteam")
   550  		}
   551  		_, err := memSet.addMember(ctx, g, meUV, storeMemberKindRecipient, false /* force poll */)
   552  		if err != nil {
   553  			return nil, nil, err
   554  		}
   555  	}
   556  
   557  	// These boxes will get posted along with the sig below.
   558  	m, err := NewTeamKeyManager(g, subteamID)
   559  	if err != nil {
   560  		return nil, nil, err
   561  	}
   562  	boxes, err = m.SharedSecretBoxes(libkb.NewMetaContext(ctx, g), deviceEncryptionKey, memSet.recipients)
   563  	if err != nil {
   564  		return nil, nil, err
   565  	}
   566  
   567  	perTeamSigningKey, err := m.SigningKey()
   568  	if err != nil {
   569  		return nil, nil, err
   570  	}
   571  	perTeamEncryptionKey, err := m.EncryptionKey()
   572  	if err != nil {
   573  		return nil, nil, err
   574  	}
   575  
   576  	// The "team" section of a subchain head link is similar to that of a
   577  	// "team.root" link, with the addition of the "parent" subsection.
   578  	teamSection, err := makeSubteamTeamSection(subteamName, subteamID, parentTeam, members, perTeamSigningKey.GetKID(), perTeamEncryptionKey.GetKID(), admin)
   579  	if err != nil {
   580  		return
   581  	}
   582  
   583  	err = addSummaryHash(&teamSection, boxes)
   584  	if err != nil {
   585  		return nil, nil, err
   586  	}
   587  
   588  	subteamHeadSigBodyBeforeReverse, err := SubteamHeadSig(g, me, signingKey, teamSection, merkleRoot)
   589  	if err != nil {
   590  		return
   591  	}
   592  
   593  	// Now generate the reverse sig and edit it into the JSON. Note that this
   594  	// (sigchain-v1-style) reverse sig is made with the derived *per-team*
   595  	// signing key.
   596  	reverseSig, _, _, err := libkb.SignJSON(subteamHeadSigBodyBeforeReverse, perTeamSigningKey)
   597  	if err != nil {
   598  		return
   599  	}
   600  
   601  	// Update the team section to include the reverse sig, sign it again, and
   602  	// make a sigchain-v2-style sig out of it. Doing it this way, instead of
   603  	// generating it twice with different parameters, makes it less likely to
   604  	// accidentally capture different global state (like ctime and merkle
   605  	// seqno).
   606  	subteamHeadSigBodyAfterReverse := subteamHeadSigBodyBeforeReverse
   607  	err = subteamHeadSigBodyAfterReverse.SetValueAtPath("body.team.per_team_key.reverse_sig", jsonw.NewString(reverseSig))
   608  	if err != nil {
   609  		return nil, nil, err
   610  	}
   611  
   612  	subteamHeadSigJSON, err := subteamHeadSigBodyAfterReverse.Marshal()
   613  	if err != nil {
   614  		return
   615  	}
   616  
   617  	seqType := seqTypeForTeamPublicness(parentTeam.IsPublic())
   618  	v2Sig, _, _, err := libkb.MakeSigchainV2OuterSig(
   619  		libkb.NewMetaContext(ctx, g),
   620  		signingKey,
   621  		libkb.LinkTypeSubteamHead,
   622  		1, /* seqno */
   623  		subteamHeadSigJSON,
   624  		nil, /* prevLinkID */
   625  		libkb.SigHasRevokes(false),
   626  		seqType,
   627  		libkb.SigIgnoreIfUnsupported(false),
   628  		nil,
   629  	)
   630  	if err != nil {
   631  		return
   632  	}
   633  
   634  	item = &libkb.SigMultiItem{
   635  		Sig:        v2Sig,
   636  		SigningKID: signingKey.GetKID(),
   637  		Type:       string(libkb.LinkTypeSubteamHead),
   638  		SeqType:    seqType,
   639  		SigInner:   string(subteamHeadSigJSON),
   640  		TeamID:     subteamID,
   641  		PublicKeys: &libkb.SigMultiItemPublicKeys{
   642  			Encryption: perTeamEncryptionKey.GetKID(),
   643  			Signing:    perTeamSigningKey.GetKID(),
   644  		},
   645  	}
   646  	return
   647  }
   648  
   649  func makeSubteamTeamSection(subteamName keybase1.TeamName, subteamID keybase1.TeamID,
   650  	parentTeam *TeamSigChainState, members SCTeamMembers, perTeamSigningKID keybase1.KID,
   651  	perTeamEncryptionKID keybase1.KID, admin *SCTeamAdmin) (SCTeamSection, error) {
   652  
   653  	subteamName2 := subteamName.String()
   654  	teamSection := SCTeamSection{
   655  		Name: (*SCTeamName)(&subteamName2),
   656  		ID:   (SCTeamID)(subteamID),
   657  		Parent: &SCTeamParent{
   658  			ID:      SCTeamID(parentTeam.GetID()),
   659  			Seqno:   parentTeam.GetLatestSeqno() + 1, // the seqno of the *new* parent link
   660  			SeqType: seqTypeForTeamPublicness(parentTeam.IsPublic()),
   661  		},
   662  		PerTeamKey: &SCPerTeamKey{
   663  			Generation: 1,
   664  			SigKID:     perTeamSigningKID,
   665  			EncKID:     perTeamEncryptionKID,
   666  		},
   667  		Members: &members,
   668  		Admin:   admin,
   669  	}
   670  
   671  	// At this point the team section has every field filled out except the
   672  	// reverse sig. Next we'll wrap it into a full sig body, marshal it to
   673  	// JSON, and sign it, *twice*. The first time with the per-team signing
   674  	// key, to produce the reverse sig, and the second time with the device
   675  	// signing key, after the reverse sig has been written in.
   676  
   677  	return teamSection, nil
   678  }
   679  
   680  // Get a per-user key.
   681  // Wait for attempt but only warn on error.
   682  func perUserKeyUpgradeSoft(ctx context.Context, g *libkb.GlobalContext, reason string) {
   683  	m := libkb.NewMetaContext(ctx, g)
   684  	arg := &engine.PerUserKeyUpgradeArgs{}
   685  	eng := engine.NewPerUserKeyUpgrade(g, arg)
   686  	err := engine.RunEngine2(m, eng)
   687  	if err != nil {
   688  		m.Debug("PerUserKeyUpgrade failed (%s): %v", reason, err)
   689  	}
   690  }