123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- package tor
- import (
- "errors"
- "io"
- "path/filepath"
- "testing"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/require"
- )
- var (
- privateKey = []byte("RSA1024 hide_me_plz")
- anotherKey = []byte("another_key")
- )
- // TestOnionFile tests that the File implementation of the OnionStore
- // interface behaves as expected.
- func TestOnionFile(t *testing.T) {
- t.Parallel()
- tempDir := t.TempDir()
- privateKeyPath := filepath.Join(tempDir, "secret")
- mockEncrypter := MockEncrypter{}
- // Create a new file-based onion store. A private key should not exist
- // yet.
- onionFile := NewOnionFile(
- privateKeyPath, 0600, false, mockEncrypter,
- )
- _, err := onionFile.PrivateKey()
- require.ErrorIs(t, err, ErrNoPrivateKey)
- // Store the private key and ensure what's stored matches.
- err = onionFile.StorePrivateKey(privateKey)
- require.NoError(t, err)
- storePrivateKey, err := onionFile.PrivateKey()
- require.NoError(t, err)
- require.Equal(t, storePrivateKey, privateKey)
- // Finally, delete the private key. We should no longer be able to
- // retrieve it.
- err = onionFile.DeletePrivateKey()
- require.NoError(t, err)
- _, err = onionFile.PrivateKey()
- require.ErrorIs(t, err, ErrNoPrivateKey)
- // Create a new file-based onion store that encrypts the key this time
- // to ensure that an encrypted key is properly handled.
- encryptedOnionFile := NewOnionFile(
- privateKeyPath, 0600, true, mockEncrypter,
- )
- err = encryptedOnionFile.StorePrivateKey(privateKey)
- require.NoError(t, err)
- storedPrivateKey, err := encryptedOnionFile.PrivateKey()
- require.NoError(t, err, "unable to retrieve encrypted private key")
- // Check that PrivateKey returns anotherKey, to make sure the mock
- // decrypter is actually called.
- require.Equal(t, storedPrivateKey, anotherKey)
- err = encryptedOnionFile.DeletePrivateKey()
- require.NoError(t, err)
- }
- // TestPrepareKeyParam checks that the key param is created as expected.
- func TestPrepareKeyParam(t *testing.T) {
- testKey := []byte("hide_me_plz")
- dummyErr := errors.New("dummy")
- // Create a dummy controller.
- controller := NewController("", "", "")
- // Test that a V3 keyParam is used.
- cfg := AddOnionConfig{Type: V3}
- keyParam, err := controller.prepareKeyparam(cfg)
- require.Equal(t, "NEW:ED25519-V3", keyParam)
- require.NoError(t, err)
- // Create a mock store which returns the test private key.
- store := &mockStore{}
- store.On("PrivateKey").Return(testKey, nil)
- // Check that the test private is returned.
- cfg = AddOnionConfig{Type: V3, Store: store}
- keyParam, err = controller.prepareKeyparam(cfg)
- require.Equal(t, string(testKey), keyParam)
- require.NoError(t, err)
- store.AssertExpectations(t)
- // Create a mock store which returns ErrNoPrivateKey.
- store = &mockStore{}
- store.On("PrivateKey").Return(nil, ErrNoPrivateKey)
- // Check that the V3 keyParam is returned.
- cfg = AddOnionConfig{Type: V3, Store: store}
- keyParam, err = controller.prepareKeyparam(cfg)
- require.Equal(t, "NEW:ED25519-V3", keyParam)
- require.NoError(t, err)
- store.AssertExpectations(t)
- // Create a mock store which returns an dummy error.
- store = &mockStore{}
- store.On("PrivateKey").Return(nil, dummyErr)
- // Check that an error is returned.
- cfg = AddOnionConfig{Type: V3, Store: store}
- keyParam, err = controller.prepareKeyparam(cfg)
- require.Empty(t, keyParam)
- require.ErrorIs(t, dummyErr, err)
- store.AssertExpectations(t)
- }
- // TestPrepareAddOnion checks that the cmd used to add onion service is created
- // as expected.
- func TestPrepareAddOnion(t *testing.T) {
- t.Parallel()
- // Create a mock store.
- store := &mockStore{}
- testKey := []byte("hide_me_plz")
- testCases := []struct {
- name string
- targetIPAddress string
- cfg AddOnionConfig
- expectedCmd string
- expectedErr error
- }{
- {
- name: "empty target IP and ports",
- targetIPAddress: "",
- cfg: AddOnionConfig{VirtualPort: 9735},
- expectedCmd: "ADD_ONION NEW:RSA1024 Port=9735,9735 ",
- expectedErr: nil,
- },
- {
- name: "specified target IP and empty ports",
- targetIPAddress: "127.0.0.1",
- cfg: AddOnionConfig{VirtualPort: 9735},
- expectedCmd: "ADD_ONION NEW:RSA1024 " +
- "Port=9735,127.0.0.1:9735 ",
- expectedErr: nil,
- },
- {
- name: "specified target IP and ports",
- targetIPAddress: "127.0.0.1",
- cfg: AddOnionConfig{
- VirtualPort: 9735,
- TargetPorts: []int{18000, 18001},
- },
- expectedCmd: "ADD_ONION NEW:RSA1024 " +
- "Port=9735,127.0.0.1:18000 " +
- "Port=9735,127.0.0.1:18001 ",
- expectedErr: nil,
- },
- {
- name: "specified private key from store",
- targetIPAddress: "",
- cfg: AddOnionConfig{
- VirtualPort: 9735,
- Store: store,
- },
- expectedCmd: "ADD_ONION hide_me_plz " +
- "Port=9735,9735 ",
- expectedErr: nil,
- },
- }
- for _, tc := range testCases {
- tc := tc
- if tc.cfg.Store != nil {
- store.On("PrivateKey").Return(
- testKey, tc.expectedErr,
- )
- }
- controller := NewController("", tc.targetIPAddress, "")
- t.Run(tc.name, func(t *testing.T) {
- cmd, _, err := controller.prepareAddOnion(tc.cfg)
- require.Equal(t, tc.expectedErr, err)
- require.Equal(t, tc.expectedCmd, cmd)
- // Check that the mocker is satisfied.
- store.AssertExpectations(t)
- })
- }
- }
- // mockStore implements a mock of the interface OnionStore.
- type mockStore struct {
- mock.Mock
- }
- // A compile-time constraint to ensure mockStore satisfies the OnionStore
- // interface.
- var _ OnionStore = (*mockStore)(nil)
- func (m *mockStore) StorePrivateKey(key []byte) error {
- args := m.Called(key)
- return args.Error(0)
- }
- func (m *mockStore) PrivateKey() ([]byte, error) {
- args := m.Called()
- return []byte("hide_me_plz"), args.Error(1)
- }
- func (m *mockStore) DeletePrivateKey() error {
- args := m.Called()
- return args.Error(0)
- }
- type MockEncrypter struct{}
- func (m MockEncrypter) EncryptPayloadToWriter(_ []byte, _ io.Writer) error {
- return nil
- }
- func (m MockEncrypter) DecryptPayloadFromReader(_ io.Reader) ([]byte, error) {
- return anotherKey, nil
- }
|