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

     1  package git
     2  
     3  import (
     4  	"testing"
     5  
     6  	"golang.org/x/crypto/nacl/secretbox"
     7  	"golang.org/x/net/context"
     8  
     9  	"github.com/keybase/client/go/externals"
    10  	"github.com/keybase/client/go/kbtest"
    11  	"github.com/keybase/client/go/libkb"
    12  	"github.com/keybase/client/go/protocol/keybase1"
    13  	"github.com/keybase/client/go/teams"
    14  	"github.com/keybase/client/go/teams/hidden"
    15  	"github.com/stretchr/testify/require"
    16  
    17  	insecureTriplesec "github.com/keybase/go-triplesec-insecure"
    18  )
    19  
    20  func InstallInsecureTriplesec(g *libkb.GlobalContext) {
    21  	g.NewTriplesec = func(passphrase []byte, salt []byte) (libkb.Triplesec, error) {
    22  		warner := func() { g.Log.Warning("Installing insecure Triplesec with weak stretch parameters") }
    23  		isProduction := func() bool {
    24  			return g.Env.GetRunMode() == libkb.ProductionRunMode
    25  		}
    26  		return insecureTriplesec.NewCipher(passphrase, salt, libkb.ClientTriplesecVersion, warner, isProduction)
    27  	}
    28  }
    29  
    30  func setupTest(tb testing.TB, name string) libkb.TestContext {
    31  	tc := libkb.SetupTest(tb, name, 1)
    32  	tc.G.SetProofServices(externals.NewProofServices(tc.G))
    33  	InstallInsecureTriplesec(tc.G)
    34  	teams.NewTeamLoaderAndInstall(tc.G)
    35  	teams.NewFastTeamLoaderAndInstall(tc.G)
    36  	teams.NewAuditorAndInstall(tc.G)
    37  	hidden.NewChainManagerAndInstall(tc.G)
    38  	return tc
    39  }
    40  
    41  func createRootTeam(tc libkb.TestContext) keybase1.TeamID {
    42  	u, err := kbtest.CreateAndSignupFakeUser("c", tc.G)
    43  	require.NoError(tc.T, err)
    44  	teamName, err := keybase1.TeamNameFromString("T" + u.Username + "T")
    45  	require.NoError(tc.T, err)
    46  	_, err = teams.CreateRootTeam(context.Background(), tc.G, teamName.String(), keybase1.TeamSettings{})
    47  	require.NoError(tc.T, err)
    48  	return teamName.ToPrivateTeamID()
    49  }
    50  
    51  func createImplicitTeam(tc libkb.TestContext, public bool) keybase1.TeamID {
    52  	u, err := kbtest.CreateAndSignupFakeUser("c", tc.G)
    53  	require.NoError(tc.T, err)
    54  	team, _, _, err := teams.LookupOrCreateImplicitTeam(context.TODO(), tc.G, u.Username, public)
    55  	require.NoError(tc.T, err)
    56  	require.Equal(tc.T, public, team.ID.IsPublic())
    57  	return team.ID
    58  }
    59  
    60  func setupBox(t *testing.T) (libkb.TestContext, *Crypto, keybase1.TeamIDWithVisibility, *keybase1.EncryptedGitMetadata) {
    61  	tc := setupTest(t, "crypto")
    62  
    63  	teamID := createRootTeam(tc)
    64  	teamSpec := keybase1.TeamIDWithVisibility{
    65  		TeamID:     teamID,
    66  		Visibility: keybase1.TLFVisibility_PRIVATE,
    67  	}
    68  	plaintext, err := libkb.RandBytes(80)
    69  	require.NoError(tc.T, err)
    70  
    71  	c := NewCrypto(tc.G)
    72  	boxed, err := c.Box(context.Background(), plaintext, teamSpec)
    73  	require.NoError(tc.T, err)
    74  	require.NotNil(tc.T, boxed)
    75  	require.EqualValues(tc.T, boxed.Gen, 1)
    76  	require.Len(tc.T, boxed.N, libkb.NaclDHNonceSize)
    77  	require.NotZero(tc.T, boxed.N)
    78  	require.NotEmpty(tc.T, boxed.E)
    79  
    80  	return tc, c, teamSpec, boxed
    81  }
    82  
    83  func TestCryptoUnbox(t *testing.T) {
    84  	testCryptoUnbox(t, false, false)
    85  	testCryptoUnbox(t, true, false)
    86  	testCryptoUnbox(t, true, true)
    87  }
    88  
    89  func testCryptoUnbox(t *testing.T, implicit, public bool) {
    90  	t.Logf("running with implicit:%v public:%v", implicit, public)
    91  
    92  	visibility := keybase1.TLFVisibility_PRIVATE
    93  	if public {
    94  		visibility = keybase1.TLFVisibility_PUBLIC
    95  	}
    96  
    97  	tc := setupTest(t, "crypto")
    98  	defer tc.Cleanup()
    99  	var teamID keybase1.TeamID
   100  	if implicit {
   101  		teamID = createImplicitTeam(tc, public)
   102  	} else {
   103  		if public {
   104  			t.Fatalf("public teams not supported")
   105  		}
   106  		teamID = createRootTeam(tc)
   107  	}
   108  	require.Equal(t, public, teamID.IsPublic())
   109  
   110  	teamSpec := keybase1.TeamIDWithVisibility{
   111  		TeamID:     teamID,
   112  		Visibility: visibility,
   113  	}
   114  	plaintext, err := libkb.RandBytes(80)
   115  	require.NoError(tc.T, err)
   116  
   117  	loadTeam := func() *teams.Team {
   118  		team, err := teams.Load(context.TODO(), tc.G, keybase1.LoadTeamArg{
   119  			ID:          teamID,
   120  			Public:      public,
   121  			ForceRepoll: true,
   122  		})
   123  		require.NoError(t, err)
   124  		return team
   125  	}
   126  
   127  	c := NewCrypto(tc.G)
   128  
   129  	for i := 1; i <= 3; i++ {
   130  		t.Logf("rotation:%v", i)
   131  
   132  		boxed, err := c.Box(context.Background(), plaintext, teamSpec)
   133  		require.NoError(tc.T, err)
   134  		require.NotNil(tc.T, boxed)
   135  		if public {
   136  			// public always uses generation 1 for publicCryptKey
   137  			require.EqualValues(tc.T, 1, boxed.Gen)
   138  		} else {
   139  			require.EqualValues(tc.T, i, boxed.Gen)
   140  		}
   141  		require.Len(tc.T, boxed.N, libkb.NaclDHNonceSize)
   142  		require.NotEmpty(tc.T, boxed.E)
   143  
   144  		unboxed, err := c.Unbox(context.Background(), teamSpec, boxed)
   145  		require.NoError(tc.T, err)
   146  		require.NotNil(tc.T, unboxed)
   147  		require.Equal(tc.T, plaintext, unboxed)
   148  
   149  		var canOpenWithPublicKey bool
   150  		{
   151  			var encKey [libkb.NaclSecretBoxKeySize]byte = publicCryptKey.Key
   152  			var naclNonce [libkb.NaclDHNonceSize]byte = boxed.N
   153  			decrypted, ok := secretbox.Open(nil, boxed.E, &naclNonce, &encKey)
   154  			canOpenWithPublicKey = ok && libkb.SecureByteArrayEq(plaintext, decrypted)
   155  		}
   156  		require.Equal(t, public, canOpenWithPublicKey, "should only be able to open with public key if public")
   157  
   158  		team := loadTeam()
   159  		err = team.Rotate(context.TODO(), keybase1.RotationType_VISIBLE)
   160  		require.NoError(t, err)
   161  		loadTeam() // load again to get the new key
   162  	}
   163  }
   164  
   165  func TestCryptoVisibility(t *testing.T) {
   166  	tc := setupTest(t, "crypto")
   167  	defer tc.Cleanup()
   168  
   169  	teamID := createRootTeam(tc)
   170  	teamSpecPublic := keybase1.TeamIDWithVisibility{
   171  		TeamID:     teamID,
   172  		Visibility: keybase1.TLFVisibility_PUBLIC, // PRIVATE is correct
   173  	}
   174  	teamSpecPrivate := keybase1.TeamIDWithVisibility{
   175  		TeamID:     teamID,
   176  		Visibility: keybase1.TLFVisibility_PRIVATE,
   177  	}
   178  	plaintext, err := libkb.RandBytes(80)
   179  	require.NoError(tc.T, err)
   180  
   181  	c := NewCrypto(tc.G)
   182  	boxed, err := c.Box(context.Background(), plaintext, teamSpecPublic)
   183  	require.Error(tc.T, err)
   184  	require.IsType(tc.T, libkb.TeamVisibilityError{}, err)
   185  	require.Nil(tc.T, boxed)
   186  
   187  	// fix it so we can box some data and test visibility on unbox
   188  	boxed, err = c.Box(context.Background(), plaintext, teamSpecPrivate)
   189  	require.NoError(tc.T, err)
   190  	require.NotNil(tc.T, boxed)
   191  
   192  	// this should fail with public spec
   193  	unboxed, err := c.Unbox(context.Background(), teamSpecPublic, boxed)
   194  	require.Error(tc.T, err)
   195  	require.IsType(tc.T, libkb.TeamVisibilityError{}, err)
   196  	require.Nil(tc.T, unboxed)
   197  
   198  	// and succeed with private spec
   199  	unboxed, err = c.Unbox(context.Background(), teamSpecPrivate, boxed)
   200  	require.NoError(tc.T, err)
   201  	require.NotNil(tc.T, unboxed)
   202  }
   203  
   204  func TestCryptoKeyGen(t *testing.T) {
   205  	tc, c, teamSpec, boxed := setupBox(t)
   206  	defer tc.Cleanup()
   207  
   208  	// choose an invalid key generation
   209  	boxed.Gen = 2
   210  	unboxed, err := c.Unbox(context.Background(), teamSpec, boxed)
   211  	require.Error(tc.T, err)
   212  	require.Equal(tc.T, "FTL Missing seed at generation: 2", err.Error())
   213  	require.Nil(tc.T, unboxed)
   214  }
   215  
   216  func TestCryptoNonce(t *testing.T) {
   217  	tc, c, teamSpec, boxed := setupBox(t)
   218  	defer tc.Cleanup()
   219  
   220  	// flip nonce bit
   221  	boxed.N[4] ^= 0x10
   222  	unboxed, err := c.Unbox(context.Background(), teamSpec, boxed)
   223  	require.Error(tc.T, err)
   224  	require.IsType(tc.T, libkb.DecryptOpenError{}, err)
   225  	require.Nil(tc.T, unboxed)
   226  }
   227  
   228  func TestCryptoData(t *testing.T) {
   229  	tc, c, teamSpec, boxed := setupBox(t)
   230  	defer tc.Cleanup()
   231  
   232  	if len(boxed.E) < 4 {
   233  		tc.T.Fatalf("very small encrypted data size: %d", len(boxed.E))
   234  	}
   235  
   236  	// flip data bit
   237  	boxed.E[3] ^= 0x10
   238  	unboxed, err := c.Unbox(context.Background(), teamSpec, boxed)
   239  	require.Error(tc.T, err)
   240  	require.IsType(tc.T, libkb.DecryptOpenError{}, err)
   241  	require.Nil(tc.T, unboxed)
   242  }
   243  
   244  func TestCryptoVersion(t *testing.T) {
   245  	tc, c, teamSpec, boxed := setupBox(t)
   246  	defer tc.Cleanup()
   247  
   248  	// bump version
   249  	boxed.V++
   250  	unboxed, err := c.Unbox(context.Background(), teamSpec, boxed)
   251  	require.Error(tc.T, err)
   252  	require.Nil(tc.T, unboxed)
   253  }