mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 18:10:34 +01:00
e0fc5bb234
The functions inside of the crypto.go file in chanbackup (like EncryptPayloadToWriter and DecryptPayloadFromReader) can be used by a lot of things outside of just the chanbackup package. We can't just reference them directly from the chanbackup package because it's likely that it would generate circular dependencies. Therefore we need to move these functions into their own package to be referenced by chanbackup and whatever new functionality that needs them
192 lines
6.3 KiB
Go
192 lines
6.3 KiB
Go
package chanbackup
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/lightningnetwork/lnd/keychain"
|
|
"github.com/lightningnetwork/lnd/lnencrypt"
|
|
"github.com/lightningnetwork/lnd/lnwire"
|
|
)
|
|
|
|
// MultiBackupVersion denotes the version of the multi channel static channel
|
|
// backup. Based on this version, we know how to encode/decode packed/unpacked
|
|
// versions of multi backups.
|
|
type MultiBackupVersion byte
|
|
|
|
const (
|
|
// DefaultMultiVersion is the default version of the multi channel
|
|
// backup. The serialized format for this version is simply: version ||
|
|
// numBackups || SCBs...
|
|
DefaultMultiVersion = 0
|
|
|
|
// NilMultiSizePacked is the size of a "nil" packed Multi (45 bytes).
|
|
// This consists of the 24 byte chacha nonce, the 16 byte MAC, one byte
|
|
// for the version, and 4 bytes to signal zero entries.
|
|
NilMultiSizePacked = 24 + 16 + 1 + 4
|
|
)
|
|
|
|
// Multi is a form of static channel backup that is amenable to being
|
|
// serialized in a single file. Rather than a series of ciphertexts, a
|
|
// multi-chan backup is a single ciphertext of all static channel backups
|
|
// concatenated. This form factor gives users a single blob that they can use
|
|
// to safely copy/obtain at anytime to backup their channels.
|
|
type Multi struct {
|
|
// Version is the version that should be observed when attempting to
|
|
// pack the multi backup.
|
|
Version MultiBackupVersion
|
|
|
|
// StaticBackups is the set of single channel backups that this multi
|
|
// backup is comprised of.
|
|
StaticBackups []Single
|
|
}
|
|
|
|
// PackToWriter packs (encrypts+serializes) the target set of static channel
|
|
// backups into a single AEAD ciphertext into the passed io.Writer. This is the
|
|
// opposite of UnpackFromReader. The plaintext form of a multi-chan backup is
|
|
// the following: a 4 byte integer denoting the number of serialized static
|
|
// channel backups serialized, a series of serialized static channel backups
|
|
// concatenated. To pack this payload, we then apply our chacha20 AEAD to the
|
|
// entire payload, using the 24-byte nonce as associated data.
|
|
func (m Multi) PackToWriter(w io.Writer, keyRing keychain.KeyRing) error {
|
|
// The only version that we know how to pack atm is version 0. Attempts
|
|
// to pack any other version will result in an error.
|
|
switch m.Version {
|
|
case DefaultMultiVersion:
|
|
break
|
|
|
|
default:
|
|
return fmt.Errorf("unable to pack unknown multi-version "+
|
|
"of %v", m.Version)
|
|
}
|
|
|
|
var multiBackupBuffer bytes.Buffer
|
|
|
|
// First, we'll write out the version of this multi channel baackup.
|
|
err := lnwire.WriteElements(&multiBackupBuffer, byte(m.Version))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Now that we've written out the version of this multi-pack format,
|
|
// we'll now write the total number of backups to expect after this
|
|
// point.
|
|
numBackups := uint32(len(m.StaticBackups))
|
|
err = lnwire.WriteElements(&multiBackupBuffer, numBackups)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Next, we'll serialize the raw plaintext version of each of the
|
|
// backup into the intermediate buffer.
|
|
for _, chanBackup := range m.StaticBackups {
|
|
err := chanBackup.Serialize(&multiBackupBuffer)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to serialize backup "+
|
|
"for %v: %v", chanBackup.FundingOutpoint, err)
|
|
}
|
|
}
|
|
|
|
// With the plaintext multi backup assembled, we'll now encrypt it
|
|
// directly to the passed writer.
|
|
e, err := lnencrypt.KeyRingEncrypter(keyRing)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to generate encrypt key %v", err)
|
|
}
|
|
|
|
return e.EncryptPayloadToWriter(multiBackupBuffer.Bytes(), w)
|
|
}
|
|
|
|
// UnpackFromReader attempts to unpack (decrypt+deserialize) a packed
|
|
// multi-chan backup form the passed io.Reader. If we're unable to decrypt the
|
|
// any portion of the multi-chan backup, an error will be returned.
|
|
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.
|
|
e, err := lnencrypt.KeyRingEncrypter(keyRing)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to generate encrypt key %v", err)
|
|
}
|
|
plaintextBackup, err := e.DecryptPayloadFromReader(r)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
backupReader := bytes.NewReader(plaintextBackup)
|
|
|
|
// Now that we've decrypted the payload successfully, we can parse out
|
|
// each of the individual static channel backups.
|
|
|
|
// First, we'll need to read the version of this multi-back up so we
|
|
// can know how to unpack each of the individual SCB's.
|
|
var multiVersion byte
|
|
err = lnwire.ReadElements(backupReader, &multiVersion)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
m.Version = MultiBackupVersion(multiVersion)
|
|
switch m.Version {
|
|
|
|
// The default version is simply a set of serialized SCB's with the
|
|
// number of total SCB's prepended to the front of the byte slice.
|
|
case DefaultMultiVersion:
|
|
// First, we'll need to read out the total number of backups
|
|
// that've been serialized into this multi-chan backup. Each
|
|
// backup is the same size, so we can continue until we've
|
|
// parsed out everything.
|
|
var numBackups uint32
|
|
err = lnwire.ReadElements(backupReader, &numBackups)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// We'll continue to parse out each backup until we've read all
|
|
// that was indicated from the length prefix.
|
|
for ; numBackups != 0; numBackups-- {
|
|
// Attempt to parse out the net static channel backup,
|
|
// if it's been malformed, then we'll return with an
|
|
// error
|
|
var chanBackup Single
|
|
err := chanBackup.Deserialize(backupReader)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Collect the next valid chan backup into the main
|
|
// multi backup slice.
|
|
m.StaticBackups = append(m.StaticBackups, chanBackup)
|
|
}
|
|
|
|
default:
|
|
return fmt.Errorf("unable to unpack unknown multi-version "+
|
|
"of %v", multiVersion)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// TODO(roasbeef): new key ring interface?
|
|
// * just returns key given params?
|
|
|
|
// PackedMulti represents a raw fully packed (serialized+encrypted)
|
|
// multi-channel static channel backup.
|
|
type PackedMulti []byte
|
|
|
|
// Unpack attempts to unpack (decrypt+desrialize) the target packed
|
|
// multi-channel back up. If we're unable to fully unpack this back, then an
|
|
// error will be returned.
|
|
func (p *PackedMulti) Unpack(keyRing keychain.KeyRing) (*Multi, error) {
|
|
var m Multi
|
|
|
|
packedReader := bytes.NewReader(*p)
|
|
if err := m.UnpackFromReader(packedReader, keyRing); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &m, nil
|
|
}
|
|
|
|
// TODO(roasbsef): fuzz parsing
|