diff --git a/htlcswitch/circuit.go b/htlcswitch/circuit.go index 2feb0601..ce091146 100644 --- a/htlcswitch/circuit.go +++ b/htlcswitch/circuit.go @@ -38,9 +38,9 @@ type paymentCircuit struct { // request back. Dest lnwire.ShortChannelID - // Obfuscator is used to re-encrypt the onion failure before sending it - // back to the originator of the payment. - Obfuscator Obfuscator + // ErrorEncrypter is used to re-encrypt the onion failure before + // sending it back to the originator of the payment. + ErrorEncrypter ErrorEncrypter // RefCount is used to count the circuits with the same circuit key. RefCount int @@ -48,13 +48,14 @@ type paymentCircuit struct { // newPaymentCircuit creates new payment circuit instance. func newPaymentCircuit(src, dest lnwire.ShortChannelID, key circuitKey, - obfuscator Obfuscator) *paymentCircuit { + e ErrorEncrypter) *paymentCircuit { + return &paymentCircuit{ - Src: src, - Dest: dest, - PaymentHash: key, - RefCount: 1, - Obfuscator: obfuscator, + Src: src, + Dest: dest, + PaymentHash: key, + RefCount: 1, + ErrorEncrypter: e, } } diff --git a/htlcswitch/failure.go b/htlcswitch/failure.go index 6d776e7f..6a2d051a 100644 --- a/htlcswitch/failure.go +++ b/htlcswitch/failure.go @@ -5,6 +5,7 @@ import ( "github.com/lightningnetwork/lightning-onion" "github.com/lightningnetwork/lnd/lnwire" + "github.com/roasbeef/btcd/btcec" ) // ForwardingError wraps an lnwire.FailureMessage in a struct that also @@ -17,45 +18,48 @@ type ForwardingError struct { lnwire.FailureMessage } + +// ErrorDecrypter is an interface that is used to decrypt the onion encrypted // failure reason an extra out a well formed error. -type Deobfuscator interface { - // Deobfuscate peels off each layer of onion encryption from the first +type ErrorDecrypter interface { + // DecryptError peels off each layer of onion encryption from the first // hop, to the source of the error. A fully populated - // lnwire.FailureMessage is returned. - Deobfuscate(lnwire.OpaqueReason) (lnwire.FailureMessage, error) + // lnwire.FailureMessage is returned along with the source of the + // error. + DecryptError(lnwire.OpaqueReason) (*ForwardingError, error) } -// Obfuscator is an interface that is used to encrypt HTLC related errors at -// the source of the error, and also at each intermediate hop all the way back -// to the source of the payment. -type Obfuscator interface { - // InitialObfuscate is used to convert the failure into opaque - // reason. - - // InitialObfuscate transforms a concrete failure message into an +// ErrorEncrypter is an interface that is used to encrypt HTLC related errors +// at the source of the error, and also at each intermediate hop all the way +// back to the source of the payment. +type ErrorEncrypter interface { + // EncryptFirstHop transforms a concrete failure message into an // encrypted opaque failure reason. This method will be used at the - // source that the error occurs. It differs from BackwardObfuscate + // source that the error occurs. It differs from IntermediateEncrypt // slightly, in that it computes a proper MAC over the error. - InitialObfuscate(lnwire.FailureMessage) (lnwire.OpaqueReason, error) + EncryptFirstHop(lnwire.FailureMessage) (lnwire.OpaqueReason, error) - // BackwardObfuscate wraps an already encrypted opaque reason error in - // an additional layer of onion encryption. This process repeats until - // the error arrives at the source of the payment. - BackwardObfuscate(lnwire.OpaqueReason) lnwire.OpaqueReason + // IntermediateEncrypt wraps an already encrypted opaque reason error + // in an additional layer of onion encryption. This process repeats + // until the error arrives at the source of the payment. + IntermediateEncrypt(lnwire.OpaqueReason) lnwire.OpaqueReason } -// FailureObfuscator is used to obfuscate the onion failure. -type FailureObfuscator struct { - *sphinx.OnionObfuscator +// SphinxErrorEncrypter is a concrete implementation of both the ErrorEncrypter +// interface backed by an implementation of the Sphinx packet format. As a +// result, all errors handled are themselves wrapped in layers of onion +// encryption and must be treated as such accordingly. +type SphinxErrorEncrypter struct { + *sphinx.OnionErrorEncrypter } -// InitialObfuscate transforms a concrete failure message into an encrypted +// EncryptFirstHop transforms a concrete failure message into an encrypted // opaque failure reason. This method will be used at the source that the error // occurs. It differs from BackwardObfuscate slightly, in that it computes a // proper MAC over the error. // -// NOTE: Part of the Obfuscator interface. -func (o *FailureObfuscator) InitialObfuscate(failure lnwire.FailureMessage) (lnwire.OpaqueReason, error) { +// NOTE: Part of the ErrorEncrypter interface. +func (o *SphinxErrorEncrypter) EncryptFirstHop(failure lnwire.FailureMessage) (lnwire.OpaqueReason, error) { var b bytes.Buffer if err := lnwire.EncodeFailure(&b, failure, 0); err != nil { return nil, err @@ -63,46 +67,54 @@ func (o *FailureObfuscator) InitialObfuscate(failure lnwire.FailureMessage) (lnw // We pass a true as the first parameter to indicate that a MAC should // be added. - return o.OnionObfuscator.Obfuscate(true, b.Bytes()), nil + return o.EncryptError(true, b.Bytes()), nil } -// BackwardObfuscate wraps an already encrypted opaque reason error in an +// IntermediateEncrypt wraps an already encrypted opaque reason error in an // additional layer of onion encryption. This process repeats until the error // arrives at the source of the payment. We re-encrypt the message on the // backwards path to ensure that the error is indistinguishable from any other // error seen. // -// NOTE: Part of the Obfuscator interface. -func (o *FailureObfuscator) BackwardObfuscate(reason lnwire.OpaqueReason) lnwire.OpaqueReason { - return o.OnionObfuscator.Obfuscate(false, reason) +// NOTE: Part of the ErrorEncrypter interface. +func (s *SphinxErrorEncrypter) IntermediateEncrypt(reason lnwire.OpaqueReason) lnwire.OpaqueReason { + return s.EncryptError(false, reason) } -// A compile time check to ensure FailureObfuscator implements the Obfuscator -// interface. -var _ Obfuscator = (*FailureObfuscator)(nil) +// A compile time check to ensure SphinxErrorEncrypter implements the +// ErrorEncrypter interface. +var _ ErrorEncrypter = (*SphinxErrorEncrypter)(nil) -// FailureDeobfuscator wraps the sphinx data obfuscator and adds awareness of -// the lnwire onion failure messages to it. -type FailureDeobfuscator struct { - *sphinx.OnionDeobfuscator +// SphinxErrorDecrypter wraps the sphinx data SphinxErrorDecrypter and maps the +// returned errors to concrete lnwire.FailureMessage instances. +type SphinxErrorDecrypter struct { + *sphinx.OnionErrorDecrypter } -// Deobfuscate peels off each layer of onion encryption from the first hop, to -// the source of the error. A fully populated lnwire.FailureMessage is -// returned. +// DecryptError peels off each layer of onion encryption from the first hop, to +// the source of the error. A fully populated lnwire.FailureMessage is returned +// along with the source of the error. // -// NOTE: Part of the Obfuscator interface. -func (o *FailureDeobfuscator) Deobfuscate(reason lnwire.OpaqueReason) (lnwire.FailureMessage, error) { +// NOTE: Part of the ErrorDecrypter interface. +func (s *SphinxErrorDecrypter) DecryptError(reason lnwire.OpaqueReason) (*ForwardingError, error) { - _, failureData, err := o.OnionDeobfuscator.Deobfuscate(reason) + source, failureData, err := s.OnionErrorDecrypter.DecryptError(reason) if err != nil { return nil, err } r := bytes.NewReader(failureData) - return lnwire.DecodeFailure(r, 0) + failureMsg, err := lnwire.DecodeFailure(r, 0) + if err != nil { + return nil, err + } + + return &ForwardingError{ + ErrorSource: source, + FailureMessage: failureMsg, + }, nil } -// A compile time check to ensure FailureDeobfuscator implements the -// Deobfuscator interface. -var _ Deobfuscator = (*FailureDeobfuscator)(nil) +// A compile time check to ensure ErrorDecrypter implements the Deobfuscator +// interface. +var _ ErrorDecrypter = (*SphinxErrorDecrypter)(nil) diff --git a/htlcswitch/iterator.go b/htlcswitch/iterator.go index f34507a1..6be6a19a 100644 --- a/htlcswitch/iterator.go +++ b/htlcswitch/iterator.go @@ -198,11 +198,13 @@ func (p *OnionProcessor) DecodeHopIterator(r io.Reader, rHash []byte) (HopIterat }, lnwire.CodeNone } -// DecodeOnionObfuscator takes an io.Reader which should contain the onion -// packet as original received by a forwarding node and creates an Obfuscator -// instance using the derived shared secret. In the case that en error occurs, -// a lnwire failure code detailing the parsing failure will be returned. -func (p *OnionProcessor) DecodeOnionObfuscator(r io.Reader) (Obfuscator, lnwire.FailCode) { +// ExtractErrorEncrypter takes an io.Reader which should contain the onion +// packet as original received by a forwarding node and creates an +// ErrorEncrypter instance using the derived shared secret. In the case that en +// error occurs, a lnwire failure code detailing the parsing failure will be +// returned. +func (p *OnionProcessor) ExtractErrorEncrypter(r io.Reader) (ErrorEncrypter, lnwire.FailCode) { + onionPkt := &sphinx.OnionPacket{} if err := onionPkt.Decode(r); err != nil { switch err { @@ -215,7 +217,7 @@ func (p *OnionProcessor) DecodeOnionObfuscator(r io.Reader) (Obfuscator, lnwire. } } - onionObfuscator, err := sphinx.NewOnionObfuscator(p.router, + onionObfuscator, err := sphinx.NewOnionErrorEncrypter(p.router, onionPkt.EphemeralKey) if err != nil { switch err { @@ -230,7 +232,7 @@ func (p *OnionProcessor) DecodeOnionObfuscator(r io.Reader) (Obfuscator, lnwire. } } - return &FailureObfuscator{ - OnionObfuscator: onionObfuscator, + return &SphinxErrorEncrypter{ + OnionErrorEncrypter: onionObfuscator, }, lnwire.CodeNone } diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 7b937804..e75e688d 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -103,7 +103,7 @@ type ChannelLinkConfig struct { // DecodeOnionObfuscator function is responsible for decoding HTLC // Sphinx onion blob, and creating onion failure obfuscator. - DecodeOnionObfuscator func(r io.Reader) (Obfuscator, lnwire.FailCode) + DecodeOnionObfuscator func(r io.Reader) (ErrorEncrypter, lnwire.FailCode) // GetLastChannelUpdate retrieves the latest routing policy for this // particular channel. This will be used to provide payment senders our @@ -570,7 +570,7 @@ func (l *channelLink) handleDownStreamPkt(pkt *htlcPacket, isReProcess bool) { // Otherwise, we'll send back a proper failure // message. default: - reason, err = obfuscator.InitialObfuscate(failure) + reason, err = obfuscator.EncryptFirstHop(failure) if err != nil { log.Errorf("unable to obfuscate error: %v", err) return @@ -1461,8 +1461,9 @@ func (l *channelLink) processLockedInHtlcs( // sendHTLCError functions cancels HTLC and send cancel message back to the // peer from which HTLC was received. func (l *channelLink) sendHTLCError(rHash [32]byte, failure lnwire.FailureMessage, - obfuscator Obfuscator) { - reason, err := obfuscator.InitialObfuscate(failure) + e ErrorEncrypter) { + + reason, err := e.EncryptFirstHop(failure) if err != nil { log.Errorf("unable to obfuscate error: %v", err) return diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index e6758f4d..2c37c480 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -153,11 +153,11 @@ var _ HopIterator = (*mockHopIterator)(nil) // encodes the failure and do not makes any onion obfuscation. type mockObfuscator struct{} -func newMockObfuscator() Obfuscator { +func newMockObfuscator() ErrorEncrypter { return &mockObfuscator{} } -func (o *mockObfuscator) InitialObfuscate(failure lnwire.FailureMessage) ( +func (o *mockObfuscator) EncryptFirstHop(failure lnwire.FailureMessage) ( lnwire.OpaqueReason, error) { var b bytes.Buffer @@ -167,7 +167,7 @@ func (o *mockObfuscator) InitialObfuscate(failure lnwire.FailureMessage) ( return b.Bytes(), nil } -func (o *mockObfuscator) BackwardObfuscate(reason lnwire.OpaqueReason) lnwire.OpaqueReason { +func (o *mockObfuscator) IntermediateEncrypt(reason lnwire.OpaqueReason) lnwire.OpaqueReason { return reason } @@ -176,21 +176,24 @@ func (o *mockObfuscator) BackwardObfuscate(reason lnwire.OpaqueReason) lnwire.Op // only decodes the failure do not makes any onion obfuscation. type mockDeobfuscator struct{} -func newMockDeobfuscator() Deobfuscator { +func newMockDeobfuscator() ErrorDecrypter { return &mockDeobfuscator{} } -func (o *mockDeobfuscator) Deobfuscate(reason lnwire.OpaqueReason) (lnwire.FailureMessage, - error) { +func (o *mockDeobfuscator) DecryptError(reason lnwire.OpaqueReason) (*ForwardingError, error) { + r := bytes.NewReader(reason) failure, err := lnwire.DecodeFailure(r, 0) if err != nil { return nil, err } - return failure, nil + + return &ForwardingError{ + FailureMessage: failure, + }, nil } -var _ Deobfuscator = (*mockDeobfuscator)(nil) +var _ ErrorDecrypter = (*mockDeobfuscator)(nil) // mockIteratorDecoder test version of hop iterator decoder which decodes the // encoded array of hops. diff --git a/htlcswitch/packet.go b/htlcswitch/packet.go index 8456367a..b9cd8365 100644 --- a/htlcswitch/packet.go +++ b/htlcswitch/packet.go @@ -35,10 +35,7 @@ type htlcPacket struct { // obfuscator contains the necessary state to allow the switch to wrap // any forwarded errors in an additional layer of encryption. - // - // TODO(andrew.shvv) revisit after refactoring the way of returning - // errors inside the htlcswitch packet. - obfuscator Obfuscator + obfuscator ErrorEncrypter // isObfuscated is set to true if an error occurs as soon as the switch // forwards a packet to the link. If so, and this is an error packet, @@ -61,13 +58,13 @@ func newInitPacket(destNode [33]byte, htlc *lnwire.UpdateAddHTLC) *htlcPacket { // newAddPacket creates htlc switch add packet which encapsulates the add htlc // request and additional information for proper forwarding over htlc switch. func newAddPacket(src, dest lnwire.ShortChannelID, - htlc *lnwire.UpdateAddHTLC, obfuscator Obfuscator) *htlcPacket { + htlc *lnwire.UpdateAddHTLC, e ErrorEncrypter) *htlcPacket { return &htlcPacket{ dest: dest, src: src, htlc: htlc, - obfuscator: obfuscator, + obfuscator: e, } } diff --git a/htlcswitch/switch.go b/htlcswitch/switch.go index b6881f44..8f45d263 100644 --- a/htlcswitch/switch.go +++ b/htlcswitch/switch.go @@ -40,7 +40,7 @@ type pendingPayment struct { // deobfuscator is an serializable entity which is used if we received // an error, it deobfuscates the onion failure blob, and extracts the // exact error from it. - deobfuscator Deobfuscator + deobfuscator ErrorDecrypter } // plexPacket encapsulates switch packet and adds error channel to receive @@ -170,7 +170,7 @@ func New(cfg Config) *Switch { // SendHTLC is used by other subsystems which aren't belong to htlc switch // package in order to send the htlc update. func (s *Switch) SendHTLC(nextNode [33]byte, htlc *lnwire.UpdateAddHTLC, - deobfuscator Deobfuscator) ([sha256.Size]byte, error) { + deobfuscator ErrorDecrypter) ([sha256.Size]byte, error) { // Create payment and add to the map of payment in order later to be // able to retrieve it and return response to the user. @@ -391,7 +391,7 @@ func (s *Switch) handleLocalDispatch(payment *pendingPayment, packet *htlcPacket // We'll attempt to fully decrypt the onion encrypted error. If // we're unable to then we'll bail early. - failure, err := payment.deobfuscator.Deobfuscate(htlc.Reason) + failure, err := payment.deobfuscator.DecryptError(htlc.Reason) if err != nil { // TODO(roasbeef): can happen in case of local error in // link pkt handling @@ -442,7 +442,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error { // than we should notify this link that some error // occurred. failure := lnwire.FailUnknownNextPeer{} - reason, err := packet.obfuscator.InitialObfuscate(failure) + reason, err := packet.obfuscator.EncryptFirstHop(failure) if err != nil { err := errors.Errorf("unable to obfuscate "+ "error: %v", err) @@ -482,7 +482,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error { // channel link than we should notify this // link that some error occurred. failure := lnwire.NewTemporaryChannelFailure(nil) - reason, err := packet.obfuscator.InitialObfuscate(failure) + reason, err := packet.obfuscator.EncryptFirstHop(failure) if err != nil { err := errors.Errorf("unable to obfuscate "+ "error: %v", err) @@ -516,7 +516,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error { packet.obfuscator, )); err != nil { failure := lnwire.NewTemporaryChannelFailure(nil) - reason, err := packet.obfuscator.InitialObfuscate(failure) + reason, err := packet.obfuscator.EncryptFirstHop(failure) if err != nil { err := errors.Errorf("unable to obfuscate "+ "error: %v", err) @@ -558,7 +558,7 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error { // If this is failure than we need to obfuscate the error. if htlc, ok := htlc.(*lnwire.UpdateFailHTLC); ok && !packet.isObfuscated { - htlc.Reason = circuit.Obfuscator.BackwardObfuscate( + htlc.Reason = circuit.ErrorEncrypter.IntermediateEncrypt( htlc.Reason, ) } diff --git a/htlcswitch/switch_test.go b/htlcswitch/switch_test.go index 0b0c20fc..11b39942 100644 --- a/htlcswitch/switch_test.go +++ b/htlcswitch/switch_test.go @@ -352,7 +352,7 @@ func TestSwitchSendPayment(t *testing.T) { // back. This request should be forwarded back to alice channel link. obfuscator := newMockObfuscator() failure := lnwire.FailIncorrectPaymentAmount{} - reason, err := obfuscator.InitialObfuscate(failure) + reason, err := obfuscator.EncryptFirstHop(failure) if err != nil { t.Fatalf("unable obfuscate failure: %v", err) } diff --git a/htlcswitch/test_utils.go b/htlcswitch/test_utils.go index 6a10f5ea..99a687dd 100644 --- a/htlcswitch/test_utils.go +++ b/htlcswitch/test_utils.go @@ -556,7 +556,7 @@ func newThreeHopNetwork(t *testing.T, aliceToBob, Peer: bobServer, Switch: aliceServer.htlcSwitch, DecodeHopIterator: decoder.DecodeHopIterator, - DecodeOnionObfuscator: func(io.Reader) (Obfuscator, + DecodeOnionObfuscator: func(io.Reader) (ErrorEncrypter, lnwire.FailCode) { return obfuscator, lnwire.CodeNone }, @@ -577,7 +577,7 @@ func newThreeHopNetwork(t *testing.T, aliceToBob, Peer: aliceServer, Switch: bobServer.htlcSwitch, DecodeHopIterator: decoder.DecodeHopIterator, - DecodeOnionObfuscator: func(io.Reader) (Obfuscator, + DecodeOnionObfuscator: func(io.Reader) (ErrorEncrypter, lnwire.FailCode) { return obfuscator, lnwire.CodeNone }, @@ -598,7 +598,7 @@ func newThreeHopNetwork(t *testing.T, aliceToBob, Peer: carolServer, Switch: bobServer.htlcSwitch, DecodeHopIterator: decoder.DecodeHopIterator, - DecodeOnionObfuscator: func(io.Reader) (Obfuscator, + DecodeOnionObfuscator: func(io.Reader) (ErrorEncrypter, lnwire.FailCode) { return obfuscator, lnwire.CodeNone }, @@ -619,7 +619,7 @@ func newThreeHopNetwork(t *testing.T, aliceToBob, Peer: bobServer, Switch: carolServer.htlcSwitch, DecodeHopIterator: decoder.DecodeHopIterator, - DecodeOnionObfuscator: func(io.Reader) (Obfuscator, + DecodeOnionObfuscator: func(io.Reader) (ErrorEncrypter, lnwire.FailCode) { return obfuscator, lnwire.CodeNone },