cmd_onion_test.go 5.9 KB

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