github.com/decred/dcrlnd@v0.7.6/chanbackup/multi.go (about) 1 package chanbackup 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 8 "github.com/decred/dcrlnd/keychain" 9 "github.com/decred/dcrlnd/lnwire" 10 ) 11 12 // MultiBackupVersion denotes the version of the multi channel static channel 13 // backup. Based on this version, we know how to encode/decode packed/unpacked 14 // versions of multi backups. 15 type MultiBackupVersion byte 16 17 const ( 18 // DefaultMultiVersion is the default version of the multi channel 19 // backup. The serialized format for this version is simply: version || 20 // numBackups || SCBs... 21 DefaultMultiVersion = 0 22 23 // NilMultiSizePacked is the size of a "nil" packed Multi (45 bytes). 24 // This consists of the 24 byte chacha nonce, the 16 byte MAC, one byte 25 // for the version, and 4 bytes to signal zero entries. 26 NilMultiSizePacked = 24 + 16 + 1 + 4 27 ) 28 29 // Multi is a form of static channel backup that is amenable to being 30 // serialized in a single file. Rather than a series of ciphertexts, a 31 // multi-chan backup is a single ciphertext of all static channel backups 32 // concatenated. This form factor gives users a single blob that they can use 33 // to safely copy/obtain at anytime to backup their channels. 34 type Multi struct { 35 // Version is the version that should be observed when attempting to 36 // pack the multi backup. 37 Version MultiBackupVersion 38 39 // StaticBackups is the set of single channel backups that this multi 40 // backup is comprised of. 41 StaticBackups []Single 42 } 43 44 // PackToWriter packs (encrypts+serializes) the target set of static channel 45 // backups into a single AEAD ciphertext into the passed io.Writer. This is the 46 // opposite of UnpackFromReader. The plaintext form of a multi-chan backup is 47 // the following: a 4 byte integer denoting the number of serialized static 48 // channel backups serialized, a series of serialized static channel backups 49 // concatenated. To pack this payload, we then apply our chacha20 AEAD to the 50 // entire payload, using the 24-byte nonce as associated data. 51 func (m Multi) PackToWriter(w io.Writer, keyRing keychain.KeyRing) error { 52 // The only version that we know how to pack atm is version 0. Attempts 53 // to pack any other version will result in an error. 54 switch m.Version { 55 case DefaultMultiVersion: 56 break 57 58 default: 59 return fmt.Errorf("unable to pack unknown multi-version "+ 60 "of %v", m.Version) 61 } 62 63 var multiBackupBuffer bytes.Buffer 64 65 // First, we'll write out the version of this multi channel baackup. 66 err := lnwire.WriteElements(&multiBackupBuffer, byte(m.Version)) 67 if err != nil { 68 return err 69 } 70 71 // Now that we've written out the version of this multi-pack format, 72 // we'll now write the total number of backups to expect after this 73 // point. 74 numBackups := uint32(len(m.StaticBackups)) 75 err = lnwire.WriteElements(&multiBackupBuffer, numBackups) 76 if err != nil { 77 return err 78 } 79 80 // Next, we'll serialize the raw plaintext version of each of the 81 // backup into the intermediate buffer. 82 for _, chanBackup := range m.StaticBackups { 83 err := chanBackup.Serialize(&multiBackupBuffer) 84 if err != nil { 85 return fmt.Errorf("unable to serialize backup "+ 86 "for %v: %v", chanBackup.FundingOutpoint, err) 87 } 88 } 89 90 // With the plaintext multi backup assembled, we'll now encrypt it 91 // directly to the passed writer. 92 return encryptPayloadToWriter(multiBackupBuffer, w, keyRing) 93 } 94 95 // UnpackFromReader attempts to unpack (decrypt+deserialize) a packed 96 // multi-chan backup form the passed io.Reader. If we're unable to decrypt the 97 // any portion of the multi-chan backup, an error will be returned. 98 func (m *Multi) UnpackFromReader(r io.Reader, keyRing keychain.KeyRing) error { 99 // We'll attempt to read the entire packed backup, and also decrypt it 100 // using the passed key ring which is expected to be able to derive the 101 // encryption keys. 102 plaintextBackup, err := decryptPayloadFromReader(r, keyRing) 103 if err != nil { 104 return err 105 } 106 backupReader := bytes.NewReader(plaintextBackup) 107 108 // Now that we've decrypted the payload successfully, we can parse out 109 // each of the individual static channel backups. 110 111 // First, we'll need to read the version of this multi-back up so we 112 // can know how to unpack each of the individual SCB's. 113 var multiVersion byte 114 err = lnwire.ReadElements(backupReader, &multiVersion) 115 if err != nil { 116 return err 117 } 118 119 m.Version = MultiBackupVersion(multiVersion) 120 switch m.Version { 121 122 // The default version is simply a set of serialized SCB's with the 123 // number of total SCB's prepended to the front of the byte slice. 124 case DefaultMultiVersion: 125 // First, we'll need to read out the total number of backups 126 // that've been serialized into this multi-chan backup. Each 127 // backup is the same size, so we can continue until we've 128 // parsed out everything. 129 var numBackups uint32 130 err = lnwire.ReadElements(backupReader, &numBackups) 131 if err != nil { 132 return err 133 } 134 135 // We'll continue to parse out each backup until we've read all 136 // that was indicated from the length prefix. 137 for ; numBackups != 0; numBackups-- { 138 // Attempt to parse out the net static channel backup, 139 // if it's been malformed, then we'll return with an 140 // error 141 var chanBackup Single 142 err := chanBackup.Deserialize(backupReader) 143 if err != nil { 144 return err 145 } 146 147 // Collect the next valid chan backup into the main 148 // multi backup slice. 149 m.StaticBackups = append(m.StaticBackups, chanBackup) 150 } 151 152 default: 153 return fmt.Errorf("unable to unpack unknown multi-version "+ 154 "of %v", multiVersion) 155 } 156 157 return nil 158 } 159 160 // TODO(roasbeef): new key ring interface? 161 // * just returns key given params? 162 163 // PackedMulti represents a raw fully packed (serialized+encrypted) 164 // multi-channel static channel backup. 165 type PackedMulti []byte 166 167 // Unpack attempts to unpack (decrypt+desrialize) the target packed 168 // multi-channel back up. If we're unable to fully unpack this back, then an 169 // error will be returned. 170 func (p *PackedMulti) Unpack(keyRing keychain.KeyRing) (*Multi, error) { 171 var m Multi 172 173 packedReader := bytes.NewReader(*p) 174 if err := m.UnpackFromReader(packedReader, keyRing); err != nil { 175 return nil, err 176 } 177 178 return &m, nil 179 } 180 181 // TODO(roasbsef): fuzz parsing