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)