1// +build darwin
2
3package libkb
4
5import (
6	"encoding/base64"
7	"testing"
8
9	keychain "github.com/keybase/go-keychain"
10	"github.com/stretchr/testify/require"
11)
12
13func TestSecretStoreDarwin(t *testing.T) {
14	tc := SetupTest(t, "secret store darwin", 1)
15	defer tc.Cleanup()
16
17	mctx := NewMetaContextForTest(tc)
18	secretStore := KeychainSecretStore{}
19	nu := NormalizedUsername("username")
20
21	defer func() {
22		err := secretStore.ClearSecret(mctx, nu)
23		require.NoError(tc.T, err)
24	}()
25
26	serviceName := secretStore.serviceName(mctx)
27	accessGroup := secretStore.accessGroup(mctx)
28
29	expectedSecret1 := []byte("test secret 1test secret 1test s")
30	expectedSecret2 := []byte("test secret 2test secret 2test s")
31	encodedSecret1 := base64.StdEncoding.EncodeToString(expectedSecret1)
32	encodedSecret2 := base64.StdEncoding.EncodeToString(expectedSecret2)
33	lkSec1, err := newLKSecFullSecretFromBytes(expectedSecret1)
34	require.NoError(t, err)
35
36	err = secretStore.StoreSecret(mctx, nu, lkSec1)
37	require.NoError(t, err)
38
39	secret, err := secretStore.RetrieveSecret(mctx, nu)
40	require.NoError(t, err)
41	require.Equal(t, string(expectedSecret1), string(secret.Bytes()))
42
43	t.Logf("Corrupt keychain, add new secret")
44	// corrupt the secret in the keychain by writing into a new slot
45	// forcing us to use a new keychain slot when writing the new item
46	account := newKeychainSlottedAccount(nu, 1)
47	item := keychain.NewGenericPassword(serviceName, account.String(),
48		"", []byte(encodedSecret2), accessGroup)
49	err = keychain.AddItem(item)
50	require.NoError(t, err)
51
52	// We now readout expectedSecret2 since it is the latest entry.
53	secret, err = secretStore.RetrieveSecret(mctx, nu)
54	require.NoError(t, err)
55	require.Equal(t, string(expectedSecret2), string(secret.Bytes()))
56
57	// Now write expectedSecret1 back into the store, which will overwrite secret2
58	err = secretStore.StoreSecret(mctx, nu, lkSec1)
59	require.NoError(t, err)
60
61	secret, err = secretStore.RetrieveSecret(mctx, nu)
62	require.NoError(t, err)
63	require.Equal(t, string(expectedSecret1), string(secret.Bytes()))
64
65	// verify our keychain state
66	for i := 0; i < 2; i++ {
67		account := newKeychainSlottedAccount(nu, i)
68		query := keychain.NewItem()
69		query.SetSecClass(keychain.SecClassGenericPassword)
70		query.SetService(serviceName)
71		query.SetAccount(account.String())
72		query.SetAccessGroup(accessGroup)
73		query.SetReturnData(true)
74		query.SetReturnAttributes(true)
75		results, err := keychain.QueryItem(query)
76		require.NoError(t, err)
77		require.Len(t, results, 1)
78		res := results[0]
79
80		require.Equal(t, secretStore.serviceName(mctx), res.Service)
81		require.Equal(t, account.String(), res.Account)
82		require.Equal(t, secretStore.accessGroup(mctx), res.AccessGroup)
83		require.Equal(t, "", res.Description)
84		require.Equal(t, encodedSecret1, string(res.Data))
85	}
86
87	// Although we have 3 keychain items, we technically only have one user in
88	// the store.
89	users, err := secretStore.GetUsersWithStoredSecrets(mctx)
90	require.NoError(t, err)
91	require.Len(t, users, 1)
92
93	err = secretStore.ClearSecret(mctx, nu)
94	require.NoError(t, err)
95
96	for i := 0; i < 2; i++ {
97		account := newKeychainSlottedAccount(nu, i)
98		query := keychain.NewItem()
99		query.SetSecClass(keychain.SecClassGenericPassword)
100		query.SetService(serviceName)
101		query.SetAccount(account.String())
102		query.SetAccessGroup(accessGroup)
103		query.SetReturnData(true)
104		query.SetReturnAttributes(true)
105		results, err := keychain.QueryItem(query)
106		require.NoError(t, err)
107		require.Nil(t, results)
108	}
109
110	users, err = secretStore.GetUsersWithStoredSecrets(mctx)
111	require.NoError(t, err)
112	require.Len(t, users, 0)
113}
114
115func TestPrimeSecretStoreDarwin(t *testing.T) {
116	tc := SetupTest(t, "secret_store_darwin", 1)
117	defer tc.Cleanup()
118	tc.G.Env.Test.SecretStorePrimingDisabled = false
119
120	mctx := NewMetaContextForTest(tc)
121	secretStore := KeychainSecretStore{}
122	err := PrimeSecretStore(mctx, secretStore)
123	require.NoError(t, err)
124}
125