github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/engine/saltpack_encrypt_test.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 package engine 5 6 import ( 7 "fmt" 8 "strings" 9 "testing" 10 11 "golang.org/x/net/context" 12 13 "github.com/keybase/client/go/libkb" 14 keybase1 "github.com/keybase/client/go/protocol/keybase1" 15 "github.com/keybase/client/go/saltpackkeystest" 16 "github.com/keybase/go-codec/codec" 17 "github.com/keybase/saltpack" 18 "github.com/stretchr/testify/require" 19 ) 20 21 type fakeSaltpackUI2 struct { 22 DidDecrypt bool 23 LastSender keybase1.SaltpackSender 24 } 25 26 func (s *fakeSaltpackUI2) SaltpackPromptForDecrypt(_ context.Context, arg keybase1.SaltpackPromptForDecryptArg, usedDelegateUI bool) (err error) { 27 s.DidDecrypt = true 28 s.LastSender = arg.Sender 29 return nil 30 } 31 32 func (s *fakeSaltpackUI2) SaltpackVerifySuccess(_ context.Context, arg keybase1.SaltpackVerifySuccessArg) error { 33 return nil 34 } 35 36 func (s *fakeSaltpackUI2) SaltpackVerifyBadSender(_ context.Context, arg keybase1.SaltpackVerifyBadSenderArg) error { 37 return fmt.Errorf("fakeSaltpackUI2 SaltpackVerifyBadSender error: %s", arg.Sender.SenderType.String()) 38 } 39 40 func TestSaltpackEncrypt(t *testing.T) { 41 tc := SetupEngineTest(t, "SaltpackEncrypt") 42 defer tc.Cleanup() 43 44 u1 := CreateAndSignupFakeUser(tc, "nalcp") 45 u2 := CreateAndSignupFakeUser(tc, "nalcp") 46 u3 := CreateAndSignupFakeUser(tc, "nalcp") 47 48 trackUI := &FakeIdentifyUI{ 49 Proofs: make(map[string]string), 50 } 51 uis := libkb.UIs{IdentifyUI: trackUI, SecretUI: u3.NewSecretUI()} 52 53 run := func(Recips []string) { 54 sink := libkb.NewBufferCloser() 55 arg := &SaltpackEncryptArg{ 56 Opts: keybase1.SaltpackEncryptOptions{ 57 Recipients: Recips, 58 UseEntityKeys: true, 59 }, 60 Source: strings.NewReader("id2 and encrypt, id2 and encrypt"), 61 Sink: sink, 62 } 63 64 eng := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface) 65 m := NewMetaContextForTest(tc).WithUIs(uis) 66 if err := RunEngine2(m, eng); err != nil { 67 t.Fatal(err) 68 } 69 70 out := sink.Bytes() 71 if len(out) == 0 { 72 t.Fatal("no output") 73 } 74 } 75 run([]string{u1.Username, u2.Username}) 76 77 // If we add ourselves, we should be smart and not error out 78 // (We are u3 in this case) 79 run([]string{u1.Username, u2.Username, u3.Username}) 80 } 81 82 // This is now the default behavior. Still good to test it though. Note that 83 // this flag is only meaningful in repudiable mode. 84 func TestSaltpackEncryptHideRecipients(t *testing.T) { 85 tc := SetupEngineTest(t, "SaltpackEncrypt") 86 defer tc.Cleanup() 87 88 u1 := CreateAndSignupFakeUser(tc, "nalcp") 89 u2 := CreateAndSignupFakeUser(tc, "nalcp") 90 u3 := CreateAndSignupFakeUser(tc, "nalcp") 91 92 trackUI := &FakeIdentifyUI{ 93 Proofs: make(map[string]string), 94 } 95 uis := libkb.UIs{IdentifyUI: trackUI, SecretUI: u3.NewSecretUI()} 96 97 run := func(Recips []string) { 98 sink := libkb.NewBufferCloser() 99 arg := &SaltpackEncryptArg{ 100 Opts: keybase1.SaltpackEncryptOptions{ 101 UseEntityKeys: true, 102 // There used to be a HideRecipients flag here, but this is now 103 // the default for encryption. 104 // (It's not really meaningful for signcryption mode, where the 105 // recipients are always opaque.) 106 AuthenticityType: keybase1.AuthenticityType_REPUDIABLE, 107 Recipients: Recips, 108 Binary: true, 109 }, 110 Source: strings.NewReader("id2 and encrypt, id2 and encrypt"), 111 Sink: sink, 112 } 113 114 eng := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface) 115 m := NewMetaContextForTest(tc).WithUIs(uis) 116 if err := RunEngine2(m, eng); err != nil { 117 t.Fatal(err) 118 } 119 120 out := sink.Bytes() 121 if len(out) == 0 { 122 t.Fatal("no output") 123 } 124 125 var header saltpack.EncryptionHeader 126 dec := codec.NewDecoderBytes(out, &codec.MsgpackHandle{WriteExt: true}) 127 var b []byte 128 if err := dec.Decode(&b); err != nil { 129 t.Fatal(err) 130 } 131 dec = codec.NewDecoderBytes(b, &codec.MsgpackHandle{WriteExt: true}) 132 if err := dec.Decode(&header); err != nil { 133 t.Fatal(err) 134 } 135 136 for _, receiver := range header.Receivers { 137 if receiver.ReceiverKID != nil { 138 t.Fatal("receiver KID included in anonymous saltpack header") 139 } 140 } 141 142 } 143 run([]string{u1.Username, u2.Username}) 144 145 // If we add ourselves, we should be smart and not error out 146 // (We are u3 in this case) 147 run([]string{u1.Username, u2.Username, u3.Username}) 148 } 149 150 func TestSaltpackEncryptAnonymousSigncryption(t *testing.T) { 151 tc := SetupEngineTest(t, "SaltpackEncrypt") 152 defer tc.Cleanup() 153 154 u1 := CreateAndSignupFakeUser(tc, "nalcp") 155 u2 := CreateAndSignupFakeUser(tc, "nalcp") 156 u3 := CreateAndSignupFakeUser(tc, "nalcp") 157 158 trackUI := &FakeIdentifyUI{ 159 Proofs: make(map[string]string), 160 } 161 saltpackUI := &fakeSaltpackUI2{} 162 uis := libkb.UIs{ 163 IdentifyUI: trackUI, 164 SecretUI: u3.NewSecretUI(), 165 SaltpackUI: saltpackUI, 166 } 167 168 run := func(Recips []string) { 169 encsink := libkb.NewBufferCloser() 170 encarg := &SaltpackEncryptArg{ 171 Opts: keybase1.SaltpackEncryptOptions{ 172 UseEntityKeys: true, 173 Recipients: Recips, 174 AuthenticityType: keybase1.AuthenticityType_ANONYMOUS, 175 Binary: true, 176 // HERE! This is what we're testing. (Signcryption mode is the 177 // default. EncryptionOnlyMode is false.) 178 }, 179 Source: strings.NewReader("id2 and encrypt, id2 and encrypt"), 180 Sink: encsink, 181 } 182 183 enceng := NewSaltpackEncrypt(encarg, NewSaltpackUserKeyfinderAsInterface) 184 m := NewMetaContextForTest(tc).WithUIs(uis) 185 if err := RunEngine2(m, enceng); err != nil { 186 t.Fatal(err) 187 } 188 189 encout := encsink.Bytes() 190 if len(encout) == 0 { 191 t.Fatal("no output") 192 } 193 194 // Decode the header. 195 var header saltpack.EncryptionHeader 196 hdec := codec.NewDecoderBytes(encout, &codec.MsgpackHandle{WriteExt: true}) 197 var hbytes []byte 198 if err := hdec.Decode(&hbytes); err != nil { 199 t.Fatal(err) 200 } 201 hdec = codec.NewDecoderBytes(hbytes, &codec.MsgpackHandle{WriteExt: true}) 202 if err := hdec.Decode(&header); err != nil { 203 t.Fatal(err) 204 } 205 206 // Necessary so that PUKs are used for decryption 207 initPerUserKeyringInTestContext(t, tc) 208 209 decsink := libkb.NewBufferCloser() 210 decarg := &SaltpackDecryptArg{ 211 Source: strings.NewReader(encsink.String()), 212 Sink: decsink, 213 } 214 deceng := NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t)) 215 if err := RunEngine2(m, deceng); err != nil { 216 t.Fatal(err) 217 } 218 219 if !saltpackUI.DidDecrypt { 220 t.Fatal("fake saltpackUI not called") 221 } 222 223 // The message should not contain the sender's public key (in the sender secretbox). 224 // Instead, the sender key should be the ephemeral key. 225 // This tests that the sender type is anonymous. 226 if saltpackUI.LastSender.SenderType != keybase1.SaltpackSenderType_ANONYMOUS { 227 t.Fatal("sender type not anonymous:", saltpackUI.LastSender.SenderType) 228 } 229 } 230 231 run([]string{u1.Username, u2.Username}) 232 233 // If we add ourselves, we should be smart and not error out 234 // (We are u3 in this case) 235 run([]string{u1.Username, u2.Username, u3.Username}) 236 } 237 238 func TestSaltpackEncryptSelfNoKey(t *testing.T) { 239 tc := SetupEngineTest(t, "SaltpackEncrypt") 240 defer tc.Cleanup() 241 242 _, passphrase := createFakeUserWithNoKeys(tc) 243 trackUI := &FakeIdentifyUI{ 244 Proofs: make(map[string]string), 245 } 246 uis := libkb.UIs{IdentifyUI: trackUI, SecretUI: &libkb.TestSecretUI{Passphrase: passphrase}} 247 248 sink := libkb.NewBufferCloser() 249 arg := &SaltpackEncryptArg{ 250 Opts: keybase1.SaltpackEncryptOptions{ 251 // Note: these users actually exist, but they do not have PUKs 252 Recipients: []string{"t_tracy+t_tracy@rooter", "t_george", "t_kb+gbrltest@twitter"}, 253 UseDeviceKeys: true, 254 }, 255 Source: strings.NewReader("track and encrypt, track and encrypt"), 256 Sink: sink, 257 } 258 259 eng := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface) 260 m := NewMetaContextForTest(tc).WithUIs(uis) 261 err := RunEngine2(m, eng) 262 if _, ok := err.(libkb.NoDeviceError); !ok { 263 t.Fatalf("expected error type libkb.NoDeviceError, got %T (%s)", err, err) 264 } 265 } 266 267 func TestSaltpackEncryptLoggedOut(t *testing.T) { 268 tc := SetupEngineTest(t, "SaltpackEncrypt") 269 defer tc.Cleanup() 270 271 trackUI := &FakeIdentifyUI{ 272 Proofs: make(map[string]string), 273 } 274 uis := libkb.UIs{IdentifyUI: trackUI, SecretUI: &libkb.TestSecretUI{}} 275 276 sink := libkb.NewBufferCloser() 277 arg := &SaltpackEncryptArg{ 278 Opts: keybase1.SaltpackEncryptOptions{ 279 // Note: these users actually exist, but they do not have PUKs 280 Recipients: []string{"t_tracy+t_tracy@rooter", "t_george", "t_kb+gbrltest@twitter"}, 281 UseDeviceKeys: true, 282 // Only anonymous mode works when you're logged out. 283 AuthenticityType: keybase1.AuthenticityType_ANONYMOUS, 284 NoSelfEncrypt: true, 285 }, 286 Source: strings.NewReader("track and encrypt, track and encrypt"), 287 Sink: sink, 288 } 289 290 eng := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface) 291 m := NewMetaContextForTest(tc).WithUIs(uis) 292 err := RunEngine2(m, eng) 293 if err != nil { 294 t.Fatalf("Got unexpected error: %s", err) 295 } 296 } 297 298 func TestSaltpackEncryptNoNaclOnlyPGP(t *testing.T) { 299 tc := SetupEngineTest(t, "SaltpackEncrypt") 300 defer tc.Cleanup() 301 302 u2 := createFakeUserWithPGPOnly(t, tc) 303 Logout(tc) 304 u1 := CreateAndSignupFakeUser(tc, "nalcp") 305 306 trackUI := &FakeIdentifyUI{ 307 Proofs: make(map[string]string), 308 } 309 uis := libkb.UIs{ 310 IdentifyUI: trackUI, 311 SecretUI: u1.NewSecretUI(), 312 SaltpackUI: &fakeSaltpackUI{}, 313 } 314 315 msg := "this will never work" 316 sink := libkb.NewBufferCloser() 317 arg := &SaltpackEncryptArg{ 318 Opts: keybase1.SaltpackEncryptOptions{ 319 Recipients: []string{u2.Username}, 320 NoSelfEncrypt: true, 321 UseDeviceKeys: true, 322 }, 323 Source: strings.NewReader(msg), 324 Sink: sink, 325 } 326 327 eng := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface) 328 m := NewMetaContextForTest(tc).WithUIs(uis) 329 err := RunEngine2(m, eng) 330 if perr, ok := err.(libkb.NoNaClEncryptionKeyError); !ok { 331 t.Fatalf("Got wrong error type: %T %v", err, err) 332 } else if !perr.HasPGPKey { 333 t.Fatalf("Should have a PGP key") 334 } else if perr.Username != u2.Username { 335 t.Fatalf("Wrong username") 336 } 337 } 338 339 func TestSaltpackEncryptNoSelf(t *testing.T) { 340 tc := SetupEngineTest(t, "SaltpackEncrypt") 341 defer tc.Cleanup() 342 343 u1 := CreateAndSignupFakeUser(tc, "nalcp") 344 u2 := CreateAndSignupFakeUser(tc, "nalcp") 345 346 msg := "for your eyes only (not even mine!)" 347 348 trackUI := &FakeIdentifyUI{ 349 Proofs: make(map[string]string), 350 } 351 uis := libkb.UIs{ 352 IdentifyUI: trackUI, 353 SecretUI: u2.NewSecretUI(), 354 SaltpackUI: &fakeSaltpackUI{}, 355 } 356 357 sink := libkb.NewBufferCloser() 358 arg := &SaltpackEncryptArg{ 359 Opts: keybase1.SaltpackEncryptOptions{ 360 Recipients: []string{u1.Username}, 361 NoSelfEncrypt: true, 362 UseDeviceKeys: true, 363 }, 364 Source: strings.NewReader(msg), 365 Sink: sink, 366 } 367 368 eng := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface) 369 m := NewMetaContextForTest(tc).WithUIs(uis) 370 if err := RunEngine2(m, eng); err != nil { 371 t.Fatal(err) 372 } 373 374 out := sink.Bytes() 375 if len(out) == 0 { 376 t.Fatal("no output") 377 } 378 379 // decrypt it 380 decoded := libkb.NewBufferCloser() 381 decarg := &SaltpackDecryptArg{ 382 Source: strings.NewReader(string(out)), 383 Sink: decoded, 384 } 385 dec := NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t)) 386 err := RunEngine2(m, dec) 387 388 decErr, ok := err.(libkb.DecryptionError) 389 if !ok { 390 t.Fatalf("Expected err type %T, but got %T", libkb.DecryptionError{}, err) 391 } 392 if _, ok = decErr.Cause.Err.(libkb.NoDecryptionKeyError); !ok { 393 t.Fatalf("Expected err Cause of type %T, but got %T", libkb.NoDecryptionKeyError{}, decErr.Cause.Err) 394 } 395 396 Logout(tc) 397 err = u1.Login(tc.G) 398 require.NoError(t, err) 399 400 m = m.WithSecretUI(u1.NewSecretUI()) 401 decarg.Source = strings.NewReader(string(out)) 402 dec = NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t)) 403 err = RunEngine2(m, dec) 404 if err != nil { 405 t.Fatal(err) 406 } 407 decmsg := decoded.String() 408 if decmsg != msg { 409 t.Errorf("decoded: %s, expected: %s", decmsg, msg) 410 } 411 } 412 413 func TestSaltpackEncryptBinary(t *testing.T) { 414 tc := SetupEngineTest(t, "SaltpackEncryptBinary") 415 defer tc.Cleanup() 416 fu := CreateAndSignupFakeUser(tc, "enc") 417 418 // encrypt a message 419 msg := "10 days in Japan" 420 sink := libkb.NewBufferCloser() 421 uis := libkb.UIs{ 422 IdentifyUI: &FakeIdentifyUI{}, 423 SecretUI: fu.NewSecretUI(), 424 LogUI: tc.G.UI.GetLogUI(), 425 SaltpackUI: &fakeSaltpackUI{}, 426 } 427 // Should encrypt for self, too. 428 arg := &SaltpackEncryptArg{ 429 Source: strings.NewReader(msg), 430 Sink: sink, 431 Opts: keybase1.SaltpackEncryptOptions{ 432 Binary: true, 433 UseEntityKeys: true, 434 }, 435 } 436 enc := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface) 437 m := NewMetaContextForTest(tc).WithUIs(uis) 438 if err := RunEngine2(m, enc); err != nil { 439 t.Fatal(err) 440 } 441 out := sink.String() 442 443 // Necessary so that PUKs are used for decryption 444 initPerUserKeyringInTestContext(t, tc) 445 446 // decrypt it 447 decoded := libkb.NewBufferCloser() 448 decarg := &SaltpackDecryptArg{ 449 Source: strings.NewReader(out), 450 Sink: decoded, 451 } 452 dec := NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t)) 453 if err := RunEngine2(m, dec); err != nil { 454 t.Fatal(err) 455 } 456 decmsg := decoded.String() 457 if decmsg != msg { 458 t.Errorf("decoded: %s, expected: %s", decmsg, msg) 459 } 460 } 461 462 func TestSaltpackEncryptForceVersion(t *testing.T) { 463 tc := SetupEngineTest(t, "SaltpackEncrypt") 464 defer tc.Cleanup() 465 466 u1 := CreateAndSignupFakeUser(tc, "nalcp") 467 468 trackUI := &FakeIdentifyUI{ 469 Proofs: make(map[string]string), 470 } 471 uis := libkb.UIs{IdentifyUI: trackUI, SecretUI: u1.NewSecretUI()} 472 473 run := func(versionFlag int, majorVersionExpected int) { 474 sink := libkb.NewBufferCloser() 475 arg := &SaltpackEncryptArg{ 476 Opts: keybase1.SaltpackEncryptOptions{ 477 // Encryption only mode is required to set version 1. 478 AuthenticityType: keybase1.AuthenticityType_REPUDIABLE, 479 Recipients: []string{u1.Username}, 480 Binary: true, 481 SaltpackVersion: versionFlag, // This is what we're testing! 482 UseEntityKeys: true, 483 }, 484 Source: strings.NewReader("testing version flag"), 485 Sink: sink, 486 } 487 488 eng := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface) 489 m := NewMetaContextForTest(tc).WithUIs(uis) 490 if err := RunEngine2(m, eng); err != nil { 491 t.Fatal(err) 492 } 493 494 out := sink.Bytes() 495 if len(out) == 0 { 496 t.Fatal("no output") 497 } 498 499 var header saltpack.EncryptionHeader 500 dec := codec.NewDecoderBytes(out, &codec.MsgpackHandle{WriteExt: true}) 501 var b []byte 502 if err := dec.Decode(&b); err != nil { 503 t.Fatal(err) 504 } 505 dec = codec.NewDecoderBytes(b, &codec.MsgpackHandle{WriteExt: true}) 506 if err := dec.Decode(&header); err != nil { 507 t.Fatal(err) 508 } 509 510 if header.Version.Major != majorVersionExpected { 511 t.Fatalf("passed saltpack version %d and expected major version %d, found %d", versionFlag, majorVersionExpected, header.Version.Major) 512 } 513 } 514 515 // 0 means the default, which is major version 2. 516 run(0, 2) 517 run(1, 1) 518 run(2, 2) 519 }