github.com/decred/dcrlnd@v0.7.6/htlcswitch/hop/error_encryptor.go (about)

     1  package hop
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  
     8  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
     9  	"github.com/decred/dcrlnd/lnwire"
    10  	sphinx "github.com/decred/lightning-onion/v4"
    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  
    30  // ErrorEncrypterExtracter defines a function signature that extracts an
    31  // ErrorEncrypter from an sphinx OnionPacket.
    32  type ErrorEncrypterExtracter func(*secp256k1.PublicKey) (ErrorEncrypter,
    33  	lnwire.FailCode)
    34  
    35  // ErrorEncrypter is an interface that is used to encrypt HTLC related errors
    36  // at the source of the error, and also at each intermediate hop all the way
    37  // back to the source of the payment.
    38  type ErrorEncrypter interface {
    39  	// EncryptFirstHop transforms a concrete failure message into an
    40  	// encrypted opaque failure reason. This method will be used at the
    41  	// source that the error occurs. It differs from IntermediateEncrypt
    42  	// slightly, in that it computes a proper MAC over the error.
    43  	EncryptFirstHop(lnwire.FailureMessage) (lnwire.OpaqueReason, error)
    44  
    45  	// EncryptMalformedError is similar to EncryptFirstHop (it adds the
    46  	// MAC), but it accepts an opaque failure reason rather than a failure
    47  	// message. This method is used when we receive an
    48  	// UpdateFailMalformedHTLC from the remote peer and then need to
    49  	// convert that into a proper error from only the raw bytes.
    50  	EncryptMalformedError(lnwire.OpaqueReason) lnwire.OpaqueReason
    51  
    52  	// IntermediateEncrypt wraps an already encrypted opaque reason error
    53  	// in an additional layer of onion encryption. This process repeats
    54  	// until the error arrives at the source of the payment.
    55  	IntermediateEncrypt(lnwire.OpaqueReason) lnwire.OpaqueReason
    56  
    57  	// Type returns an enum indicating the underlying concrete instance
    58  	// backing this interface.
    59  	Type() EncrypterType
    60  
    61  	// Encode serializes the encrypter's ephemeral public key to the given
    62  	// io.Writer.
    63  	Encode(io.Writer) error
    64  
    65  	// Decode deserializes the encrypter' ephemeral public key from the
    66  	// given io.Reader.
    67  	Decode(io.Reader) error
    68  
    69  	// Reextract rederives the encrypter using the extracter, performing an
    70  	// ECDH with the sphinx router's key and the ephemeral public key.
    71  	//
    72  	// NOTE: This should be called shortly after Decode to properly
    73  	// reinitialize the error encrypter.
    74  	Reextract(ErrorEncrypterExtracter) error
    75  }
    76  
    77  // SphinxErrorEncrypter is a concrete implementation of both the ErrorEncrypter
    78  // interface backed by an implementation of the Sphinx packet format. As a
    79  // result, all errors handled are themselves wrapped in layers of onion
    80  // encryption and must be treated as such accordingly.
    81  type SphinxErrorEncrypter struct {
    82  	*sphinx.OnionErrorEncrypter
    83  
    84  	EphemeralKey *secp256k1.PublicKey
    85  }
    86  
    87  // NewSphinxErrorEncrypter initializes a blank sphinx error encrypter, that
    88  // should be used to deserialize an encoded SphinxErrorEncrypter. Since the
    89  // actual encrypter is not stored in plaintext while at rest, reconstructing the
    90  // error encrypter requires:
    91  //  1. Decode: to deserialize the ephemeral public key.
    92  //  2. Reextract: to "unlock" the actual error encrypter using an active
    93  //     OnionProcessor.
    94  func NewSphinxErrorEncrypter() *SphinxErrorEncrypter {
    95  	return &SphinxErrorEncrypter{
    96  		OnionErrorEncrypter: nil,
    97  		EphemeralKey:        &secp256k1.PublicKey{},
    98  	}
    99  }
   100  
   101  // EncryptFirstHop transforms a concrete failure message into an encrypted
   102  // opaque failure reason. This method will be used at the source that the error
   103  // occurs. It differs from BackwardObfuscate slightly, in that it computes a
   104  // proper MAC over the error.
   105  //
   106  // NOTE: Part of the ErrorEncrypter interface.
   107  func (s *SphinxErrorEncrypter) EncryptFirstHop(
   108  	failure lnwire.FailureMessage) (lnwire.OpaqueReason, error) {
   109  
   110  	var b bytes.Buffer
   111  	if err := lnwire.EncodeFailure(&b, failure, 0); err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	// We pass a true as the first parameter to indicate that a MAC should
   116  	// be added.
   117  	return s.EncryptError(true, b.Bytes()), nil
   118  }
   119  
   120  // EncryptMalformedError is similar to EncryptFirstHop (it adds the MAC), but
   121  // it accepts an opaque failure reason rather than a failure message. This
   122  // method is used when we receive an UpdateFailMalformedHTLC from the remote
   123  // peer and then need to convert that into an proper error from only the raw
   124  // bytes.
   125  //
   126  // NOTE: Part of the ErrorEncrypter interface.
   127  func (s *SphinxErrorEncrypter) EncryptMalformedError(
   128  	reason lnwire.OpaqueReason) lnwire.OpaqueReason {
   129  
   130  	return s.EncryptError(true, reason)
   131  }
   132  
   133  // IntermediateEncrypt wraps an already encrypted opaque reason error in an
   134  // additional layer of onion encryption. This process repeats until the error
   135  // arrives at the source of the payment. We re-encrypt the message on the
   136  // backwards path to ensure that the error is indistinguishable from any other
   137  // error seen.
   138  //
   139  // NOTE: Part of the ErrorEncrypter interface.
   140  func (s *SphinxErrorEncrypter) IntermediateEncrypt(
   141  	reason lnwire.OpaqueReason) lnwire.OpaqueReason {
   142  
   143  	return s.EncryptError(false, reason)
   144  }
   145  
   146  // Type returns the identifier for a sphinx error encrypter.
   147  func (s *SphinxErrorEncrypter) Type() EncrypterType {
   148  	return EncrypterTypeSphinx
   149  }
   150  
   151  // Encode serializes the error encrypter' ephemeral public key to the provided
   152  // io.Writer.
   153  func (s *SphinxErrorEncrypter) Encode(w io.Writer) error {
   154  	ephemeral := s.EphemeralKey.SerializeCompressed()
   155  	_, err := w.Write(ephemeral)
   156  	return err
   157  }
   158  
   159  // Decode reconstructs the error encrypter's ephemeral public key from the
   160  // provided io.Reader.
   161  func (s *SphinxErrorEncrypter) Decode(r io.Reader) error {
   162  	var ephemeral [33]byte
   163  	if _, err := io.ReadFull(r, ephemeral[:]); err != nil {
   164  		return err
   165  	}
   166  
   167  	var err error
   168  	s.EphemeralKey, err = secp256k1.ParsePubKey(ephemeral[:])
   169  	if err != nil {
   170  		return err
   171  	}
   172  
   173  	return nil
   174  }
   175  
   176  // Reextract rederives the error encrypter from the currently held EphemeralKey.
   177  // This intended to be used shortly after Decode, to fully initialize a
   178  // SphinxErrorEncrypter.
   179  func (s *SphinxErrorEncrypter) Reextract(
   180  	extract ErrorEncrypterExtracter) error {
   181  
   182  	obfuscator, failcode := extract(s.EphemeralKey)
   183  	if failcode != lnwire.CodeNone {
   184  		// This should never happen, since we already validated that
   185  		// this obfuscator can be extracted when it was received in the
   186  		// link.
   187  		return fmt.Errorf("unable to reconstruct onion "+
   188  			"obfuscator, got failcode: %d", failcode)
   189  	}
   190  
   191  	sphinxEncrypter, ok := obfuscator.(*SphinxErrorEncrypter)
   192  	if !ok {
   193  		return fmt.Errorf("incorrect onion error extracter")
   194  	}
   195  
   196  	// Copy the freshly extracted encrypter.
   197  	s.OnionErrorEncrypter = sphinxEncrypter.OnionErrorEncrypter
   198  
   199  	return nil
   200  
   201  }
   202  
   203  // A compile time check to ensure SphinxErrorEncrypter implements the
   204  // ErrorEncrypter interface.
   205  var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil)