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

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