github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/hidden/hidden.go (about)

     1  package hidden
     2  
     3  import (
     4  	"encoding/hex"
     5  	"fmt"
     6  
     7  	"github.com/keybase/client/go/libkb"
     8  	"github.com/keybase/client/go/merkletree2"
     9  	"github.com/keybase/client/go/protocol/keybase1"
    10  	"github.com/keybase/client/go/sig3"
    11  )
    12  
    13  func importChain(mctx libkb.MetaContext, raw []sig3.ExportJSON) (ret []sig3.Generic, err error) {
    14  	ret = make([]sig3.Generic, 0, len(raw))
    15  	for _, s := range raw {
    16  		g, err := s.Import()
    17  		if err != nil {
    18  			return nil, err
    19  		}
    20  		ret = append(ret, g)
    21  	}
    22  	return ret, nil
    23  }
    24  
    25  func populateLink(mctx libkb.MetaContext, ret *keybase1.HiddenTeamChain, link sig3.Generic) (err error) {
    26  	hsh, err := sig3.Hash(link)
    27  	if err != nil {
    28  		return err
    29  	}
    30  	q := link.Seqno()
    31  	ret.Outer[q] = hsh.Export()
    32  	if q > ret.Last {
    33  		ret.Last = q
    34  	}
    35  	if sig3.IsStubbed(link) {
    36  		return nil
    37  	}
    38  	rotateKey, ok := link.(*sig3.RotateKey)
    39  	if !ok {
    40  		return nil
    41  	}
    42  	rkex, err := rotateKey.Export()
    43  	if err != nil {
    44  		return err
    45  	}
    46  	ret.Inner[q] = *rkex
    47  
    48  	// Because this link isn't stubbed, we can bump the `LastFull` field
    49  	// forward if it's one more than previous. ret.LastFull will start at 0
    50  	// so this should work for the first link.
    51  	if ret.LastFull+1 == q {
    52  		ret.LastFull = q
    53  	}
    54  
    55  	// For each PTK (right now we really only expect one - the Reader PTK),
    56  	// update our maximum PTK generation
    57  	for _, ptk := range rotateKey.PTKs() {
    58  		max, ok := ret.LastPerTeamKeys[ptk.PTKType]
    59  		if !ok || max < q {
    60  			ret.LastPerTeamKeys[ptk.PTKType] = q
    61  		}
    62  		if ptk.PTKType == keybase1.PTKType_READER {
    63  			_, found := ret.ReaderPerTeamKeys[ptk.Generation]
    64  			if found {
    65  				return newRepeatPTKGenerationError(ptk.Generation, "clashes another hidden link")
    66  			}
    67  			ret.ReaderPerTeamKeys[ptk.Generation] = q
    68  		}
    69  	}
    70  	ret.MerkleRoots[q] = link.Inner().MerkleRoot.Export()
    71  
    72  	return nil
    73  }
    74  
    75  // GenerateKeyRotationParams are for generating a sig3 KeyRotation to store on the hidden team chain.
    76  // Fill in all parameters of the struct (there were too many to pass as a list)
    77  type GenerateKeyRotationParams struct {
    78  	TeamID           keybase1.TeamID
    79  	IsPublic         bool
    80  	IsImplicit       bool
    81  	MerkleRoot       *libkb.MerkleRoot
    82  	Me               libkb.UserForSignatures
    83  	SigningKey       libkb.GenericKey
    84  	MainPrev         keybase1.LinkTriple
    85  	HiddenPrev       *keybase1.LinkTriple
    86  	Gen              keybase1.PerTeamKeyGeneration
    87  	NewSigningKey    libkb.NaclSigningKeyPair
    88  	NewEncryptionKey libkb.NaclDHKeyPair
    89  	Check            keybase1.PerTeamSeedCheck
    90  	Admin            *sig3.ChainLocation
    91  }
    92  
    93  // GenerateKeyRotation generates and signs a new sig3 KeyRotation. The result can be passed to
    94  // sig/multi.json and stored along with other sig1, sig2 or sig3 signatures in an atomic transaction.
    95  func GenerateKeyRotation(mctx libkb.MetaContext, p GenerateKeyRotationParams) (ret *libkb.SigMultiItem, ratchets *keybase1.HiddenTeamChainRatchetSet, err error) {
    96  
    97  	s3, ratchets, err := generateKeyRotationSig3(mctx, p)
    98  	if err != nil {
    99  		return nil, nil, err
   100  	}
   101  
   102  	sigMultiItem := &libkb.SigMultiItem{
   103  		SigningKID: p.SigningKey.GetKID(),
   104  		Type:       "team.rotate_key_hidden",
   105  		SeqType:    sig3.ChainTypeTeamPrivateHidden,
   106  		TeamID:     p.TeamID,
   107  		Sig3: &libkb.Sig3{
   108  			Inner: s3.Inner,
   109  			Outer: s3.Outer,
   110  			Sig:   s3.Sig,
   111  		},
   112  		PublicKeys: &libkb.SigMultiItemPublicKeys{
   113  			Encryption: p.NewEncryptionKey.GetKID(),
   114  			Signing:    p.NewSigningKey.GetKID(),
   115  		},
   116  		Version: 3,
   117  	}
   118  
   119  	return sigMultiItem, ratchets, nil
   120  }
   121  
   122  func generateKeyRotationSig3(mctx libkb.MetaContext, p GenerateKeyRotationParams) (ret *sig3.ExportJSON, ratchets *keybase1.HiddenTeamChainRatchetSet, err error) {
   123  
   124  	outer := sig3.OuterLink{}
   125  	if p.HiddenPrev != nil {
   126  		outer.Seqno = p.HiddenPrev.Seqno + 1
   127  		if p.HiddenPrev.LinkID.IsNil() {
   128  			return nil, nil, NewGenerateError("unexpected nil prev")
   129  		}
   130  		tmp, err := sig3.ImportLinkID(p.HiddenPrev.LinkID)
   131  		if err != nil {
   132  			return nil, nil, err
   133  		}
   134  		outer.Prev = tmp
   135  	} else {
   136  		outer.Seqno = keybase1.Seqno(1)
   137  	}
   138  	tmp, err := sig3.ImportTail(p.MainPrev)
   139  	if err != nil {
   140  		return nil, nil, err
   141  	}
   142  	rsq := p.MerkleRoot.Seqno()
   143  	if rsq == nil {
   144  		return nil, nil, NewGenerateError("cannot work with a nil merkle root seqno")
   145  	}
   146  	teamIDimport, err := sig3.ImportTeamID(p.TeamID)
   147  	if err != nil {
   148  		return nil, nil, err
   149  	}
   150  	now := mctx.G().Clock().Now()
   151  	inner := sig3.InnerLink{
   152  		Ctime: sig3.TimeSec(now.Unix()),
   153  		ClientInfo: &sig3.ClientInfo{
   154  			Desc:    libkb.GoClientID,
   155  			Version: libkb.Version,
   156  		},
   157  		MerkleRoot: sig3.MerkleRoot{
   158  			Ctime: sig3.TimeSec(p.MerkleRoot.Ctime()),
   159  			Hash:  p.MerkleRoot.HashMeta(),
   160  			Seqno: *rsq,
   161  		},
   162  		ParentChain: *tmp,
   163  		Signer: sig3.Signer{
   164  			UID:         sig3.ImportUID(p.Me.GetUID()),
   165  			KID:         sig3.ImportKID(p.SigningKey.GetKID()),
   166  			EldestSeqno: p.Me.GetEldestSeqno(),
   167  		},
   168  		Team: &sig3.Team{
   169  			Admin:      p.Admin,
   170  			TeamID:     *teamIDimport,
   171  			IsPublic:   p.IsPublic,
   172  			IsImplicit: p.IsImplicit,
   173  		},
   174  	}
   175  	checkPostImage, err := p.Check.Hash()
   176  	if err != nil {
   177  		return nil, nil, err
   178  	}
   179  	rkb := sig3.RotateKeyBody{
   180  		PTKs: []sig3.PerTeamKey{
   181  			{
   182  				AppkeyDerivationVersion: sig3.AppkeyDerivationXOR,
   183  				Generation:              p.Gen,
   184  				SeedCheck:               *checkPostImage,
   185  				EncryptionKID:           p.NewEncryptionKey.GetBinaryKID(),
   186  				SigningKID:              p.NewSigningKey.GetBinaryKID(),
   187  				PTKType:                 keybase1.PTKType_READER,
   188  			},
   189  		},
   190  	}
   191  
   192  	keyPair := func(g libkb.GenericKey) (*sig3.KeyPair, error) {
   193  		signing, ok := g.(libkb.NaclSigningKeyPair)
   194  		if !ok {
   195  			return nil, NewGenerateError("bad key pair, wrong type: %T", g)
   196  		}
   197  		if signing.Private == nil {
   198  			return nil, NewGenerateError("bad key pair, got null private key")
   199  		}
   200  		return sig3.NewKeyPair(*signing.Private, g.GetBinaryKID()), nil
   201  	}
   202  
   203  	rk := sig3.NewRotateKey(outer, inner, rkb)
   204  	outerKeyPair, err := keyPair(p.SigningKey)
   205  	if err != nil {
   206  		return nil, nil, err
   207  	}
   208  	innerKeyPair, err := keyPair(p.NewSigningKey)
   209  	if err != nil {
   210  		return nil, nil, err
   211  	}
   212  
   213  	sig, err := rk.Sign(*outerKeyPair, []sig3.KeyPair{*innerKeyPair})
   214  	if err != nil {
   215  		return nil, nil, err
   216  	}
   217  	bun, err := sig.Export()
   218  	if err != nil {
   219  		return nil, nil, err
   220  	}
   221  	outer = sig.Outer
   222  	outerHash, err := outer.Hash()
   223  	if err != nil {
   224  		return nil, nil, err
   225  	}
   226  	ratchets = &keybase1.HiddenTeamChainRatchetSet{}
   227  	ratchets.Add(keybase1.RatchetType_SELF,
   228  		keybase1.LinkTripleAndTime{
   229  			Triple: keybase1.LinkTriple{
   230  				Seqno:   outer.Seqno,
   231  				SeqType: sig3.ChainTypeTeamPrivateHidden,
   232  				LinkID:  outerHash.Export(),
   233  			},
   234  			Time: keybase1.ToTime(now),
   235  		},
   236  	)
   237  	return &bun, ratchets, nil
   238  }
   239  
   240  func CheckFeatureGateForSupportWithRotationType(mctx libkb.MetaContext, teamID keybase1.TeamID, rt keybase1.RotationType) (ret keybase1.RotationType, err error) {
   241  	if rt == keybase1.RotationType_VISIBLE {
   242  		return rt, nil
   243  	}
   244  	ok, err := checkFeatureGateForSupport(mctx, teamID)
   245  	if err != nil {
   246  		return rt, err
   247  	}
   248  
   249  	switch {
   250  	case rt == keybase1.RotationType_CLKR && !ok:
   251  		return keybase1.RotationType_VISIBLE, nil
   252  	case rt == keybase1.RotationType_CLKR && ok:
   253  		return keybase1.RotationType_HIDDEN, nil
   254  
   255  	case rt == keybase1.RotationType_HIDDEN && ok:
   256  		return keybase1.RotationType_HIDDEN, nil
   257  	case rt == keybase1.RotationType_HIDDEN && !ok:
   258  		return keybase1.RotationType_HIDDEN, NewHiddenChainNotSupportedError(teamID)
   259  
   260  	default:
   261  		return keybase1.RotationType_HIDDEN, fmt.Errorf("unhandled case")
   262  	}
   263  }
   264  
   265  type rawSupport struct {
   266  	Status  libkb.AppStatus `json:"status"`
   267  	Support bool            `json:"support"`
   268  }
   269  
   270  func (r *rawSupport) GetAppStatus() *libkb.AppStatus {
   271  	return &r.Status
   272  }
   273  
   274  func featureGateForTeamFromServer(mctx libkb.MetaContext, teamID keybase1.TeamID) (ok bool, err error) {
   275  	arg := libkb.NewAPIArg("team/supports_hidden_chain")
   276  	arg.SessionType = libkb.APISessionTypeREQUIRED
   277  	arg.Args = libkb.HTTPArgs{
   278  		"id": libkb.S{Val: string(teamID)},
   279  	}
   280  	var raw rawSupport
   281  	err = mctx.G().API.GetDecode(mctx, arg, &raw)
   282  	if err != nil {
   283  		return false, err
   284  	}
   285  	return raw.Support, nil
   286  }
   287  
   288  func checkFeatureGateForSupport(mctx libkb.MetaContext, teamID keybase1.TeamID) (ok bool, err error) {
   289  	userFlagEnabled := mctx.G().FeatureFlags.Enabled(mctx, libkb.FeatureCheckForHiddenChainSupport)
   290  	runmode := mctx.G().Env.GetRunMode()
   291  	if runmode != libkb.ProductionRunMode {
   292  		return true, nil
   293  	}
   294  	if runmode == libkb.ProductionRunMode && !userFlagEnabled {
   295  		return false, nil
   296  	}
   297  
   298  	return mctx.G().GetHiddenTeamChainManager().TeamSupportsHiddenChain(mctx, teamID)
   299  }
   300  
   301  func CheckFeatureGateForSupport(mctx libkb.MetaContext, teamID keybase1.TeamID) (err error) {
   302  	ok, err := checkFeatureGateForSupport(mctx, teamID)
   303  	if err != nil {
   304  		return err
   305  	}
   306  	if !ok {
   307  		return NewHiddenChainNotSupportedError(teamID)
   308  	}
   309  	return nil
   310  }
   311  
   312  func ProcessHiddenResponseFunc(m libkb.MetaContext, teamID keybase1.TeamID, apiRes *libkb.APIRes, blindRootHashStr string) (hiddenResp *libkb.MerkleHiddenResponse, err error) {
   313  	if CheckFeatureGateForSupport(m, teamID) != nil {
   314  		m.Debug("Skipped ProcessHiddenResponseFunc as the feature flag is off (%v)", err)
   315  		return &libkb.MerkleHiddenResponse{RespType: libkb.MerkleHiddenResponseTypeFLAGOFF}, nil
   316  	}
   317  
   318  	if blindRootHashStr == "" {
   319  		m.Debug("blind tree root not found in the main tree: %v", err)
   320  		// TODO: Y2K-770 Until the root of the blind tree starts getting
   321  		// included in the main tree, we can get such root from the server as an
   322  		// additional parameter and assume the server is honest.
   323  		blindRootHashStr, err = apiRes.Body.AtKey("last_blind_root_hash").GetString()
   324  		if err != nil {
   325  			return &libkb.MerkleHiddenResponse{RespType: libkb.MerkleHiddenResponseTypeNONE}, nil
   326  		}
   327  		m.Debug("the server is providing a blind tree root which is not included in the main tree. We trust the server on this as the blind tree is an experimental feature.")
   328  	}
   329  	blindRootHashBytes, err := hex.DecodeString(blindRootHashStr)
   330  	if err != nil {
   331  		return nil, err
   332  	}
   333  
   334  	return ParseAndVerifyCommittedHiddenLinkID(m, teamID, apiRes, merkletree2.Hash(blindRootHashBytes))
   335  }
   336  
   337  func ParseAndVerifyCommittedHiddenLinkID(m libkb.MetaContext, teamID keybase1.TeamID, apiRes *libkb.APIRes, blindHash merkletree2.Hash) (hiddenResp *libkb.MerkleHiddenResponse, err error) {
   338  	lastHiddenSeqnoInt, err := apiRes.Body.AtKey("last_hidden_seqno").GetInt()
   339  	if err != nil {
   340  		m.Debug("Error decoding last_hidden_seqno (%v), assuming the server did not send it.", err.Error())
   341  		return &libkb.MerkleHiddenResponse{RespType: libkb.MerkleHiddenResponseTypeNONE}, nil
   342  	}
   343  	lastHiddenSeqno := keybase1.Seqno(lastHiddenSeqnoInt)
   344  	return makeHiddenRespFromTeamLeaf(m, lastHiddenSeqno)
   345  }
   346  
   347  func makeHiddenRespFromTeamLeaf(m libkb.MetaContext, lastHiddenSeqno keybase1.Seqno) (hiddenResp *libkb.MerkleHiddenResponse, err error) {
   348  	return &libkb.MerkleHiddenResponse{
   349  		RespType:         libkb.MerkleHiddenResponseTypeOK,
   350  		UncommittedSeqno: lastHiddenSeqno,
   351  	}, nil
   352  }