mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-10-10 05:12:34 +02:00
tor: Add option to encrypt Tor private key
This commit lays the groundwork for enabling the option of encrypting a Tor private key on disk, and removes the onion type parameters from the OnionStore interface methods, since they are unused.
This commit is contained in:
143
tor/cmd_onion.go
143
tor/cmd_onion.go
@@ -1,13 +1,22 @@
|
|||||||
package tor
|
package tor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// ErrEncryptedTorPrivateKey is thrown when a tor private key is
|
||||||
|
// encrypted, but the user requested an unencrypted key.
|
||||||
|
ErrEncryptedTorPrivateKey = errors.New("it appears the Tor private key " +
|
||||||
|
"is encrypted but you didn't pass the --tor.encryptkey flag. " +
|
||||||
|
"Please restart lnd with the --tor.encryptkey flag or delete " +
|
||||||
|
"the Tor key file for regeneration")
|
||||||
|
|
||||||
// ErrNoPrivateKey is an error returned by the OnionStore.PrivateKey
|
// ErrNoPrivateKey is an error returned by the OnionStore.PrivateKey
|
||||||
// method when a private key hasn't yet been stored.
|
// method when a private key hasn't yet been stored.
|
||||||
ErrNoPrivateKey = errors.New("private key not found")
|
ErrNoPrivateKey = errors.New("private key not found")
|
||||||
@@ -24,19 +33,34 @@ const (
|
|||||||
V3
|
V3
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// V2KeyParam is a parameter that Tor accepts for a new V2 service.
|
||||||
|
V2KeyParam = "RSA1024"
|
||||||
|
|
||||||
|
// V3KeyParam is a parameter that Tor accepts for a new V3 service.
|
||||||
|
V3KeyParam = "ED25519-V3"
|
||||||
|
)
|
||||||
|
|
||||||
// OnionStore is a store containing information about a particular onion
|
// OnionStore is a store containing information about a particular onion
|
||||||
// service.
|
// service.
|
||||||
type OnionStore interface {
|
type OnionStore interface {
|
||||||
// StorePrivateKey stores the private key according to the
|
// StorePrivateKey stores the private key according to the
|
||||||
// implementation of the OnionStore interface.
|
// implementation of the OnionStore interface.
|
||||||
StorePrivateKey(OnionType, []byte) error
|
StorePrivateKey([]byte) error
|
||||||
|
|
||||||
// PrivateKey retrieves a stored private key. If it is not found, then
|
// PrivateKey retrieves a stored private key. If it is not found, then
|
||||||
// ErrNoPrivateKey should be returned.
|
// ErrNoPrivateKey should be returned.
|
||||||
PrivateKey(OnionType) ([]byte, error)
|
PrivateKey() ([]byte, error)
|
||||||
|
|
||||||
// DeletePrivateKey securely removes the private key from the store.
|
// DeletePrivateKey securely removes the private key from the store.
|
||||||
DeletePrivateKey(OnionType) error
|
DeletePrivateKey() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncrypterDecrypter is used for encrypting and decrypting the onion service
|
||||||
|
// private key.
|
||||||
|
type EncrypterDecrypter interface {
|
||||||
|
EncryptPayloadToWriter([]byte, io.Writer) error
|
||||||
|
DecryptPayloadFromReader(io.Reader) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnionFile is a file-based implementation of the OnionStore interface that
|
// OnionFile is a file-based implementation of the OnionStore interface that
|
||||||
@@ -44,6 +68,8 @@ type OnionStore interface {
|
|||||||
type OnionFile struct {
|
type OnionFile struct {
|
||||||
privateKeyPath string
|
privateKeyPath string
|
||||||
privateKeyPerm os.FileMode
|
privateKeyPerm os.FileMode
|
||||||
|
encryptKey bool
|
||||||
|
encrypter EncrypterDecrypter
|
||||||
}
|
}
|
||||||
|
|
||||||
// A compile-time constraint to ensure OnionFile satisfies the OnionStore
|
// A compile-time constraint to ensure OnionFile satisfies the OnionStore
|
||||||
@@ -52,31 +78,85 @@ var _ OnionStore = (*OnionFile)(nil)
|
|||||||
|
|
||||||
// NewOnionFile creates a file-based implementation of the OnionStore interface
|
// NewOnionFile creates a file-based implementation of the OnionStore interface
|
||||||
// to store an onion service's private key.
|
// to store an onion service's private key.
|
||||||
func NewOnionFile(privateKeyPath string,
|
func NewOnionFile(privateKeyPath string, privateKeyPerm os.FileMode,
|
||||||
privateKeyPerm os.FileMode) *OnionFile {
|
encryptKey bool, encrypter EncrypterDecrypter) *OnionFile {
|
||||||
|
|
||||||
return &OnionFile{
|
return &OnionFile{
|
||||||
privateKeyPath: privateKeyPath,
|
privateKeyPath: privateKeyPath,
|
||||||
privateKeyPerm: privateKeyPerm,
|
privateKeyPerm: privateKeyPerm,
|
||||||
|
encryptKey: encryptKey,
|
||||||
|
encrypter: encrypter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StorePrivateKey stores the private key at its expected path.
|
// StorePrivateKey stores the private key at its expected path. It also
|
||||||
func (f *OnionFile) StorePrivateKey(_ OnionType, privateKey []byte) error {
|
// encrypts the key before storing it if requested.
|
||||||
return ioutil.WriteFile(f.privateKeyPath, privateKey, f.privateKeyPerm)
|
func (f *OnionFile) StorePrivateKey(privateKey []byte) error {
|
||||||
|
privateKeyContent := privateKey
|
||||||
|
|
||||||
|
if f.encryptKey {
|
||||||
|
var b bytes.Buffer
|
||||||
|
err := f.encrypter.EncryptPayloadToWriter(
|
||||||
|
privateKey, &b,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
privateKeyContent = b.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ioutil.WriteFile(
|
||||||
|
f.privateKeyPath, privateKeyContent, f.privateKeyPerm,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to write private key "+
|
||||||
|
"to file: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivateKey retrieves the private key from its expected path. If the file
|
// PrivateKey retrieves the private key from its expected path. If the file does
|
||||||
// does not exist, then ErrNoPrivateKey is returned.
|
// not exist, then ErrNoPrivateKey is returned.
|
||||||
func (f *OnionFile) PrivateKey(_ OnionType) ([]byte, error) {
|
func (f *OnionFile) PrivateKey() ([]byte, error) {
|
||||||
if _, err := os.Stat(f.privateKeyPath); os.IsNotExist(err) {
|
_, err := os.Stat(f.privateKeyPath)
|
||||||
|
if err != nil && errors.Is(err, os.ErrNotExist) {
|
||||||
return nil, ErrNoPrivateKey
|
return nil, ErrNoPrivateKey
|
||||||
}
|
}
|
||||||
return ioutil.ReadFile(f.privateKeyPath)
|
|
||||||
|
// Try to read the Tor private key to pass into the AddOnion call.
|
||||||
|
privateKeyContent, err := ioutil.ReadFile(f.privateKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the privateKey starts with either v2 or v3 key params then
|
||||||
|
// it's likely not encrypted and we can return the data as is.
|
||||||
|
if bytes.HasPrefix(privateKeyContent, []byte(V2KeyParam)) ||
|
||||||
|
bytes.HasPrefix(privateKeyContent, []byte(V3KeyParam)) {
|
||||||
|
|
||||||
|
return privateKeyContent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the privateKeyContent is encrypted but --tor.encryptkey
|
||||||
|
// wasn't set we return an error.
|
||||||
|
if !f.encryptKey {
|
||||||
|
return nil, ErrEncryptedTorPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to decrypt the key.
|
||||||
|
reader := bytes.NewReader(privateKeyContent)
|
||||||
|
privateKeyContent, err = f.encrypter.DecryptPayloadFromReader(
|
||||||
|
reader,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return privateKeyContent, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeletePrivateKey removes the file containing the private key.
|
// DeletePrivateKey removes the file containing the private key.
|
||||||
func (f *OnionFile) DeletePrivateKey(_ OnionType) error {
|
func (f *OnionFile) DeletePrivateKey() error {
|
||||||
return os.Remove(f.privateKeyPath)
|
return os.Remove(f.privateKeyPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,13 +197,13 @@ func (c *Controller) prepareKeyparam(cfg AddOnionConfig) (string, error) {
|
|||||||
switch cfg.Type {
|
switch cfg.Type {
|
||||||
// TODO(yy): drop support for v2.
|
// TODO(yy): drop support for v2.
|
||||||
case V2:
|
case V2:
|
||||||
keyParam = "NEW:RSA1024"
|
keyParam = "NEW:" + V2KeyParam
|
||||||
case V3:
|
case V3:
|
||||||
keyParam = "NEW:ED25519-V3"
|
keyParam = "NEW:" + V3KeyParam
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.Store != nil {
|
if cfg.Store != nil {
|
||||||
privateKey, err := cfg.Store.PrivateKey(cfg.Type)
|
privateKey, err := cfg.Store.PrivateKey()
|
||||||
switch err {
|
switch err {
|
||||||
// Proceed to request a new onion service.
|
// Proceed to request a new onion service.
|
||||||
case ErrNoPrivateKey:
|
case ErrNoPrivateKey:
|
||||||
@@ -142,11 +222,13 @@ func (c *Controller) prepareKeyparam(cfg AddOnionConfig) (string, error) {
|
|||||||
|
|
||||||
// prepareAddOnion constructs a cmd command string based on the specified
|
// prepareAddOnion constructs a cmd command string based on the specified
|
||||||
// config.
|
// config.
|
||||||
func (c *Controller) prepareAddOnion(cfg AddOnionConfig) (string, error) {
|
func (c *Controller) prepareAddOnion(cfg AddOnionConfig) (string, string,
|
||||||
|
error) {
|
||||||
|
|
||||||
// Create the keyParam.
|
// Create the keyParam.
|
||||||
keyParam, err := c.prepareKeyparam(cfg)
|
keyParam, err := c.prepareKeyparam(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, we'll create a mapping from the virtual port to each target
|
// Now, we'll create a mapping from the virtual port to each target
|
||||||
@@ -178,7 +260,7 @@ func (c *Controller) prepareAddOnion(cfg AddOnionConfig) (string, error) {
|
|||||||
// await its response.
|
// await its response.
|
||||||
cmd := fmt.Sprintf("ADD_ONION %s %s", keyParam, portParam)
|
cmd := fmt.Sprintf("ADD_ONION %s %s", keyParam, portParam)
|
||||||
|
|
||||||
return cmd, nil
|
return cmd, keyParam, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddOnion creates an ephemeral onion service and returns its onion address.
|
// AddOnion creates an ephemeral onion service and returns its onion address.
|
||||||
@@ -202,7 +284,7 @@ func (c *Controller) AddOnion(cfg AddOnionConfig) (*OnionAddr, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Construct the cmd command.
|
// Construct the cmd command.
|
||||||
cmd, err := c.prepareAddOnion(cfg)
|
cmd, keyParam, err := c.prepareAddOnion(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -234,11 +316,19 @@ func (c *Controller) AddOnion(cfg AddOnionConfig) (*OnionAddr, error) {
|
|||||||
return nil, errors.New("service id not found in reply")
|
return nil, errors.New("service id not found in reply")
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a new onion service was created and an onion store was provided,
|
// If a new onion service was created, use the new private key for
|
||||||
// we'll store its private key to disk in the event that it needs to be
|
// storage.
|
||||||
// recreated later on.
|
newPrivateKey, ok := replyParams["PrivateKey"]
|
||||||
if privateKey, ok := replyParams["PrivateKey"]; cfg.Store != nil && ok {
|
if ok {
|
||||||
err := cfg.Store.StorePrivateKey(cfg.Type, []byte(privateKey))
|
keyParam = newPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// If an onion store was provided and a key return wasn't requested,
|
||||||
|
// we'll store its private key to disk in the event that it needs to
|
||||||
|
// be recreated later on. We write the private key to disk every time
|
||||||
|
// in case the user toggles the --tor.encryptkey flag.
|
||||||
|
if cfg.Store != nil {
|
||||||
|
err := cfg.Store.StorePrivateKey([]byte(keyParam))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to write private key "+
|
return nil, fmt.Errorf("unable to write private key "+
|
||||||
"to file: %v", err)
|
"to file: %v", err)
|
||||||
@@ -254,6 +344,7 @@ func (c *Controller) AddOnion(cfg AddOnionConfig) (*OnionAddr, error) {
|
|||||||
return &OnionAddr{
|
return &OnionAddr{
|
||||||
OnionService: serviceID + ".onion",
|
OnionService: serviceID + ".onion",
|
||||||
Port: cfg.VirtualPort,
|
Port: cfg.VirtualPort,
|
||||||
|
PrivateKey: keyParam,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
package tor
|
package tor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -10,40 +10,61 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestOnionFile tests that the OnionFile implementation of the OnionStore
|
var (
|
||||||
|
privateKey = []byte("RSA1024 hide_me_plz")
|
||||||
|
anotherKey = []byte("another_key")
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestOnionFile tests that the File implementation of the OnionStore
|
||||||
// interface behaves as expected.
|
// interface behaves as expected.
|
||||||
func TestOnionFile(t *testing.T) {
|
func TestOnionFile(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
privateKey := []byte("hide_me_plz")
|
tempDir := t.TempDir()
|
||||||
privateKeyPath := filepath.Join(t.TempDir(), "secret")
|
privateKeyPath := filepath.Join(tempDir, "secret")
|
||||||
|
mockEncrypter := MockEncrypter{}
|
||||||
|
|
||||||
// Create a new file-based onion store. A private key should not exist
|
// Create a new file-based onion store. A private key should not exist
|
||||||
// yet.
|
// yet.
|
||||||
onionFile := NewOnionFile(privateKeyPath, 0600)
|
onionFile := NewOnionFile(
|
||||||
if _, err := onionFile.PrivateKey(V2); err != ErrNoPrivateKey {
|
privateKeyPath, 0600, false, mockEncrypter,
|
||||||
t.Fatalf("expected ErrNoPrivateKey, got \"%v\"", err)
|
)
|
||||||
}
|
_, err := onionFile.PrivateKey()
|
||||||
|
require.ErrorIs(t, err, ErrNoPrivateKey)
|
||||||
|
|
||||||
// Store the private key and ensure what's stored matches.
|
// Store the private key and ensure what's stored matches.
|
||||||
if err := onionFile.StorePrivateKey(V2, privateKey); err != nil {
|
err = onionFile.StorePrivateKey(privateKey)
|
||||||
t.Fatalf("unable to store private key: %v", err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
storePrivateKey, err := onionFile.PrivateKey(V2)
|
storePrivateKey, err := onionFile.PrivateKey()
|
||||||
require.NoError(t, err, "unable to retrieve private key")
|
require.NoError(t, err)
|
||||||
if !bytes.Equal(storePrivateKey, privateKey) {
|
require.Equal(t, storePrivateKey, privateKey)
|
||||||
t.Fatalf("expected private key \"%v\", got \"%v\"",
|
|
||||||
string(privateKey), string(storePrivateKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, delete the private key. We should no longer be able to
|
// Finally, delete the private key. We should no longer be able to
|
||||||
// retrieve it.
|
// retrieve it.
|
||||||
if err := onionFile.DeletePrivateKey(V2); err != nil {
|
err = onionFile.DeletePrivateKey()
|
||||||
t.Fatalf("unable to delete private key: %v", err)
|
require.NoError(t, err)
|
||||||
}
|
|
||||||
if _, err := onionFile.PrivateKey(V2); err != ErrNoPrivateKey {
|
_, err = onionFile.PrivateKey()
|
||||||
t.Fatal("found deleted private key")
|
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.
|
// TestPrepareKeyParam checks that the key param is created as expected.
|
||||||
@@ -63,7 +84,7 @@ func TestPrepareKeyParam(t *testing.T) {
|
|||||||
|
|
||||||
// Create a mock store which returns the test private key.
|
// Create a mock store which returns the test private key.
|
||||||
store := &mockStore{}
|
store := &mockStore{}
|
||||||
store.On("PrivateKey", cfg.Type).Return(testKey, nil)
|
store.On("PrivateKey").Return(testKey, nil)
|
||||||
|
|
||||||
// Check that the test private is returned.
|
// Check that the test private is returned.
|
||||||
cfg = AddOnionConfig{Type: V3, Store: store}
|
cfg = AddOnionConfig{Type: V3, Store: store}
|
||||||
@@ -75,7 +96,7 @@ func TestPrepareKeyParam(t *testing.T) {
|
|||||||
|
|
||||||
// Create a mock store which returns ErrNoPrivateKey.
|
// Create a mock store which returns ErrNoPrivateKey.
|
||||||
store = &mockStore{}
|
store = &mockStore{}
|
||||||
store.On("PrivateKey", cfg.Type).Return(nil, ErrNoPrivateKey)
|
store.On("PrivateKey").Return(nil, ErrNoPrivateKey)
|
||||||
|
|
||||||
// Check that the V3 keyParam is returned.
|
// Check that the V3 keyParam is returned.
|
||||||
cfg = AddOnionConfig{Type: V3, Store: store}
|
cfg = AddOnionConfig{Type: V3, Store: store}
|
||||||
@@ -87,7 +108,7 @@ func TestPrepareKeyParam(t *testing.T) {
|
|||||||
|
|
||||||
// Create a mock store which returns an dummy error.
|
// Create a mock store which returns an dummy error.
|
||||||
store = &mockStore{}
|
store = &mockStore{}
|
||||||
store.On("PrivateKey", cfg.Type).Return(nil, dummyErr)
|
store.On("PrivateKey").Return(nil, dummyErr)
|
||||||
|
|
||||||
// Check that an error is returned.
|
// Check that an error is returned.
|
||||||
cfg = AddOnionConfig{Type: V3, Store: store}
|
cfg = AddOnionConfig{Type: V3, Store: store}
|
||||||
@@ -158,14 +179,14 @@ func TestPrepareAddOnion(t *testing.T) {
|
|||||||
tc := tc
|
tc := tc
|
||||||
|
|
||||||
if tc.cfg.Store != nil {
|
if tc.cfg.Store != nil {
|
||||||
store.On("PrivateKey", tc.cfg.Type).Return(
|
store.On("PrivateKey").Return(
|
||||||
testKey, tc.expectedErr,
|
testKey, tc.expectedErr,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
controller := NewController("", tc.targetIPAddress, "")
|
controller := NewController("", tc.targetIPAddress, "")
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
cmd, err := controller.prepareAddOnion(tc.cfg)
|
cmd, _, err := controller.prepareAddOnion(tc.cfg)
|
||||||
require.Equal(t, tc.expectedErr, err)
|
require.Equal(t, tc.expectedErr, err)
|
||||||
require.Equal(t, tc.expectedCmd, cmd)
|
require.Equal(t, tc.expectedCmd, cmd)
|
||||||
|
|
||||||
@@ -184,20 +205,27 @@ type mockStore struct {
|
|||||||
// interface.
|
// interface.
|
||||||
var _ OnionStore = (*mockStore)(nil)
|
var _ OnionStore = (*mockStore)(nil)
|
||||||
|
|
||||||
func (m *mockStore) StorePrivateKey(ot OnionType, key []byte) error {
|
func (m *mockStore) StorePrivateKey(key []byte) error {
|
||||||
args := m.Called(ot, key)
|
args := m.Called(key)
|
||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockStore) PrivateKey(ot OnionType) ([]byte, error) {
|
func (m *mockStore) PrivateKey() ([]byte, error) {
|
||||||
args := m.Called(ot)
|
args := m.Called()
|
||||||
if args.Get(0) == nil {
|
return []byte("hide_me_plz"), args.Error(1)
|
||||||
return nil, args.Error(1)
|
|
||||||
}
|
|
||||||
return args.Get(0).([]byte), args.Error(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockStore) DeletePrivateKey(ot OnionType) error {
|
func (m *mockStore) DeletePrivateKey() error {
|
||||||
args := m.Called(ot)
|
args := m.Called()
|
||||||
return args.Error(0)
|
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
|
||||||
|
}
|
||||||
|
@@ -45,6 +45,9 @@ type OnionAddr struct {
|
|||||||
|
|
||||||
// Port is the port of the onion address.
|
// Port is the port of the onion address.
|
||||||
Port int
|
Port int
|
||||||
|
|
||||||
|
// PrivateKey is the onion address' private key.
|
||||||
|
PrivateKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
// A compile-time check to ensure that OnionAddr implements the net.Addr
|
// A compile-time check to ensure that OnionAddr implements the net.Addr
|
||||||
|
Reference in New Issue
Block a user