From 72260adddb30c4e9c18a14e37f6abf5bcd365712 Mon Sep 17 00:00:00 2001 From: Carla Kirk-Cohen Date: Tue, 23 Apr 2024 12:33:04 -0400 Subject: [PATCH] htlcswitch: create error obfuscator with wrapped type for blinded Create our error encrypter with a wrapped type if we have a blinding point present. Doing this in the iterator allows us to track this information when we have both pieces of information available to us, compared to trying to handle this later down the line: - Downstream link on failure: we know that we've set a blinding point for out outgoing HTLC, but not whether we're introduction or not - Upstream link on failure: once the failure packet has been sent through the switch, we no longer know whether we were the introduction point (without looking it up / examining our payload again / propagating this information through the switch). --- htlcswitch/hop/iterator.go | 30 +++++++++++++--- htlcswitch/link.go | 70 +++++++++++++++++++++++++++----------- htlcswitch/mock.go | 2 +- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/htlcswitch/hop/iterator.go b/htlcswitch/hop/iterator.go index c387bae3a..a89f1b734 100644 --- a/htlcswitch/hop/iterator.go +++ b/htlcswitch/hop/iterator.go @@ -103,8 +103,8 @@ type Iterator interface { // ExtractErrorEncrypter returns the ErrorEncrypter needed for this hop, // along with a failure code to signal if the decoding was successful. - ExtractErrorEncrypter(ErrorEncrypterExtracter) (ErrorEncrypter, - lnwire.FailCode) + ExtractErrorEncrypter(extractor ErrorEncrypterExtracter, + introductionNode bool) (ErrorEncrypter, lnwire.FailCode) } // sphinxHopIterator is the Sphinx implementation of hop iterator which uses @@ -235,9 +235,31 @@ func (r *sphinxHopIterator) HopPayload() (*Payload, RouteRole, error) { // // NOTE: Part of the HopIterator interface. func (r *sphinxHopIterator) ExtractErrorEncrypter( - extracter ErrorEncrypterExtracter) (ErrorEncrypter, lnwire.FailCode) { + extracter ErrorEncrypterExtracter, introductionNode bool) ( + ErrorEncrypter, lnwire.FailCode) { - return extracter(r.ogPacket.EphemeralKey) + encrypter, errCode := extracter(r.ogPacket.EphemeralKey) + if errCode != lnwire.CodeNone { + return nil, errCode + } + + // If we're in a blinded path, wrap the error encrypter that we just + // derived in a "marker" type which we'll use to know what type of + // error we're handling. + switch { + case introductionNode: + return &IntroductionErrorEncrypter{ + ErrorEncrypter: encrypter, + }, errCode + + case r.blindingKit.UpdateAddBlinding.IsSome(): + return &RelayingErrorEncrypter{ + ErrorEncrypter: encrypter, + }, errCode + + default: + return encrypter, errCode + } } // BlindingProcessor is an interface that provides the cryptographic operations diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 5f250636d..b5cba787c 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -3262,7 +3262,7 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, // DecodeHopIterator function which process the Sphinx packet. chanIterator, failureCode := decodeResps[i].Result() if failureCode != lnwire.CodeNone { - // If we're unable to process the onion blob than we + // If we're unable to process the onion blob then we // should send the malformed htlc error to payment // sender. l.sendMalformedHTLCError(pd.HtlcIndex, failureCode, @@ -3273,27 +3273,9 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, continue } - // Retrieve onion obfuscator from onion blob in order to - // produce initial obfuscation of the onion failureCode. - obfuscator, failureCode := chanIterator.ExtractErrorEncrypter( - l.cfg.ExtractErrorEncrypter, - ) - if failureCode != lnwire.CodeNone { - // If we're unable to process the onion blob than we - // should send the malformed htlc error to payment - // sender. - l.sendMalformedHTLCError( - pd.HtlcIndex, failureCode, onionBlob[:], pd.SourceRef, - ) - - l.log.Errorf("unable to decode onion "+ - "obfuscator: %v", failureCode) - continue - } - heightNow := l.cfg.BestHeight() - pld, _, pldErr := chanIterator.HopPayload() + pld, routeRole, pldErr := chanIterator.HopPayload() if pldErr != nil { // If we're unable to process the onion payload, or we // received invalid onion payload failure, then we @@ -3308,6 +3290,33 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, failedType = uint64(e.Type) } + // If we couldn't parse the payload, make our best + // effort at creating an error encrypter that knows + // what blinding type we were, but if we couldn't + // parse the payload we have no way of knowing whether + // we were the introduction node or not. + // + //nolint:lll + obfuscator, failCode := chanIterator.ExtractErrorEncrypter( + l.cfg.ExtractErrorEncrypter, + // We need our route role here because we + // couldn't parse or validate the payload. + routeRole == hop.RouteRoleIntroduction, + ) + if failCode != lnwire.CodeNone { + l.log.Errorf("could not extract error "+ + "encrypter: %v", pldErr) + + // We can't process this htlc, send back + // malformed. + l.sendMalformedHTLCError( + pd.HtlcIndex, failureCode, + onionBlob[:], pd.SourceRef, + ) + + continue + } + // TODO: currently none of the test unit infrastructure // is setup to handle TLV payloads, so testing this // would require implementing a separate mock iterator @@ -3325,6 +3334,27 @@ func (l *channelLink) processRemoteAdds(fwdPkg *channeldb.FwdPkg, continue } + // Retrieve onion obfuscator from onion blob in order to + // produce initial obfuscation of the onion failureCode. + obfuscator, failureCode := chanIterator.ExtractErrorEncrypter( + l.cfg.ExtractErrorEncrypter, + routeRole == hop.RouteRoleIntroduction, + ) + if failureCode != lnwire.CodeNone { + // If we're unable to process the onion blob than we + // should send the malformed htlc error to payment + // sender. + l.sendMalformedHTLCError( + pd.HtlcIndex, failureCode, onionBlob[:], + pd.SourceRef, + ) + + l.log.Errorf("unable to decode onion "+ + "obfuscator: %v", failureCode) + + continue + } + fwdInfo := pld.ForwardingInfo() // Check whether the payload we've just processed uses our diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index 0dbefd45c..cd3e5026b 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -341,7 +341,7 @@ func (r *mockHopIterator) ExtraOnionBlob() []byte { } func (r *mockHopIterator) ExtractErrorEncrypter( - extracter hop.ErrorEncrypterExtracter) (hop.ErrorEncrypter, + extracter hop.ErrorEncrypterExtracter, _ bool) (hop.ErrorEncrypter, lnwire.FailCode) { return extracter(nil)