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 }