/ htlcswitch / hop / error_encryptor.go
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  }