github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/engine/pgp_export_key_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  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/keybase/client/go/libkb"
    12  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    13  )
    14  
    15  func TestPGPExportOptions(t *testing.T) {
    16  	tc := SetupEngineTest(t, "pgpsave")
    17  	defer tc.Cleanup()
    18  
    19  	u := CreateAndSignupFakeUser(tc, "login")
    20  	secui := &libkb.TestSecretUI{Passphrase: u.Passphrase}
    21  	uis := libkb.UIs{LogUI: tc.G.UI.GetLogUI(), SecretUI: secui}
    22  
    23  	fp, kid, key := genPGPKeyAndArmor(t, tc, u.Email)
    24  	eng, err := NewPGPKeyImportEngineFromBytes(tc.G, []byte(key), true)
    25  	if err != nil {
    26  		t.Fatal(err)
    27  	}
    28  	m := NewMetaContextForTest(tc).WithUIs(uis)
    29  	if err = RunEngine2(m, eng); err != nil {
    30  		t.Fatal(err)
    31  	}
    32  
    33  	table := []exportTest{
    34  		{true, fp.String(), false, 1, 1, 0},
    35  		{true, fp.String(), true, 1, 1, 0},
    36  		{false, fp.String(), false, 1, 1, 0},
    37  		{false, fp.String(), true, 1, 1, 0},
    38  
    39  		// fingerprint substring must be suffix:
    40  		{true, fp.String()[len(fp.String())-5:], false, 1, 1, 0},
    41  		{true, fp.String()[len(fp.String())-5:], true, 0, 0, 0},
    42  		{false, fp.String()[len(fp.String())-5:], false, 1, 1, 0},
    43  		{false, fp.String()[len(fp.String())-5:], true, 0, 0, 0},
    44  		{true, fp.String()[0:5], false, 0, 0, 0},
    45  		{true, fp.String()[0:5], true, 0, 0, 0},
    46  		{false, fp.String()[0:5], false, 0, 0, 0},
    47  		{false, fp.String()[0:5], true, 0, 0, 0},
    48  
    49  		{true, kid.String(), false, 1, 0, 1},
    50  		{true, kid.String(), true, 1, 0, 1},
    51  		{false, kid.String(), false, 1, 0, 1},
    52  		{false, kid.String(), true, 1, 0, 1},
    53  
    54  		// kid substring must be prefix:
    55  		{true, kid.String()[len(fp.String())-5:], false, 0, 0, 0},
    56  		{true, kid.String()[len(fp.String())-5:], true, 0, 0, 0},
    57  		{false, kid.String()[len(fp.String())-5:], false, 0, 0, 0},
    58  		{false, kid.String()[len(fp.String())-5:], true, 0, 0, 0},
    59  		{true, kid.String()[0:5], false, 1, 0, 1},
    60  		{true, kid.String()[0:5], true, 0, 0, 0},
    61  		{false, kid.String()[0:5], false, 1, 0, 1},
    62  		{false, kid.String()[0:5], true, 0, 0, 0},
    63  	}
    64  
    65  	for i, test := range table {
    66  		ec, err := pgpExport(m, test.secret, test.query, test.exact)
    67  		if err != nil {
    68  			t.Errorf("test %d error: %s", i, err)
    69  		}
    70  		if ec.either != test.either {
    71  			t.Errorf("test %d: (either) num keys exported: %d, expected %d", i, ec.either, test.either)
    72  		}
    73  		if ec.fingerprint != test.fingerprint {
    74  			t.Errorf("test %d: (fp) num keys exported: %d, expected %d", i, ec.fingerprint, test.fingerprint)
    75  		}
    76  		if ec.kid != test.kid {
    77  			t.Errorf("test %d: (kid) num keys exported: %d, expected %d", i, ec.kid, test.kid)
    78  		}
    79  	}
    80  }
    81  
    82  type exportTest struct {
    83  	secret      bool
    84  	query       string
    85  	exact       bool
    86  	either      int
    87  	fingerprint int
    88  	kid         int
    89  }
    90  
    91  type exportCounts struct {
    92  	either      int
    93  	fingerprint int
    94  	kid         int
    95  }
    96  
    97  func pgpExport(m libkb.MetaContext, secret bool, query string, exact bool) (exportCounts, error) {
    98  	opts := keybase1.PGPQuery{
    99  		Secret:     secret,
   100  		Query:      query,
   101  		ExactMatch: exact,
   102  	}
   103  
   104  	var xcount exportCounts
   105  
   106  	arg := keybase1.PGPExportArg{
   107  		Options: opts,
   108  	}
   109  	g := m.G()
   110  	xe := NewPGPKeyExportEngine(g, arg)
   111  	if err := RunEngine2(m, xe); err != nil {
   112  		return xcount, err
   113  	}
   114  
   115  	xcount.either = len(xe.Results())
   116  
   117  	farg := keybase1.PGPExportByFingerprintArg{
   118  		Options: opts,
   119  	}
   120  	xf := NewPGPKeyExportByFingerprintEngine(g, farg)
   121  	if err := RunEngine2(m, xf); err != nil {
   122  		return xcount, err
   123  	}
   124  
   125  	xcount.fingerprint = len(xf.Results())
   126  
   127  	karg := keybase1.PGPExportByKIDArg{
   128  		Options: opts,
   129  	}
   130  	xk := NewPGPKeyExportByKIDEngine(g, karg)
   131  	if err := RunEngine2(m, xk); err != nil {
   132  		return xcount, err
   133  	}
   134  
   135  	xcount.kid = len(xk.Results())
   136  
   137  	return xcount, nil
   138  }
   139  
   140  type PGPTestSecretUI struct {
   141  	libkb.TestSecretUI
   142  	Prompts []string
   143  }
   144  
   145  func (t *PGPTestSecretUI) GetPassphrase(p keybase1.GUIEntryArg, terminal *keybase1.SecretEntryArg) (keybase1.GetPassphraseRes, error) {
   146  	t.CalledGetPassphrase = true
   147  	t.Prompts = append(t.Prompts, p.Prompt)
   148  	return keybase1.GetPassphraseRes{
   149  		Passphrase:  t.Passphrase,
   150  		StoreSecret: t.StoreSecret,
   151  	}, nil
   152  }
   153  
   154  func TestPGPExportEncryption(t *testing.T) {
   155  	tc := SetupEngineTest(t, "pgpsave")
   156  	defer tc.Cleanup()
   157  
   158  	u := CreateAndSignupFakeUser(tc, "login")
   159  
   160  	pgpPassphrase := "hello_pgp" + u.Passphrase
   161  	secui := &PGPTestSecretUI{}
   162  	secui.Passphrase = pgpPassphrase
   163  	uis := libkb.UIs{LogUI: tc.G.UI.GetLogUI(), SecretUI: secui}
   164  
   165  	fp, _, key := genPGPKeyAndArmor(t, tc, u.Email)
   166  	eng, err := NewPGPKeyImportEngineFromBytes(tc.G, []byte(key), true)
   167  	require.NoError(t, err)
   168  
   169  	m := NewMetaContextForTest(tc).WithUIs(uis)
   170  	err = RunEngine2(m, eng)
   171  	require.NoError(t, err)
   172  
   173  	opts := keybase1.PGPQuery{
   174  		Secret:     true,
   175  		Query:      fp.String(),
   176  		ExactMatch: true,
   177  	}
   178  
   179  	// Run export with Encrypted: true
   180  
   181  	arg := keybase1.PGPExportArg{
   182  		Options:   opts,
   183  		Encrypted: true,
   184  	}
   185  	xe := NewPGPKeyExportEngine(tc.G, arg)
   186  	if err := RunEngine2(m, xe); err != nil {
   187  		t.Fatal(err)
   188  	}
   189  
   190  	require.Len(t, secui.Prompts, 2, "Expected two prompts in SecretUI (PGP passphrase and confirmation)")
   191  	secui.Prompts = []string{}
   192  
   193  	entity, _, err := libkb.ReadOneKeyFromString(xe.Results()[0].Key)
   194  	require.NoError(t, err)
   195  
   196  	require.NotNil(t, entity.PrivateKey, "Key isn't private key")
   197  	require.True(t, entity.PrivateKey.Encrypted, "Key is not encrypted")
   198  
   199  	for i, subkey := range entity.Subkeys {
   200  		require.True(t, subkey.PrivateKey.Encrypted, "Subkey %d is not encrypted", i)
   201  	}
   202  
   203  	if err := entity.PrivateKey.Decrypt([]byte(pgpPassphrase)); err != nil {
   204  		t.Fatal("Decryption with passphrase failed")
   205  	}
   206  
   207  	// Run export with Encrypted: false
   208  
   209  	arg = keybase1.PGPExportArg{
   210  		Options:   opts,
   211  		Encrypted: false,
   212  	}
   213  	xe = NewPGPKeyExportEngine(tc.G, arg)
   214  	err = RunEngine2(m, xe)
   215  	require.NoError(t, err)
   216  
   217  	require.Len(t, secui.Prompts, 0, "Expected no prompts in SecretUI")
   218  
   219  	entity, _, err = libkb.ReadOneKeyFromString(xe.Results()[0].Key)
   220  	require.NoError(t, err)
   221  
   222  	require.NotNil(t, entity.PrivateKey, "Key isn't private key")
   223  	require.False(t, entity.PrivateKey.Encrypted, "Key is encrypted")
   224  
   225  	for i, subkey := range entity.Subkeys {
   226  		require.False(t, subkey.PrivateKey.Encrypted, "Subkey %d is encrypted", i)
   227  	}
   228  }
   229  
   230  func TestPGPExportMultipleSyncedKeys(t *testing.T) {
   231  	tc := SetupEngineTest(t, "pgpexport")
   232  	defer tc.Cleanup()
   233  
   234  	u := CreateAndSignupFakeUser(tc, "pgp")
   235  
   236  	secui := &PGPTestSecretUI{}
   237  	uis := libkb.UIs{LogUI: tc.G.UI.GetLogUI(), SecretUI: secui}
   238  
   239  	// Generate two keys and import with pushPrivate.
   240  	fps := make([]libkb.PGPFingerprint, 2)
   241  	for i := range fps {
   242  		fp, _, key := genPGPKeyAndArmor(t, tc, u.Email)
   243  		eng, err := NewPGPKeyImportEngineFromBytes(tc.G, []byte(key), true /* pushPrivate */)
   244  		require.NoError(t, err)
   245  
   246  		m := NewMetaContextForTest(tc).WithUIs(uis)
   247  		err = RunEngine2(m, eng)
   248  		require.NoError(t, err)
   249  
   250  		fps[i] = fp
   251  	}
   252  
   253  	// Purge PGP keys from local keychain so we are forced to fetch server
   254  	// synced keys.
   255  	{
   256  		eng := NewPGPPurge(tc.G, keybase1.PGPPurgeArg{
   257  			DoPurge: true,
   258  		})
   259  		m := NewMetaContextForTest(tc).WithUIs(uis)
   260  		err := RunEngine2(m, eng)
   261  		require.NoError(t, err)
   262  	}
   263  
   264  	t.Logf("Trying to export keys now")
   265  
   266  	// Try to export each key.
   267  	for _, fp := range fps {
   268  		arg := keybase1.PGPExportArg{
   269  			Options: keybase1.PGPQuery{
   270  				Secret:     true,
   271  				Query:      fp.String(),
   272  				ExactMatch: true,
   273  			},
   274  			Encrypted: false,
   275  		}
   276  		eng := NewPGPKeyExportEngine(tc.G, arg)
   277  		m := NewMetaContextForTest(tc).WithUIs(uis)
   278  		err := RunEngine2(m, eng)
   279  		require.NoError(t, err)
   280  
   281  		require.Len(t, eng.Results(), 1)
   282  
   283  		entity, _, err := libkb.ReadOneKeyFromString(eng.Results()[0].Key)
   284  		require.NoError(t, err)
   285  
   286  		require.NotNil(t, entity.PrivateKey, "Key isn't private key")
   287  		require.Equal(t, fp, entity.GetFingerprint())
   288  	}
   289  }