github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/saltpack_decrypt_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  	"errors"
     8  	"fmt"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/keybase/client/go/libkb"
    13  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    14  	"github.com/keybase/client/go/saltpackkeystest"
    15  	"github.com/keybase/saltpack"
    16  	"github.com/stretchr/testify/require"
    17  	"golang.org/x/net/context"
    18  )
    19  
    20  type fakeSaltpackUI struct{}
    21  
    22  func (s fakeSaltpackUI) SaltpackPromptForDecrypt(_ context.Context, arg keybase1.SaltpackPromptForDecryptArg, usedDelegateUI bool) (err error) {
    23  	return nil
    24  }
    25  
    26  func (s fakeSaltpackUI) SaltpackVerifySuccess(_ context.Context, arg keybase1.SaltpackVerifySuccessArg) error {
    27  	return nil
    28  }
    29  
    30  type FakeBadSenderError struct {
    31  	senderType keybase1.SaltpackSenderType
    32  }
    33  
    34  func (e *FakeBadSenderError) Error() string {
    35  	return fmt.Sprintf("fakeSaltpackUI bad sender error: %s", e.senderType.String())
    36  }
    37  
    38  func (s fakeSaltpackUI) SaltpackVerifyBadSender(_ context.Context, arg keybase1.SaltpackVerifyBadSenderArg) error {
    39  	return &FakeBadSenderError{arg.Sender.SenderType}
    40  }
    41  
    42  func initPerUserKeyringInTestContext(t *testing.T, tc libkb.TestContext) {
    43  	kr, err := tc.G.GetPerUserKeyring(context.Background())
    44  	require.NoError(t, err)
    45  	err = kr.Sync(libkb.NewMetaContext(context.Background(), tc.G))
    46  	require.NoError(t, err)
    47  }
    48  
    49  func TestSaltpackDecrypt(t *testing.T) {
    50  	tc := SetupEngineTest(t, "SaltpackDecrypt")
    51  	defer tc.Cleanup()
    52  	fu := CreateAndSignupFakeUser(tc, "naclp")
    53  
    54  	initPerUserKeyringInTestContext(t, tc)
    55  
    56  	// encrypt a message
    57  	msg := "10 days in Japan"
    58  	sink := libkb.NewBufferCloser()
    59  	uis := libkb.UIs{
    60  		IdentifyUI: &FakeIdentifyUI{},
    61  		SecretUI:   fu.NewSecretUI(),
    62  		LogUI:      tc.G.UI.GetLogUI(),
    63  		SaltpackUI: &fakeSaltpackUI{},
    64  	}
    65  	// Should encrypt for self, too.
    66  	arg := &SaltpackEncryptArg{
    67  		Opts: keybase1.SaltpackEncryptOptions{
    68  			UseEntityKeys: true,
    69  		},
    70  		Source: strings.NewReader(msg),
    71  		Sink:   sink,
    72  	}
    73  	enc := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface)
    74  	m := NewMetaContextForTest(tc).WithUIs(uis)
    75  	if err := RunEngine2(m, enc); err != nil {
    76  		t.Fatal(err)
    77  	}
    78  	out := sink.String()
    79  
    80  	t.Logf("encrypted data: %s", out)
    81  
    82  	// decrypt it
    83  	decoded := libkb.NewBufferCloser()
    84  	decarg := &SaltpackDecryptArg{
    85  		Source: strings.NewReader(out),
    86  		Sink:   decoded,
    87  	}
    88  	dec := NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t))
    89  	if err := RunEngine2(m, dec); err != nil {
    90  		t.Fatal(err)
    91  	}
    92  	decmsg := decoded.String()
    93  	if decmsg != msg {
    94  		t.Errorf("decoded: %s, expected: %s", decmsg, msg)
    95  	}
    96  
    97  	pgpMsg := `-----BEGIN PGP MESSAGE-----
    98  Version: GnuPG v1
    99  
   100  hQEMA5gKPw0B/gTfAQf+JacZcP+4d1cdmRV5qlrDUhK3qm5dtzAh8KE3z6OMSOmE
   101  fUAdMZweHZMkWA5C1OZbvZ6SKaFLFHjmiD0DWlcdiXsvgPH9RpTHOSrxdjRlBuwK
   102  JBz5OrDM/OStIam6jKcxBcrI43JkWOG64AOwJ4Rx3OjAnzbKJKeUCAaopbXc2M5O
   103  iyTPzEsexRFjSfPGRk9cQD5zfar3Qjk2cRWElgABiQczWtfNAQ3NyQLzmRU6mw+i
   104  ZLoViAwQm2BMYa2i6MYOJCQtxHLwZCtAbRXTGFZ2nP0gVVX50KIeL/rnzrQ4I05M
   105  CljEVk3BBSQBl3jqecfT2Ooh+rwgf3VSQ684HIEt5dI/Aama8l7S3ypwVyt8gWhN
   106  HTngZWUk8Tjn6Q8zrnnoB92G1G+rZHAiChgBFQCaYDBsWa0Pia6Vm+10OAIulGGj
   107  =pNG+
   108  -----END PGP MESSAGE-----
   109  `
   110  	decoded = libkb.NewBufferCloser()
   111  	decarg = &SaltpackDecryptArg{
   112  		Source: strings.NewReader(pgpMsg),
   113  		Sink:   decoded,
   114  	}
   115  	dec = NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t))
   116  	err := RunEngine2(m, dec)
   117  	if wse, ok := err.(libkb.WrongCryptoFormatError); !ok {
   118  		t.Fatalf("Wanted a WrongCryptoFormat error, but got %T (%v)", err, err)
   119  	} else if wse.Wanted != libkb.CryptoMessageFormatSaltpack ||
   120  		wse.Received != libkb.CryptoMessageFormatPGP ||
   121  		wse.Operation != "decrypt" {
   122  		t.Fatalf("Bad error: %v", wse)
   123  	}
   124  
   125  }
   126  
   127  type testDecryptSaltpackUI struct {
   128  	fakeSaltpackUI
   129  	f func(arg keybase1.SaltpackPromptForDecryptArg) error
   130  }
   131  
   132  func (t *testDecryptSaltpackUI) SaltpackPromptForDecrypt(_ context.Context, arg keybase1.SaltpackPromptForDecryptArg, usedDelegateUI bool) (err error) {
   133  	if t.f == nil {
   134  		return nil
   135  	}
   136  	return t.f(arg)
   137  }
   138  
   139  func TestSaltpackDecryptBrokenTrack(t *testing.T) {
   140  	tc := SetupEngineTest(t, "SaltpackDecrypt")
   141  	defer tc.Cleanup()
   142  	sigVersion := libkb.GetDefaultSigVersion(tc.G)
   143  
   144  	// create a user to track the proofUser
   145  	trackUser := CreateAndSignupFakeUser(tc, "naclp")
   146  	Logout(tc)
   147  
   148  	// create a user with a rooter proof
   149  	proofUser := CreateAndSignupFakeUser(tc, "naclp")
   150  	ui, _, err := proveRooter(tc.G, proofUser, sigVersion)
   151  	if err != nil {
   152  		t.Fatal(err)
   153  	}
   154  
   155  	spui := testDecryptSaltpackUI{}
   156  
   157  	// encrypt a message
   158  	msg := "10 days in Japan"
   159  	sink := libkb.NewBufferCloser()
   160  	uis := libkb.UIs{
   161  		IdentifyUI: &FakeIdentifyUI{},
   162  		SecretUI:   proofUser.NewSecretUI(),
   163  		LogUI:      tc.G.UI.GetLogUI(),
   164  		SaltpackUI: &spui,
   165  	}
   166  
   167  	arg := &SaltpackEncryptArg{
   168  		Source: strings.NewReader(msg),
   169  		Sink:   sink,
   170  		Opts: keybase1.SaltpackEncryptOptions{
   171  			NoSelfEncrypt: true,
   172  			UseEntityKeys: true,
   173  			Recipients: []string{
   174  				trackUser.Username,
   175  			},
   176  		},
   177  	}
   178  	enc := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface)
   179  	m := NewMetaContextForTest(tc).WithUIs(uis)
   180  	if err := RunEngine2(m, enc); err != nil {
   181  		t.Fatal(err)
   182  	}
   183  	out := sink.String()
   184  
   185  	// Also output a anonymous-sender message
   186  	arg.Opts.AuthenticityType = keybase1.AuthenticityType_ANONYMOUS
   187  	sink = libkb.NewBufferCloser()
   188  	arg.Source = strings.NewReader(msg)
   189  	arg.Sink = sink
   190  	enc = NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface)
   191  	if err := RunEngine2(m, enc); err != nil {
   192  		t.Fatal(err)
   193  	}
   194  	outHidden := sink.String()
   195  
   196  	Logout(tc)
   197  
   198  	// Now login as the track user and track the proofUser
   199  	trackUser.LoginOrBust(tc)
   200  	rbl := sb{
   201  		social:     true,
   202  		id:         proofUser.Username + "@rooter",
   203  		proofState: keybase1.ProofState_OK,
   204  	}
   205  	outcome := keybase1.IdentifyOutcome{
   206  		NumProofSuccesses: 1,
   207  		TrackStatus:       keybase1.TrackStatus_NEW_OK,
   208  	}
   209  	err = checkTrack(tc, trackUser, proofUser.Username, []sb{rbl}, &outcome, sigVersion)
   210  	if err != nil {
   211  		t.Fatal(err)
   212  	}
   213  
   214  	// decrypt it
   215  	decoded := libkb.NewBufferCloser()
   216  	decarg := &SaltpackDecryptArg{
   217  		Source: strings.NewReader(out),
   218  		Sink:   decoded,
   219  	}
   220  	initPerUserKeyringInTestContext(t, tc)
   221  	dec := NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t))
   222  	spui.f = func(arg keybase1.SaltpackPromptForDecryptArg) error {
   223  		if arg.Sender.SenderType != keybase1.SaltpackSenderType_TRACKING_OK {
   224  			t.Fatalf("Bad sender type: %v", arg.Sender.SenderType)
   225  		}
   226  		return nil
   227  	}
   228  	if err := RunEngine2(m, dec); err != nil {
   229  		t.Fatal(err)
   230  	}
   231  	decmsg := decoded.String()
   232  	// Should work!
   233  	if decmsg != msg {
   234  		t.Fatalf("decoded: %s, expected: %s", decmsg, msg)
   235  	}
   236  
   237  	// now decrypt the hidden-self message
   238  	decoded = libkb.NewBufferCloser()
   239  	decarg = &SaltpackDecryptArg{
   240  		Source: strings.NewReader(outHidden),
   241  		Sink:   decoded,
   242  	}
   243  	initPerUserKeyringInTestContext(t, tc)
   244  	dec = NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t))
   245  	spui.f = func(arg keybase1.SaltpackPromptForDecryptArg) error {
   246  		if arg.Sender.SenderType != keybase1.SaltpackSenderType_ANONYMOUS {
   247  			t.Fatalf("Bad sender type: %v", arg.Sender.SenderType)
   248  		}
   249  		return nil
   250  	}
   251  	if err := RunEngine2(m, dec); err != nil {
   252  		t.Fatal(err)
   253  	}
   254  	decmsg = decoded.String()
   255  	// Should work!
   256  	if decmsg != msg {
   257  		t.Fatalf("decoded: %s, expected: %s", decmsg, msg)
   258  	}
   259  
   260  	// remove the rooter proof to break the tracking statement
   261  	Logout(tc)
   262  	proofUser.LoginOrBust(tc)
   263  	if err := proveRooterRemove(tc.G, ui.postID); err != nil {
   264  		t.Fatal(err)
   265  	}
   266  
   267  	Logout(tc)
   268  
   269  	// Decrypt the message and fail, since our tracking statement is now
   270  	// broken.
   271  	trackUser.LoginOrBust(tc)
   272  	decoded = libkb.NewBufferCloser()
   273  	decarg = &SaltpackDecryptArg{
   274  		Source: strings.NewReader(out),
   275  		Sink:   decoded,
   276  		Opts: keybase1.SaltpackDecryptOptions{
   277  			ForceRemoteCheck: true,
   278  		},
   279  	}
   280  	initPerUserKeyringInTestContext(t, tc)
   281  	dec = NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t))
   282  	errTrackingBroke := errors.New("tracking broke")
   283  	spui.f = func(arg keybase1.SaltpackPromptForDecryptArg) error {
   284  		if arg.Sender.SenderType != keybase1.SaltpackSenderType_TRACKING_BROKE {
   285  			t.Fatalf("Bad sender type: %v", arg.Sender.SenderType)
   286  		}
   287  		return errTrackingBroke
   288  	}
   289  	err = RunEngine2(m, dec)
   290  	if decErr, ok := err.(libkb.DecryptionError); ok && decErr.Cause.Err != errTrackingBroke {
   291  		t.Fatalf("Expected an error %v; but got %v", errTrackingBroke, err)
   292  	}
   293  }
   294  
   295  // The error info that this test is looking for is only available in legacy
   296  // saltpack encryption (i.e. repudiable) mode messages (originally called
   297  // encryption-only mode). Modern repudiable messages always have all-anonymous-recipients,
   298  // and signcryption messages have opaque receivers.
   299  func TestSaltpackNoEncryptionForDevice(t *testing.T) {
   300  	// userX has one device (device X) and a paper key. userZ will encrypt a message for userX,
   301  	// then userX will add a new device (Y) which should not be able to decrypt the message
   302  	// (as we are not using PUKs) but will receive an helpful error message which tells them
   303  	// which other devices to use to decrypt.
   304  
   305  	// device X (provisioner) context:
   306  	tcX := SetupEngineTest(t, "kex2provision")
   307  	defer tcX.Cleanup()
   308  
   309  	// device Z is the encryptor's device
   310  	tcZ := SetupEngineTest(t, "encryptor")
   311  	defer tcZ.Cleanup()
   312  
   313  	// provisioner needs to be logged in
   314  	userX := CreateAndSignupFakeUserPaper(tcX, "naclp")
   315  
   316  	encryptor := CreateAndSignupFakeUser(tcZ, "naclp")
   317  	spui := testDecryptSaltpackUI{}
   318  
   319  	// encrypt a message with encryption / tcZ
   320  	msg := "10 days in Japan"
   321  	sink := libkb.NewBufferCloser()
   322  	uis := libkb.UIs{
   323  		IdentifyUI: &FakeIdentifyUI{},
   324  		SecretUI:   encryptor.NewSecretUI(),
   325  		LogUI:      tcZ.G.UI.GetLogUI(),
   326  		SaltpackUI: &spui,
   327  	}
   328  
   329  	arg := &SaltpackEncryptArg{
   330  		Source: strings.NewReader(msg),
   331  		Sink:   sink,
   332  		Opts: keybase1.SaltpackEncryptOptions{
   333  			Recipients: []string{
   334  				userX.Username,
   335  			},
   336  			UseDeviceKeys:    true,
   337  			UsePaperKeys:     true,
   338  			AuthenticityType: keybase1.AuthenticityType_REPUDIABLE,
   339  		},
   340  	}
   341  	enc := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface)
   342  	m := NewMetaContextForTest(tcZ).WithUIs(uis)
   343  	// The error messages in this test only work for visible recipients, which
   344  	// aren't the default anymore.
   345  	enc.visibleRecipientsForTesting = true
   346  
   347  	if err := RunEngine2(m, enc); err != nil {
   348  		t.Fatal(err)
   349  	}
   350  	out := sink.String()
   351  
   352  	// decrypt it with userX / tcX
   353  	decoded := libkb.NewBufferCloser()
   354  	decarg := &SaltpackDecryptArg{
   355  		Source: strings.NewReader(out),
   356  		Sink:   decoded,
   357  	}
   358  	dec := NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t))
   359  	spui.f = func(arg keybase1.SaltpackPromptForDecryptArg) error {
   360  		if arg.Sender.SenderType != keybase1.SaltpackSenderType_NOT_TRACKED {
   361  			t.Fatalf("Bad sender type: %v", arg.Sender.SenderType)
   362  		}
   363  		return nil
   364  	}
   365  	uis = libkb.UIs{
   366  		IdentifyUI: &FakeIdentifyUI{},
   367  		SecretUI:   userX.NewSecretUI(),
   368  		LogUI:      tcX.G.UI.GetLogUI(),
   369  		SaltpackUI: &spui,
   370  	}
   371  	m = NewMetaContextForTest(tcX).WithUIs(uis)
   372  	if err := RunEngine2(m, dec); err != nil {
   373  		t.Fatal(err)
   374  	}
   375  	decmsg := decoded.String()
   376  	// Should work!
   377  	if decmsg != msg {
   378  		t.Fatalf("decoded: %s, expected: %s", decmsg, msg)
   379  	}
   380  
   381  	// Now make a new device for userX
   382  	tcY, Cleanup := provisionNewDeviceKex(&tcX, userX)
   383  	defer Cleanup()
   384  	if err := AssertProvisioned(*tcY); err != nil {
   385  		t.Fatal(err)
   386  	}
   387  
   388  	// Now try and fail to decrypt with device Y (via tcY)
   389  	decoded = libkb.NewBufferCloser()
   390  	decarg = &SaltpackDecryptArg{
   391  		Source: strings.NewReader(out),
   392  		Sink:   decoded,
   393  	}
   394  	dec = NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t))
   395  	spui.f = func(arg keybase1.SaltpackPromptForDecryptArg) error {
   396  		t.Fatal("should not be prompted for decryption")
   397  		return nil
   398  	}
   399  	uis = libkb.UIs{
   400  		IdentifyUI: &FakeIdentifyUI{},
   401  		SecretUI:   userX.NewSecretUI(),
   402  		LogUI:      tcY.G.UI.GetLogUI(),
   403  		SaltpackUI: &spui,
   404  	}
   405  	m = NewMetaContextForTest(*tcY).WithUIs(uis)
   406  	if err := RunEngine2(m, dec); err == nil {
   407  		t.Fatal("Should have seen a decryption error")
   408  	}
   409  
   410  	// Make sure we get the right helpful debug message back
   411  	me := dec.MessageInfo()
   412  	if len(me.Devices) != 2 {
   413  		t.Fatalf("expected 2 valid devices; got %d", len(me.Devices))
   414  	}
   415  
   416  	backup := 0
   417  	desktops := 0
   418  	for _, d := range me.Devices {
   419  		switch d.Type {
   420  		case keybase1.DeviceTypeV2_PAPER:
   421  			backup++
   422  		case keybase1.DeviceTypeV2_DESKTOP:
   423  			desktops++
   424  			if !userX.EncryptionKey.GetKID().Equal(d.EncryptKey) {
   425  				t.Fatal("got wrong encryption key for good possibilities")
   426  			}
   427  		default:
   428  			t.Fatalf("wrong kind of device: %s\n", d.Type)
   429  		}
   430  	}
   431  	if backup != 1 {
   432  		t.Fatal("Only wanted 1 backup key")
   433  	}
   434  	if desktops != 1 {
   435  		t.Fatal("only wanted 1 desktop key")
   436  	}
   437  }
   438  
   439  func TestSaltpackDecryptWithPaperKey(t *testing.T) {
   440  	tc := SetupEngineTest(t, "SaltpackDecrypt")
   441  	defer tc.Cleanup()
   442  
   443  	// We don't log in as a test user here. This flow should work even if
   444  	// you're totally logged out.
   445  
   446  	msg := `BEGIN KEYBASE SALTPACK ENCRYPTED MESSAGE. kiPgBwdlv6bV9N8
   447  	dSkCbjKrku4NADt gV8qC1k8zRA0Bi0 6KvGQoMyf99b2id uGZ3EDTqb5nZVPT
   448  	vhMiB49BOHavdzN mySkmzwlSWDsuQA z9RIPfrIX9IJCfi yqlaD1HOqK1lilP
   449  	tDrign5LrAB8zLz 4NwPFBwpQJWW8sO N9Jk6yzf6QvdPav GN9SqL6YX7XEbJc
   450  	PLrDD7LCj7fHObD O3pTQSLjuUKqAqa 3LDiQEVEDUZzYLy TvKyMJ2U8gCuhcU
   451  	SeqDClUNAPKqEEM MRgyTcw0LSwK4A5 YyZhDM065PA5SHb 6ZFPGYnv81HOibR
   452  	FHHv5lYEqPqPAZa ETIXLblnxI61F2q 6cH3w60bbxFuQB2 fwLZQSUS4ZzyVSw
   453  	UN3OKZMr79vqr6S ap3vMMqGiWm3blG ptjZEmFXI5dQqZG w9AO0Djmy2fWnCB
   454  	Z42e7BZteGaRhz8 zVmNLdOvtWiJkRF FUo2KvUgBsk9ecJ 3iZUOhYbdqja2Xx
   455  	osdOqu6OUS9V4XC H9vRylZJShvVg2X NLaeeHZ6AHdxkxO NgrG1NqHeIubq8p
   456  	0VaDq1iKk78Qj27 4q26yqnt5E9sgnN xJ850oP5DeKWrN3 yaif8ouprlETzY3
   457  	CLmDsAN5vVCgVga gx1q3YEjKUmJqD2 EsY5KBKogE1YjvQ eVaoqX5qiKtS6o0
   458  	oGE70tbveveK0kV SErmRsOSFBieaCq JzW75TXRCHpLvVB 1ZB8Wih6cyvw1yx
   459  	pK5RJNfPOF6lzKm i28FT9EoCw7uvsB kBG2EfA9YRkhXKh RoqAGrkdX3ziGy8
   460  	j5eOK91eyIcl7f7 SfUFLzETW5ULZfm 7Z9BIeOJogk7a1B 7IUJQiYpLyG3xAF
   461  	p3nmeSIalwfIzhV opNxUB7ltUOn3PX t9abJAZkUodMURG zXw0dKHQKtWXce6
   462  	y8jHbaU0zLwxvhO W3bxHNGoQ10t7Gq hSPu7SYLYyD926w 8nv5FqiUtTf7eJq
   463  	Ay1c2FAYMPkB4ay 6lB0wxtpNCGt8MO RtrC1Da3aj7rLTL fFNz2kxb78hT2Tu
   464  	QNiHyBL. END KEYBASE SALTPACK ENCRYPTED MESSAGE.`
   465  
   466  	paperkey := "fitness weasel session truly among connect explain found measure smile ask ball shoulder"
   467  
   468  	// decrypt it
   469  	decoded := libkb.NewBufferCloser()
   470  	decarg := &SaltpackDecryptArg{
   471  		Source: strings.NewReader(msg),
   472  		Sink:   decoded,
   473  		Opts: keybase1.SaltpackDecryptOptions{
   474  			UsePaperKey: true,
   475  		},
   476  	}
   477  	dec := NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t))
   478  	uis := libkb.UIs{
   479  		IdentifyUI: &FakeIdentifyUI{},
   480  		// Here's where the paper key goes in!
   481  		SecretUI:   &libkb.TestSecretUI{Passphrase: paperkey},
   482  		LogUI:      tc.G.UI.GetLogUI(),
   483  		SaltpackUI: &fakeSaltpackUI{},
   484  	}
   485  	m := NewMetaContextForTest(tc).WithUIs(uis)
   486  	if err := RunEngine2(m, dec); err != nil {
   487  		t.Fatal(err)
   488  	}
   489  	decmsg := decoded.String()
   490  	expected := "message for paper key"
   491  	if decmsg != expected {
   492  		t.Errorf("decoded: %s, expected: %s", decmsg, expected)
   493  	}
   494  }
   495  
   496  func TestSaltpackDecryptErrors(t *testing.T) {
   497  	tc := SetupEngineTest(t, "SaltpackDecrypt")
   498  	defer tc.Cleanup()
   499  	fu := CreateAndSignupFakeUser(tc, "naclp")
   500  
   501  	initPerUserKeyringInTestContext(t, tc)
   502  
   503  	// encrypt a message
   504  	msg := "10 days in Japan"
   505  	sink := libkb.NewBufferCloser()
   506  	uis := libkb.UIs{
   507  		IdentifyUI: &FakeIdentifyUI{},
   508  		SecretUI:   fu.NewSecretUI(),
   509  		LogUI:      tc.G.UI.GetLogUI(),
   510  		SaltpackUI: &fakeSaltpackUI{},
   511  	}
   512  	// Should encrypt for self, too.
   513  	arg := &SaltpackEncryptArg{
   514  		Opts: keybase1.SaltpackEncryptOptions{
   515  			UseEntityKeys: true,
   516  		},
   517  		Source: strings.NewReader(msg),
   518  		Sink:   sink,
   519  	}
   520  	enc := NewSaltpackEncrypt(arg, NewSaltpackUserKeyfinderAsInterface)
   521  	m := NewMetaContextForTest(tc).WithUIs(uis)
   522  	if err := RunEngine2(m, enc); err != nil {
   523  		t.Fatal(err)
   524  	}
   525  	out := sink.String()
   526  
   527  	t.Logf("encrypted data: %s", out)
   528  
   529  	// assert that decryption works
   530  	decoded := libkb.NewBufferCloser()
   531  	decarg := &SaltpackDecryptArg{
   532  		Source: strings.NewReader(out),
   533  		Sink:   decoded,
   534  	}
   535  	dec := NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t))
   536  	if err := RunEngine2(m, dec); err != nil {
   537  		t.Fatal(err)
   538  	}
   539  	decmsg := decoded.String()
   540  	if decmsg != msg {
   541  		t.Errorf("decoded: %s, expected: %s", decmsg, msg)
   542  	}
   543  
   544  	// no suitable key
   545  
   546  	noKeyOut := `
   547  		BEGIN KEYBASE SALTPACK ENCRYPTED MESSAGE. keDIDMQWYvVR58B FTfTeDQNI4kF4aV
   548  		DVTLefltGS2t8xf vO9CL7B6UQvpJEM WofNn31JYOAziF6 5vLTstSAXwLrJQ0
   549  		K6KJtSb38V1EsJJ sJMdU57C9s3dYEy Fk9Rw1bK2dTnua5 b822rmZdhdw7VzS
   550  		RwR1shIDHPXgFGC 5HhonSburbeywdE lSriPuVhSAXX9yR kwgCa3jRY3y863C
   551  		pwxv8lB10IjmgSx Xb5D61t8WCZ86N0 pADtHnYeyXwezjW fSEE57xOLE8naAz
   552  		RJLPlTEhoqllLQh iK1yzp6bGeJyAlI ctWQKKORiIyIsNS 1oFT8dj7SUtWhxy
   553  		nAGjUIDfkIBzBB3 SbPgWxhBKnXbW0R LCMxKOGhgpolubZ sdlVLZmP0F. END KEYBASE
   554  		SALTPACK ENCRYPTED MESSAGE.
   555  	`
   556  
   557  	decoded = libkb.NewBufferCloser()
   558  	decarg = &SaltpackDecryptArg{
   559  		Source: strings.NewReader(noKeyOut),
   560  		Sink:   decoded,
   561  	}
   562  	dec = NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t))
   563  	err := RunEngine2(m, dec)
   564  	require.Error(t, err)
   565  	require.IsType(t, libkb.DecryptionError{}, err)
   566  	if err, ok := err.(libkb.DecryptionError); ok {
   567  		require.IsType(t, libkb.NoDecryptionKeyError{}, err.Cause.Err)
   568  		require.Equal(t, libkb.SCDecryptionKeyNotFound, err.Cause.StatusCode)
   569  	}
   570  
   571  	// wrong type
   572  
   573  	wrongTypeOut := `BEGIN KEYBASE SALTPACK SIGNED MESSAGE. kXR7VktZdyH7rvq v5weRa0zkUpZbPl nShyKLCCGSyBcvL sg7Gi9ySjlkxHPS RUM4Vm3DUCrEkxO bkbzrs0zuASSJbt aRoYF7ojm5zUxFX QasNzKboA7khwAa qIlCC0iwTFB3NBq fmDjvyKYfyQACcV 4BG5Mij3RfCYqAw gLtvUzU0UmfhJoP JPbj2eztW. END KEYBASE SALTPACK SIGNED MESSAGE.`
   574  
   575  	decoded = libkb.NewBufferCloser()
   576  	decarg = &SaltpackDecryptArg{
   577  		Source: strings.NewReader(wrongTypeOut),
   578  		Sink:   decoded,
   579  	}
   580  	dec = NewSaltpackDecrypt(decarg, saltpackkeystest.NewMockPseudonymResolver(t))
   581  	err = RunEngine2(m, dec)
   582  	require.Error(t, err)
   583  	require.IsType(t, libkb.DecryptionError{}, err)
   584  	if err, ok := err.(libkb.DecryptionError); ok {
   585  		require.IsType(t, saltpack.ErrWrongMessageType{}, err.Cause.Err)
   586  		require.Equal(t, libkb.SCWrongCryptoMsgType, err.Cause.StatusCode)
   587  	}
   588  }