github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/systests/saltpack_test.go (about) 1 package systests 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "strings" 7 "testing" 8 9 "github.com/keybase/client/go/saltpackkeys" 10 11 "github.com/keybase/client/go/engine" 12 "github.com/keybase/client/go/externalstest" 13 "github.com/keybase/client/go/kbtest" 14 15 "golang.org/x/net/context" 16 17 "github.com/keybase/client/go/libkb" 18 "github.com/keybase/client/go/protocol/keybase1" 19 "github.com/keybase/client/go/teams" 20 "github.com/stretchr/testify/require" 21 ) 22 23 func SetupTestWithInsecureTriplesec(tb libkb.TestingTB, name string) (tc libkb.TestContext) { 24 // SetupTest ignores the depth argument, so we can safely pass 0. 25 tc = externalstest.SetupTest(tb, name, 0) 26 27 // use an insecure triplesec in tests 28 installInsecureTriplesec(tc.G) 29 30 return tc 31 } 32 33 func createTeam(tc libkb.TestContext) (keybase1.TeamID, string) { 34 teams.ServiceInit(tc.G) 35 36 b, err := libkb.RandBytes(4) 37 require.NoError(tc.T, err) 38 name := "t_" + hex.EncodeToString(b) 39 teamID, err := teams.CreateRootTeam(context.TODO(), tc.G, name, keybase1.TeamSettings{}) 40 require.NoError(tc.T, err) 41 require.NotNil(tc.T, teamID) 42 43 return *teamID, name 44 } 45 46 type fakeSaltpackUI struct{} 47 48 var _ libkb.SaltpackUI = fakeSaltpackUI{} 49 50 func (s fakeSaltpackUI) SaltpackPromptForDecrypt(_ context.Context, arg keybase1.SaltpackPromptForDecryptArg, usedDelegateUI bool) (err error) { 51 return nil 52 } 53 54 func (s fakeSaltpackUI) SaltpackVerifySuccess(_ context.Context, arg keybase1.SaltpackVerifySuccessArg) error { 55 return nil 56 } 57 58 type FakeBadSenderError struct { 59 senderType keybase1.SaltpackSenderType 60 } 61 62 func (e *FakeBadSenderError) Error() string { 63 return fmt.Sprintf("fakeSaltpackUI bad sender error: %s", e.senderType.String()) 64 } 65 66 func (s fakeSaltpackUI) SaltpackVerifyBadSender(_ context.Context, arg keybase1.SaltpackVerifyBadSenderArg) error { 67 return &FakeBadSenderError{arg.Sender.SenderType} 68 } 69 70 func TestSaltpackEncryptDecryptForTeams(t *testing.T) { 71 tc := SetupTestWithInsecureTriplesec(t, "SysSpckEncDecTeam") 72 defer tc.Cleanup() 73 74 u1, err := kbtest.CreateAndSignupFakeUser("spkfe", tc.G) 75 require.NoError(t, err) 76 u2, err := kbtest.CreateAndSignupFakeUser("spkfe", tc.G) 77 require.NoError(t, err) 78 79 msg := "this message will be encrypted for a team" 80 81 _, teamName := createTeam(tc) 82 _, err = teams.AddMember(context.TODO(), tc.G, teamName, u1.Username, keybase1.TeamRole_WRITER, nil) 83 require.NoError(t, err) 84 85 trackUI := &kbtest.FakeIdentifyUI{ 86 Proofs: make(map[string]string), 87 } 88 uis2 := libkb.UIs{IdentifyUI: trackUI, SecretUI: u2.NewSecretUI()} 89 90 sink := libkb.NewBufferCloser() 91 arg := &engine.SaltpackEncryptArg{ 92 Opts: keybase1.SaltpackEncryptOptions{ 93 TeamRecipients: []string{teamName}, 94 UseEntityKeys: true, 95 NoSelfEncrypt: true, 96 }, 97 Source: strings.NewReader(msg), 98 Sink: sink, 99 } 100 101 eng := engine.NewSaltpackEncrypt(arg, saltpackkeys.NewSaltpackRecipientKeyfinderEngineAsInterface) 102 m2 := libkb.NewMetaContextForTest(tc).WithUIs(uis2) 103 if err := engine.RunEngine2(m2, eng); err != nil { 104 t.Fatal(err) 105 } 106 107 out := sink.String() 108 if len(out) == 0 { 109 t.Fatal("no output") 110 } 111 112 t.Logf("encrypted data: %s", out) 113 114 // switch to another team member and decrypt 115 kbtest.Logout(tc) 116 err = u1.Login(tc.G) 117 require.NoError(t, err) 118 uis1 := libkb.UIs{IdentifyUI: trackUI, SecretUI: u1.NewSecretUI(), SaltpackUI: fakeSaltpackUI{}} 119 m1 := libkb.NewMetaContextForTest(tc).WithUIs(uis1) 120 121 decoded := libkb.NewBufferCloser() 122 decarg := &engine.SaltpackDecryptArg{ 123 Source: strings.NewReader(out), 124 Sink: decoded, 125 } 126 dec := engine.NewSaltpackDecrypt(decarg, saltpackkeys.NewKeyPseudonymResolver(m1)) 127 if err := engine.RunEngine2(m1, dec); err != nil { 128 t.Fatal(err) 129 } 130 decmsg := decoded.String() 131 require.Equal(t, msg, decmsg) 132 } 133 134 func TestSaltpackEncryptDecryptWithEntityKeysForTeams(t *testing.T) { 135 tt := newTeamTester(t) 136 defer tt.cleanup() 137 138 u1 := tt.addUser("u1sp") 139 u2 := tt.addUser("u2sp") 140 u3 := tt.addUser("u3sp") 141 142 // u2 creates the team, and adds u1 143 team := u2.createTeam() 144 u2.addTeamMember(team, u1.username, keybase1.TeamRole_WRITER) 145 146 msg := "this message will be encrypted for a team" 147 148 sink := libkb.NewBufferCloser() 149 arg := &engine.SaltpackEncryptArg{ 150 Opts: keybase1.SaltpackEncryptOptions{ 151 TeamRecipients: []string{team}, 152 NoSelfEncrypt: true, 153 UseEntityKeys: true, 154 }, 155 Source: strings.NewReader(msg), 156 Sink: sink, 157 } 158 159 trackUI := &kbtest.FakeIdentifyUI{ 160 Proofs: make(map[string]string), 161 } 162 uis1 := libkb.UIs{IdentifyUI: trackUI, SecretUI: u1.newSecretUI(), SaltpackUI: fakeSaltpackUI{}} 163 eng := engine.NewSaltpackEncrypt(arg, saltpackkeys.NewSaltpackRecipientKeyfinderEngineAsInterface) 164 m1 := libkb.NewMetaContextForTest(*u1.tc).WithUIs(uis1) 165 if err := engine.RunEngine2(m1, eng); err != nil { 166 t.Fatal(err) 167 } 168 169 out := sink.String() 170 if len(out) == 0 { 171 t.Fatal("no output") 172 } 173 174 t.Logf("encrypted data: %s", out) 175 176 // u1 can decrypt 177 decoded := libkb.NewBufferCloser() 178 decarg := &engine.SaltpackDecryptArg{ 179 Source: strings.NewReader(out), 180 Sink: decoded, 181 } 182 dec1 := engine.NewSaltpackDecrypt(decarg, saltpackkeys.NewKeyPseudonymResolver(m1)) 183 if err := engine.RunEngine2(m1, dec1); err != nil { 184 t.Fatal(err) 185 } 186 decmsg := decoded.String() 187 require.Equal(t, msg, decmsg) 188 189 // u2 can decrypt 190 uis2 := libkb.UIs{IdentifyUI: trackUI, SecretUI: u2.newSecretUI(), SaltpackUI: fakeSaltpackUI{}} 191 m2 := libkb.NewMetaContextForTest(*u2.tc).WithUIs(uis2) 192 193 decoded2 := libkb.NewBufferCloser() 194 decarg = &engine.SaltpackDecryptArg{ 195 Source: strings.NewReader(out), 196 Sink: decoded2, 197 } 198 dec2 := engine.NewSaltpackDecrypt(decarg, saltpackkeys.NewKeyPseudonymResolver(m2)) 199 if err := engine.RunEngine2(m2, dec2); err != nil { 200 t.Fatal(err) 201 } 202 decmsg = decoded2.String() 203 require.Equal(t, msg, decmsg) 204 205 // u1 leaves team, can't decrypt 206 u1.leave(team) 207 teams := u1.teamList("", false, false) 208 require.Len(t, teams.Teams, 0) 209 decoded3 := libkb.NewBufferCloser() 210 decarg = &engine.SaltpackDecryptArg{ 211 Source: strings.NewReader(out), 212 Sink: decoded3, 213 } 214 dec1 = engine.NewSaltpackDecrypt(decarg, saltpackkeys.NewKeyPseudonymResolver(m1)) 215 err := engine.RunEngine2(m1, dec1) 216 require.IsType(t, libkb.DecryptionError{}, err) 217 x, _ := err.(libkb.DecryptionError) 218 require.IsType(t, libkb.NoDecryptionKeyError{}, x.Cause.Err) 219 220 // u2 can still decrypt 221 teams = u2.teamList("", false, false) 222 require.Len(t, teams.Teams, 1) 223 decoded4 := libkb.NewBufferCloser() 224 decarg = &engine.SaltpackDecryptArg{ 225 Source: strings.NewReader(out), 226 Sink: decoded4, 227 } 228 dec2 = engine.NewSaltpackDecrypt(decarg, saltpackkeys.NewKeyPseudonymResolver(m2)) 229 if err := engine.RunEngine2(m2, dec2); err != nil { 230 t.Fatal(err) 231 } 232 decmsg = decoded4.String() 233 require.Equal(t, msg, decmsg) 234 235 // u2 adds u3, can decrypt 236 u2.addTeamMember(team, u3.username, keybase1.TeamRole_WRITER) 237 uis3 := libkb.UIs{IdentifyUI: trackUI, SecretUI: u3.newSecretUI(), SaltpackUI: fakeSaltpackUI{}} 238 m3 := libkb.NewMetaContextForTest(*u3.tc).WithUIs(uis3) 239 240 decoded5 := libkb.NewBufferCloser() 241 decarg = &engine.SaltpackDecryptArg{ 242 Source: strings.NewReader(out), 243 Sink: decoded5, 244 } 245 dec3 := engine.NewSaltpackDecrypt(decarg, saltpackkeys.NewKeyPseudonymResolver(m3)) 246 if err := engine.RunEngine2(m3, dec3); err != nil { 247 t.Fatal(err) 248 } 249 decmsg = decoded5.String() 250 require.Equal(t, msg, decmsg) 251 } 252 253 func TestSaltpackEncryptDecryptForImplicitTeams(t *testing.T) { 254 tt := newTeamTester(t) 255 defer tt.cleanup() 256 257 // u1 will send a message to u2@rooter (before u2 proves rooter) 258 u1 := tt.addUser("u1sp") 259 u2 := tt.addUser("u2sp") 260 261 msg := "this message will be encrypted for an implicit team" 262 263 trackUI := &kbtest.FakeIdentifyUI{ 264 Proofs: make(map[string]string), 265 } 266 uis1 := libkb.UIs{IdentifyUI: trackUI, SecretUI: u1.newSecretUI()} 267 268 sink := libkb.NewBufferCloser() 269 arg := &engine.SaltpackEncryptArg{ 270 Opts: keybase1.SaltpackEncryptOptions{ 271 Recipients: []string{(u2.username + "@rooter")}, 272 UseEntityKeys: true, 273 }, 274 Source: strings.NewReader(msg), 275 Sink: sink, 276 } 277 278 eng := engine.NewSaltpackEncrypt(arg, saltpackkeys.NewSaltpackRecipientKeyfinderEngineAsInterface) 279 m1 := libkb.NewMetaContextForTest(*u1.tc).WithUIs(uis1) 280 if err := engine.RunEngine2(m1, eng); err != nil { 281 t.Fatal(err) 282 } 283 284 out := sink.String() 285 if len(out) == 0 { 286 t.Fatal("no output") 287 } 288 289 t.Logf("encrypted data: %s", out) 290 291 // u2 has not proven rooter yet, they should not be able to decrypt 292 uis2 := libkb.UIs{IdentifyUI: trackUI, SecretUI: u2.newSecretUI(), SaltpackUI: fakeSaltpackUI{}} 293 m2 := libkb.NewMetaContextForTest(*u2.tc).WithUIs(uis2) 294 295 decoded := libkb.NewBufferCloser() 296 decarg := &engine.SaltpackDecryptArg{ 297 Source: strings.NewReader(out), 298 Sink: decoded, 299 } 300 dec := engine.NewSaltpackDecrypt(decarg, saltpackkeys.NewKeyPseudonymResolver(m2)) 301 err := engine.RunEngine2(m2, dec) 302 require.IsType(t, libkb.DecryptionError{}, err) 303 x, _ := err.(libkb.DecryptionError) 304 require.IsType(t, libkb.NoDecryptionKeyError{}, x.Cause.Err) 305 306 // Get current implicit team seqno so we can wait for it to be updated later 307 team, _, _, err := teams.LookupImplicitTeam(context.Background(), u1.tc.G, u1.username+","+u2.username+"@rooter", false, teams.ImplicitTeamOptions{}) 308 require.NoError(t, err) 309 nextSeqno := team.NextSeqno() 310 311 u2.proveRooter() 312 313 // Wait for u1 to add u2 to the implicit team 314 u2.kickTeamRekeyd() 315 u2.waitForTeamChangedGregor(team.ID, nextSeqno) 316 317 // Now decryption should succeed 318 decoded = libkb.NewBufferCloser() 319 decarg = &engine.SaltpackDecryptArg{ 320 Source: strings.NewReader(out), 321 Sink: decoded, 322 } 323 dec = engine.NewSaltpackDecrypt(decarg, saltpackkeys.NewKeyPseudonymResolver(m2)) 324 if err := engine.RunEngine2(m2, dec); err != nil { 325 t.Fatal(err) 326 } 327 decmsg := decoded.String() 328 require.Equal(t, msg, decmsg) 329 }