error_encryptor.go
1 package hop 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 8 "github.com/btcsuite/btcd/btcec/v2" 9 sphinx "github.com/lightningnetwork/lightning-onion" 10 "github.com/lightningnetwork/lnd/lnwire" 11 ) 12 13 // EncrypterType establishes an enum used in serialization to indicate how to 14 // decode a concrete instance of the ErrorEncrypter interface. 15 type EncrypterType byte 16 17 const ( 18 // EncrypterTypeNone signals that no error encyrpter is present, this 19 // can happen if the htlc is originates in the switch. 20 EncrypterTypeNone EncrypterType = 0 21 22 // EncrypterTypeSphinx is used to identify a sphinx onion error 23 // encrypter instance. 24 EncrypterTypeSphinx = 1 25 26 // EncrypterTypeMock is used to identify a mock obfuscator instance. 27 EncrypterTypeMock = 2 28 29 // EncrypterTypeIntroduction is used to identify a sphinx onion error 30 // encrypter where we are the introduction node in a blinded route. It 31 // has the same functionality as EncrypterTypeSphinx, but is used to 32 // mark our special-case error handling. 33 EncrypterTypeIntroduction = 3 34 35 // EncrypterTypeRelaying is used to identify a sphinx onion error 36 // encryper where we are a relaying node in a blinded route. It has 37 // the same functionality as a EncrypterTypeSphinx, but is used to mark 38 // our special-case error handling. 39 EncrypterTypeRelaying = 4 40 ) 41 42 // IsBlinded returns a boolean indicating whether the error encrypter belongs 43 // to a blinded route. 44 func (e EncrypterType) IsBlinded() bool { 45 return e == EncrypterTypeIntroduction || e == EncrypterTypeRelaying 46 } 47 48 // ErrorEncrypterExtracter defines a function signature that extracts an 49 // ErrorEncrypter from an sphinx OnionPacket. 50 type ErrorEncrypterExtracter func(*btcec.PublicKey) (ErrorEncrypter, 51 lnwire.FailCode) 52 53 // ErrorEncrypter is an interface that is used to encrypt HTLC related errors 54 // at the source of the error, and also at each intermediate hop all the way 55 // back to the source of the payment. 56 type ErrorEncrypter interface { 57 // EncryptFirstHop transforms a concrete failure message into an 58 // encrypted opaque failure reason. This method will be used at the 59 // source that the error occurs. It differs from IntermediateEncrypt 60 // slightly, in that it computes a proper MAC over the error. 61 EncryptFirstHop(lnwire.FailureMessage) (lnwire.OpaqueReason, error) 62 63 // EncryptMalformedError is similar to EncryptFirstHop (it adds the 64 // MAC), but it accepts an opaque failure reason rather than a failure 65 // message. This method is used when we receive an 66 // UpdateFailMalformedHTLC from the remote peer and then need to 67 // convert that into a proper error from only the raw bytes. 68 EncryptMalformedError(lnwire.OpaqueReason) lnwire.OpaqueReason 69 70 // IntermediateEncrypt wraps an already encrypted opaque reason error 71 // in an additional layer of onion encryption. This process repeats 72 // until the error arrives at the source of the payment. 73 IntermediateEncrypt(lnwire.OpaqueReason) lnwire.OpaqueReason 74 75 // Type returns an enum indicating the underlying concrete instance 76 // backing this interface. 77 Type() EncrypterType 78 79 // Encode serializes the encrypter's ephemeral public key to the given 80 // io.Writer. 81 Encode(io.Writer) error 82 83 // Decode deserializes the encrypter' ephemeral public key from the 84 // given io.Reader. 85 Decode(io.Reader) error 86 87 // Reextract rederives the encrypter using the extracter, performing an 88 // ECDH with the sphinx router's key and the ephemeral public key. 89 // 90 // NOTE: This should be called shortly after Decode to properly 91 // reinitialize the error encrypter. 92 Reextract(ErrorEncrypterExtracter) error 93 } 94 95 // SphinxErrorEncrypter is a concrete implementation of both the ErrorEncrypter 96 // interface backed by an implementation of the Sphinx packet format. As a 97 // result, all errors handled are themselves wrapped in layers of onion 98 // encryption and must be treated as such accordingly. 99 type SphinxErrorEncrypter struct { 100 *sphinx.OnionErrorEncrypter 101 102 EphemeralKey *btcec.PublicKey 103 } 104 105 // NewSphinxErrorEncrypter initializes a blank sphinx error encrypter, that 106 // should be used to deserialize an encoded SphinxErrorEncrypter. Since the 107 // actual encrypter is not stored in plaintext while at rest, reconstructing the 108 // error encrypter requires: 109 // 1. Decode: to deserialize the ephemeral public key. 110 // 2. Reextract: to "unlock" the actual error encrypter using an active 111 // OnionProcessor. 112 func NewSphinxErrorEncrypter() *SphinxErrorEncrypter { 113 return &SphinxErrorEncrypter{ 114 OnionErrorEncrypter: nil, 115 EphemeralKey: &btcec.PublicKey{}, 116 } 117 } 118 119 // EncryptFirstHop transforms a concrete failure message into an encrypted 120 // opaque failure reason. This method will be used at the source that the error 121 // occurs. It differs from BackwardObfuscate slightly, in that it computes a 122 // proper MAC over the error. 123 // 124 // NOTE: Part of the ErrorEncrypter interface. 125 func (s *SphinxErrorEncrypter) EncryptFirstHop( 126 failure lnwire.FailureMessage) (lnwire.OpaqueReason, error) { 127 128 var b bytes.Buffer 129 if err := lnwire.EncodeFailure(&b, failure, 0); err != nil { 130 return nil, err 131 } 132 133 // We pass a true as the first parameter to indicate that a MAC should 134 // be added. 135 return s.EncryptError(true, b.Bytes()), nil 136 } 137 138 // EncryptMalformedError is similar to EncryptFirstHop (it adds the MAC), but 139 // it accepts an opaque failure reason rather than a failure message. This 140 // method is used when we receive an UpdateFailMalformedHTLC from the remote 141 // peer and then need to convert that into an proper error from only the raw 142 // bytes. 143 // 144 // NOTE: Part of the ErrorEncrypter interface. 145 func (s *SphinxErrorEncrypter) EncryptMalformedError( 146 reason lnwire.OpaqueReason) lnwire.OpaqueReason { 147 148 return s.EncryptError(true, reason) 149 } 150 151 // IntermediateEncrypt wraps an already encrypted opaque reason error in an 152 // additional layer of onion encryption. This process repeats until the error 153 // arrives at the source of the payment. We re-encrypt the message on the 154 // backwards path to ensure that the error is indistinguishable from any other 155 // error seen. 156 // 157 // NOTE: Part of the ErrorEncrypter interface. 158 func (s *SphinxErrorEncrypter) IntermediateEncrypt( 159 reason lnwire.OpaqueReason) lnwire.OpaqueReason { 160 161 return s.EncryptError(false, reason) 162 } 163 164 // Type returns the identifier for a sphinx error encrypter. 165 func (s *SphinxErrorEncrypter) Type() EncrypterType { 166 return EncrypterTypeSphinx 167 } 168 169 // Encode serializes the error encrypter' ephemeral public key to the provided 170 // io.Writer. 171 func (s *SphinxErrorEncrypter) Encode(w io.Writer) error { 172 ephemeral := s.EphemeralKey.SerializeCompressed() 173 _, err := w.Write(ephemeral) 174 return err 175 } 176 177 // Decode reconstructs the error encrypter's ephemeral public key from the 178 // provided io.Reader. 179 func (s *SphinxErrorEncrypter) Decode(r io.Reader) error { 180 var ephemeral [33]byte 181 if _, err := io.ReadFull(r, ephemeral[:]); err != nil { 182 return err 183 } 184 185 var err error 186 s.EphemeralKey, err = btcec.ParsePubKey(ephemeral[:]) 187 if err != nil { 188 return err 189 } 190 191 return nil 192 } 193 194 // Reextract rederives the error encrypter from the currently held EphemeralKey. 195 // This intended to be used shortly after Decode, to fully initialize a 196 // SphinxErrorEncrypter. 197 func (s *SphinxErrorEncrypter) Reextract( 198 extract ErrorEncrypterExtracter) error { 199 200 obfuscator, failcode := extract(s.EphemeralKey) 201 if failcode != lnwire.CodeNone { 202 // This should never happen, since we already validated that 203 // this obfuscator can be extracted when it was received in the 204 // link. 205 return fmt.Errorf("unable to reconstruct onion "+ 206 "obfuscator, got failcode: %d", failcode) 207 } 208 209 sphinxEncrypter, ok := obfuscator.(*SphinxErrorEncrypter) 210 if !ok { 211 return fmt.Errorf("incorrect onion error extracter") 212 } 213 214 // Copy the freshly extracted encrypter. 215 s.OnionErrorEncrypter = sphinxEncrypter.OnionErrorEncrypter 216 217 return nil 218 } 219 220 // A compile time check to ensure SphinxErrorEncrypter implements the 221 // ErrorEncrypter interface. 222 var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil) 223 224 // A compile time check to ensure that IntroductionErrorEncrypter implements 225 // the ErrorEncrypter interface. 226 var _ ErrorEncrypter = (*IntroductionErrorEncrypter)(nil) 227 228 // IntroductionErrorEncrypter is a wrapper type on SphinxErrorEncrypter which 229 // is used to signal that we have special HTLC error handling for this hop. 230 type IntroductionErrorEncrypter struct { 231 // ErrorEncrypter is the underlying error encrypter, embedded 232 // directly in the struct so that we don't have to re-implement the 233 // ErrorEncrypter interface. 234 ErrorEncrypter 235 } 236 237 // NewIntroductionErrorEncrypter returns a blank IntroductionErrorEncrypter. 238 func NewIntroductionErrorEncrypter() *IntroductionErrorEncrypter { 239 return &IntroductionErrorEncrypter{ 240 ErrorEncrypter: NewSphinxErrorEncrypter(), 241 } 242 } 243 244 // Type returns the identifier for an introduction error encrypter. 245 func (i *IntroductionErrorEncrypter) Type() EncrypterType { 246 return EncrypterTypeIntroduction 247 } 248 249 // Reextract rederives the error encrypter from the currently held EphemeralKey, 250 // relying on the logic in the underlying SphinxErrorEncrypter. 251 func (i *IntroductionErrorEncrypter) Reextract( 252 extract ErrorEncrypterExtracter) error { 253 254 return i.ErrorEncrypter.Reextract(extract) 255 } 256 257 // A compile time check to ensure that RelayingErrorEncrypte implements 258 // the ErrorEncrypter interface. 259 var _ ErrorEncrypter = (*RelayingErrorEncrypter)(nil) 260 261 // RelayingErrorEncrypter is a wrapper type on SphinxErrorEncrypter which 262 // is used to signal that we have special HTLC error handling for this hop. 263 type RelayingErrorEncrypter struct { 264 ErrorEncrypter 265 } 266 267 // NewRelayingErrorEncrypter returns a blank RelayingErrorEncrypter with 268 // an underlying SphinxErrorEncrypter. 269 func NewRelayingErrorEncrypter() *RelayingErrorEncrypter { 270 return &RelayingErrorEncrypter{ 271 ErrorEncrypter: NewSphinxErrorEncrypter(), 272 } 273 } 274 275 // Type returns the identifier for a relaying error encrypter. 276 func (r *RelayingErrorEncrypter) Type() EncrypterType { 277 return EncrypterTypeRelaying 278 } 279 280 // Reextract rederives the error encrypter from the currently held EphemeralKey, 281 // relying on the logic in the underlying SphinxErrorEncrypter. 282 func (r *RelayingErrorEncrypter) Reextract( 283 extract ErrorEncrypterExtracter) error { 284 285 return r.ErrorEncrypter.Reextract(extract) 286 }