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  }