Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tor] Add ability to encrypt the Tor private key on disk #4458

Closed
Closed
4 changes: 3 additions & 1 deletion chanbackup/backupfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"os"
"path/filepath"
"testing"

"github.com/lightningnetwork/lnd/lnencrypt"
)

func makeFakePackedMulti() (PackedMulti, error) {
Expand Down Expand Up @@ -188,7 +190,7 @@ func assertMultiEqual(t *testing.T, a, b *Multi) {
func TestExtractMulti(t *testing.T) {
t.Parallel()

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we move to the mock.MockKeyRing here directly? Instead of creating a new one and then moving away from that in a later commit?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes so I think I was attempting to split out the mock key change into a new PR as requested here #4458 (comment)

But perhaps it'd be better to resquash it into one commit? Thoughts?


// First, as prep, we'll create a single chan backup, then pack that
// fully into a multi backup.
Expand Down
5 changes: 3 additions & 2 deletions chanbackup/multi.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"

"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnencrypt"
"github.com/lightningnetwork/lnd/lnwire"
)

Expand Down Expand Up @@ -89,7 +90,7 @@ func (m Multi) PackToWriter(w io.Writer, keyRing keychain.KeyRing) error {

// With the plaintext multi backup assembled, we'll now encrypt it
// directly to the passed writer.
return encryptPayloadToWriter(multiBackupBuffer, w, keyRing)
return lnencrypt.EncryptPayloadToWriter(multiBackupBuffer, w, keyRing)
}

// UnpackFromReader attempts to unpack (decrypt+deserialize) a packed
Expand All @@ -99,7 +100,7 @@ func (m *Multi) UnpackFromReader(r io.Reader, keyRing keychain.KeyRing) error {
// We'll attempt to read the entire packed backup, and also decrypt it
// using the passed key ring which is expected to be able to derive the
// encryption keys.
plaintextBackup, err := decryptPayloadFromReader(r, keyRing)
plaintextBackup, err := lnencrypt.DecryptPayloadFromReader(r, keyRing)
if err != nil {
return err
}
Expand Down
8 changes: 5 additions & 3 deletions chanbackup/multi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"bytes"
"net"
"testing"

"github.com/lightningnetwork/lnd/lnencrypt"
)

// TestMultiPackUnpack...
Expand All @@ -25,7 +27,7 @@ func TestMultiPackUnpack(t *testing.T) {
multi.StaticBackups = append(multi.StaticBackups, single)
}

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}

versionTestCases := []struct {
// version is the pack/unpack version that we should use to
Expand Down Expand Up @@ -97,7 +99,7 @@ func TestMultiPackUnpack(t *testing.T) {
fakeRawMulti := bytes.NewBuffer(
bytes.Repeat([]byte{99}, 20),
)
err := encryptPayloadToWriter(
err := lnencrypt.EncryptPayloadToWriter(
*fakeRawMulti, &fakePackedMulti, keyRing,
)
if err != nil {
Expand All @@ -122,7 +124,7 @@ func TestMultiPackUnpack(t *testing.T) {
func TestPackedMultiUnpack(t *testing.T) {
t.Parallel()

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}

// First, we'll make a new unpacked multi with a random channel.
testChannel, err := genRandomOpenChannelShell()
Expand Down
7 changes: 4 additions & 3 deletions chanbackup/pubsub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnencrypt"
)

type mockSwapper struct {
Expand Down Expand Up @@ -79,7 +80,7 @@ func (m *mockChannelNotifier) SubscribeChans(chans map[wire.OutPoint]struct{}) (
func TestNewSubSwapperSubscribeFail(t *testing.T) {
t.Parallel()

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}

var swapper mockSwapper
chanNotifier := mockChannelNotifier{
Expand Down Expand Up @@ -151,7 +152,7 @@ func assertExpectedBackupSwap(t *testing.T, swapper *mockSwapper,
func TestSubSwapperIdempotentStartStop(t *testing.T) {
t.Parallel()

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}

var chanNotifier mockChannelNotifier

Expand Down Expand Up @@ -182,7 +183,7 @@ func TestSubSwapperIdempotentStartStop(t *testing.T) {
func TestSubSwapperUpdater(t *testing.T) {
t.Parallel()

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}
chanNotifier := newMockChannelNotifier()
swapper := newMockSwapper(keyRing)

Expand Down
9 changes: 5 additions & 4 deletions chanbackup/recover_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/btcsuite/btcd/btcec"
"github.com/lightningnetwork/lnd/lnencrypt"
)

type mockChannelRestorer struct {
Expand Down Expand Up @@ -48,7 +49,7 @@ func (m *mockPeerConnector) ConnectPeer(node *btcec.PublicKey,
func TestUnpackAndRecoverSingles(t *testing.T) {
t.Parallel()

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}

// First, we'll create a number of single chan backups that we'll
// shortly back to so we can begin our recovery attempt.
Expand Down Expand Up @@ -124,7 +125,7 @@ func TestUnpackAndRecoverSingles(t *testing.T) {
}

// If we modify the keyRing, then unpacking should fail.
keyRing.fail = true
keyRing.Fail = true
err = UnpackAndRecoverSingles(
packedBackups, keyRing, &chanRestorer, &peerConnector,
)
Expand All @@ -140,7 +141,7 @@ func TestUnpackAndRecoverSingles(t *testing.T) {
func TestUnpackAndRecoverMulti(t *testing.T) {
t.Parallel()

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}

// First, we'll create a number of single chan backups that we'll
// shortly back to so we can begin our recovery attempt.
Expand Down Expand Up @@ -220,7 +221,7 @@ func TestUnpackAndRecoverMulti(t *testing.T) {
}

// If we modify the keyRing, then unpacking should fail.
keyRing.fail = true
keyRing.Fail = true
err = UnpackAndRecoverMulti(
packedMulti, keyRing, &chanRestorer, &peerConnector,
)
Expand Down
5 changes: 3 additions & 2 deletions chanbackup/single.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/btcsuite/btcutil"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnencrypt"
"github.com/lightningnetwork/lnd/lnwire"
)

Expand Down Expand Up @@ -333,7 +334,7 @@ func (s *Single) PackToWriter(w io.Writer, keyRing keychain.KeyRing) error {
// Finally, we'll encrypt the raw serialized SCB (using the nonce as
// associated data), and write out the ciphertext prepend with the
// nonce that we used to the passed io.Reader.
return encryptPayloadToWriter(rawBytes, w, keyRing)
return lnencrypt.EncryptPayloadToWriter(rawBytes, w, keyRing)
}

// readLocalKeyDesc reads a KeyDescriptor encoded within an unpacked Single.
Expand Down Expand Up @@ -512,7 +513,7 @@ func (s *Single) Deserialize(r io.Reader) error {
// payload for whatever reason (wrong key, wrong nonce, etc), then this method
// will return an error.
func (s *Single) UnpackFromReader(r io.Reader, keyRing keychain.KeyRing) error {
plaintext, err := decryptPayloadFromReader(r, keyRing)
plaintext, err := lnencrypt.DecryptPayloadFromReader(r, keyRing)
if err != nil {
return err
}
Expand Down
13 changes: 7 additions & 6 deletions chanbackup/single_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/keychain"
"github.com/lightningnetwork/lnd/lnencrypt"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/shachain"
)
Expand Down Expand Up @@ -211,7 +212,7 @@ func TestSinglePackUnpack(t *testing.T) {
singleChanBackup := NewSingle(channel, []net.Addr{addr1, addr2})
singleChanBackup.RemoteNodePub.Curve = nil

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}

versionTestCases := []struct {
// version is the pack/unpack version that we should use to
Expand Down Expand Up @@ -317,7 +318,7 @@ func TestSinglePackUnpack(t *testing.T) {
func TestPackedSinglesUnpack(t *testing.T) {
t.Parallel()

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}

// To start, we'll create 10 new singles, and them assemble their
// packed forms into a slice.
Expand Down Expand Up @@ -368,7 +369,7 @@ func TestPackedSinglesUnpack(t *testing.T) {
func TestSinglePackStaticChanBackups(t *testing.T) {
t.Parallel()

keyRing := &mockKeyRing{}
keyRing := &lnencrypt.MockKeyRing{}

// First, we'll create a set of random single, and along the way,
// create a map that will let us look up each single by its chan point.
Expand Down Expand Up @@ -416,8 +417,9 @@ func TestSinglePackStaticChanBackups(t *testing.T) {

// If we attempt to pack again, but force the key ring to fail, then
// the entire method should fail.
keyRing.Fail = true
_, err = PackStaticChanBackups(
unpackedSingles, &mockKeyRing{true},
unpackedSingles, &lnencrypt.MockKeyRing{true},
)
if err == nil {
t.Fatalf("pack attempt should fail")
Expand All @@ -443,8 +445,7 @@ func TestSingleUnconfirmedChannel(t *testing.T) {
channel.FundingBroadcastHeight = fundingBroadcastHeight

singleChanBackup := NewSingle(channel, []net.Addr{addr1, addr2})
keyRing := &mockKeyRing{}

keyRing := &lnencrypt.MockKeyRing{}
// Pack it and then unpack it again to make sure everything is written
// correctly, then check that the block height of the unpacked
// is the funding broadcast height we set before.
Expand Down
28 changes: 13 additions & 15 deletions chanbackup/crypto.go → lnencrypt/crypto.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package chanbackup
package lnencrypt

import (
"bytes"
Expand Down Expand Up @@ -50,18 +50,18 @@ func genEncryptionKey(keyRing keychain.KeyRing) ([]byte, error) {
return encryptionKey[:], nil
}

// encryptPayloadToWriter attempts to write the set of bytes contained within
// EncryptPayloadToWriter attempts to write the set of bytes contained within
// the passed byes.Buffer into the passed io.Writer in an encrypted form. We
// use a 24-byte chachapoly AEAD instance with a randomized nonce that's
// pre-pended to the final payload and used as associated data in the AEAD. We
// use the passed keyRing to generate the encryption key, see genEncryptionKey
// for further details.
func encryptPayloadToWriter(payload bytes.Buffer, w io.Writer,
func EncryptPayloadToWriter(payload bytes.Buffer, w io.Writer,
keyRing keychain.KeyRing) error {

// First, we'll derive the key that we'll use to encrypt the payload
// for safe storage without giving away the details of any of our
// channels. The final operation is:
// for safe storage without giving away the details of any sensitive
// details. The final operation is:
//
// key = SHA256(baseKey)
encryptionKey, err := genEncryptionKey(keyRing)
Expand Down Expand Up @@ -96,37 +96,35 @@ func encryptPayloadToWriter(payload bytes.Buffer, w io.Writer,
return nil
}

// decryptPayloadFromReader attempts to decrypt the encrypted bytes within the
// DecryptPayloadFromReader attempts to decrypt the encrypted bytes within the
// passed io.Reader instance using the key derived from the passed keyRing. For
// further details regarding the key derivation protocol, see the
// genEncryptionKey method.
func decryptPayloadFromReader(payload io.Reader,
func DecryptPayloadFromReader(payload io.Reader,
keyRing keychain.KeyRing) ([]byte, error) {

// First, we'll re-generate the encryption key that we use for all the
// SCBs.
// First, we'll re-generate the encryption key.
encryptionKey, err := genEncryptionKey(keyRing)
if err != nil {
return nil, err
}

// Next, we'll read out the entire blob as we need to isolate the nonce
// from the rest of the ciphertext.
packedBackup, err := ioutil.ReadAll(payload)
packedPayload, err := ioutil.ReadAll(payload)
if err != nil {
return nil, err
}
if len(packedBackup) < chacha20poly1305.NonceSizeX {
if len(packedPayload) < chacha20poly1305.NonceSizeX {
return nil, fmt.Errorf("payload size too small, must be at "+
"least %v bytes", chacha20poly1305.NonceSizeX)
}

nonce := packedBackup[:chacha20poly1305.NonceSizeX]
ciphertext := packedBackup[chacha20poly1305.NonceSizeX:]
nonce := packedPayload[:chacha20poly1305.NonceSizeX]
ciphertext := packedPayload[chacha20poly1305.NonceSizeX:]

// Now that we have the cipher text and the nonce separated, we can go
// ahead and decrypt the final blob so we can properly serialized the
// SCB.
// ahead and decrypt the final blob so we can properly serialize.
cipher, err := chacha20poly1305.NewX(encryptionKey)
if err != nil {
return nil, err
Expand Down
Loading