github.com/decred/dcrlnd@v0.7.6/tor/cmd_onion_test.go (about) 1 package tor 2 3 import ( 4 "bytes" 5 "errors" 6 "io/ioutil" 7 "path/filepath" 8 "testing" 9 10 "github.com/stretchr/testify/mock" 11 "github.com/stretchr/testify/require" 12 ) 13 14 // TestOnionFile tests that the OnionFile implementation of the OnionStore 15 // interface behaves as expected. 16 func TestOnionFile(t *testing.T) { 17 t.Parallel() 18 19 tempDir, err := ioutil.TempDir("", "onion_store") 20 if err != nil { 21 t.Fatalf("unable to create temp dir: %v", err) 22 } 23 24 privateKey := []byte("hide_me_plz") 25 privateKeyPath := filepath.Join(tempDir, "secret") 26 27 // Create a new file-based onion store. A private key should not exist 28 // yet. 29 onionFile := NewOnionFile(privateKeyPath, 0600) 30 if _, err := onionFile.PrivateKey(V2); err != ErrNoPrivateKey { 31 t.Fatalf("expected ErrNoPrivateKey, got \"%v\"", err) 32 } 33 34 // Store the private key and ensure what's stored matches. 35 if err := onionFile.StorePrivateKey(V2, privateKey); err != nil { 36 t.Fatalf("unable to store private key: %v", err) 37 } 38 storePrivateKey, err := onionFile.PrivateKey(V2) 39 if err != nil { 40 t.Fatalf("unable to retrieve private key: %v", err) 41 } 42 if !bytes.Equal(storePrivateKey, privateKey) { 43 t.Fatalf("expected private key \"%v\", got \"%v\"", 44 string(privateKey), string(storePrivateKey)) 45 } 46 47 // Finally, delete the private key. We should no longer be able to 48 // retrieve it. 49 if err := onionFile.DeletePrivateKey(V2); err != nil { 50 t.Fatalf("unable to delete private key: %v", err) 51 } 52 if _, err := onionFile.PrivateKey(V2); err != ErrNoPrivateKey { 53 t.Fatal("found deleted private key") 54 } 55 } 56 57 // TestPrepareKeyParam checks that the key param is created as expected. 58 func TestPrepareKeyParam(t *testing.T) { 59 testKey := []byte("hide_me_plz") 60 dummyErr := errors.New("dummy") 61 62 // Create a dummy controller. 63 controller := NewController("", "", "") 64 65 // Test that a V3 keyParam is used. 66 cfg := AddOnionConfig{Type: V3} 67 keyParam, err := controller.prepareKeyparam(cfg) 68 69 require.Equal(t, "NEW:ED25519-V3", keyParam) 70 require.NoError(t, err) 71 72 // Create a mock store which returns the test private key. 73 store := &mockStore{} 74 store.On("PrivateKey", cfg.Type).Return(testKey, nil) 75 76 // Check that the test private is returned. 77 cfg = AddOnionConfig{Type: V3, Store: store} 78 keyParam, err = controller.prepareKeyparam(cfg) 79 80 require.Equal(t, string(testKey), keyParam) 81 require.NoError(t, err) 82 store.AssertExpectations(t) 83 84 // Create a mock store which returns ErrNoPrivateKey. 85 store = &mockStore{} 86 store.On("PrivateKey", cfg.Type).Return(nil, ErrNoPrivateKey) 87 88 // Check that the V3 keyParam is returned. 89 cfg = AddOnionConfig{Type: V3, Store: store} 90 keyParam, err = controller.prepareKeyparam(cfg) 91 92 require.Equal(t, "NEW:ED25519-V3", keyParam) 93 require.NoError(t, err) 94 store.AssertExpectations(t) 95 96 // Create a mock store which returns an dummy error. 97 store = &mockStore{} 98 store.On("PrivateKey", cfg.Type).Return(nil, dummyErr) 99 100 // Check that an error is returned. 101 cfg = AddOnionConfig{Type: V3, Store: store} 102 keyParam, err = controller.prepareKeyparam(cfg) 103 104 require.Empty(t, keyParam) 105 require.ErrorIs(t, dummyErr, err) 106 store.AssertExpectations(t) 107 } 108 109 // TestPrepareAddOnion checks that the cmd used to add onion service is created 110 // as expected. 111 func TestPrepareAddOnion(t *testing.T) { 112 t.Parallel() 113 114 // Create a mock store. 115 store := &mockStore{} 116 testKey := []byte("hide_me_plz") 117 118 testCases := []struct { 119 name string 120 targetIPAddress string 121 cfg AddOnionConfig 122 expectedCmd string 123 expectedErr error 124 }{ 125 { 126 name: "empty target IP and ports", 127 targetIPAddress: "", 128 cfg: AddOnionConfig{VirtualPort: 9735}, 129 expectedCmd: "ADD_ONION NEW:RSA1024 Port=9735,9735 ", 130 expectedErr: nil, 131 }, 132 { 133 name: "specified target IP and empty ports", 134 targetIPAddress: "127.0.0.1", 135 cfg: AddOnionConfig{VirtualPort: 9735}, 136 expectedCmd: "ADD_ONION NEW:RSA1024 " + 137 "Port=9735,127.0.0.1:9735 ", 138 expectedErr: nil, 139 }, 140 { 141 name: "specified target IP and ports", 142 targetIPAddress: "127.0.0.1", 143 cfg: AddOnionConfig{ 144 VirtualPort: 9735, 145 TargetPorts: []int{18000, 18001}, 146 }, 147 expectedCmd: "ADD_ONION NEW:RSA1024 " + 148 "Port=9735,127.0.0.1:18000 " + 149 "Port=9735,127.0.0.1:18001 ", 150 expectedErr: nil, 151 }, 152 { 153 name: "specified private key from store", 154 targetIPAddress: "", 155 cfg: AddOnionConfig{ 156 VirtualPort: 9735, 157 Store: store, 158 }, 159 expectedCmd: "ADD_ONION hide_me_plz " + 160 "Port=9735,9735 ", 161 expectedErr: nil, 162 }, 163 } 164 165 for _, tc := range testCases { 166 tc := tc 167 168 if tc.cfg.Store != nil { 169 store.On("PrivateKey", tc.cfg.Type).Return( 170 testKey, tc.expectedErr, 171 ) 172 } 173 174 controller := NewController("", tc.targetIPAddress, "") 175 t.Run(tc.name, func(t *testing.T) { 176 cmd, err := controller.prepareAddOnion(tc.cfg) 177 require.Equal(t, tc.expectedErr, err) 178 require.Equal(t, tc.expectedCmd, cmd) 179 180 // Check that the mocker is satisfied. 181 store.AssertExpectations(t) 182 }) 183 } 184 185 } 186 187 // mockStore implements a mock of the interface OnionStore. 188 type mockStore struct { 189 mock.Mock 190 } 191 192 // A compile-time constraint to ensure mockStore satisfies the OnionStore 193 // interface. 194 var _ OnionStore = (*mockStore)(nil) 195 196 func (m *mockStore) StorePrivateKey(ot OnionType, key []byte) error { 197 args := m.Called(ot, key) 198 return args.Error(0) 199 } 200 201 func (m *mockStore) PrivateKey(ot OnionType) ([]byte, error) { 202 args := m.Called(ot) 203 if args.Get(0) == nil { 204 return nil, args.Error(1) 205 } 206 return args.Get(0).([]byte), args.Error(1) 207 } 208 209 func (m *mockStore) DeletePrivateKey(ot OnionType) error { 210 args := m.Called(ot) 211 return args.Error(0) 212 }