-
Notifications
You must be signed in to change notification settings - Fork 2.2k
htlcswitch+lnwire: invalid onion payload #3470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2f8021d
3c91c3a
0fc506e
3455f79
d08e8dd
e85aaa4
937b781
cba523a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,21 +5,56 @@ import ( | |
| "fmt" | ||
| "io" | ||
|
|
||
| "github.com/lightningnetwork/lightning-onion" | ||
| sphinx "github.com/lightningnetwork/lightning-onion" | ||
| "github.com/lightningnetwork/lnd/lnwire" | ||
| "github.com/lightningnetwork/lnd/record" | ||
| "github.com/lightningnetwork/lnd/tlv" | ||
| ) | ||
|
|
||
| // PayloadViolation is an enum encapsulating the possible invalid payload | ||
| // violations that can occur when processing or validating a payload. | ||
| type PayloadViolation byte | ||
|
|
||
| const ( | ||
| // OmittedViolation indicates that a type was expected to be found the | ||
| // payload but was absent. | ||
| OmittedViolation PayloadViolation = iota | ||
|
|
||
| // IncludedViolation indicates that a type was expected to be omitted | ||
| // from the payload but was present. | ||
| IncludedViolation | ||
|
|
||
| // RequiredViolation indicates that an unknown even type was found in | ||
| // the payload that we could not process. | ||
| RequiredViolation | ||
| ) | ||
cfromknecht marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // String returns a human-readable description of the violation as a verb. | ||
| func (v PayloadViolation) String() string { | ||
| switch v { | ||
| case OmittedViolation: | ||
| return "omitted" | ||
|
|
||
| case IncludedViolation: | ||
| return "included" | ||
|
|
||
| case RequiredViolation: | ||
| return "required" | ||
|
|
||
| default: | ||
| return "unknown violation" | ||
| } | ||
| } | ||
|
|
||
| // ErrInvalidPayload is an error returned when a parsed onion payload either | ||
| // included or omitted incorrect records for a particular hop type. | ||
| type ErrInvalidPayload struct { | ||
| // Type the record's type that cause the violation. | ||
| Type tlv.Type | ||
|
|
||
| // Ommitted if true, signals that the sender did not include the record. | ||
| // Otherwise, the sender included the record when it shouldn't have. | ||
| Omitted bool | ||
| // Violation is an enum indicating the type of violation detected in | ||
| // processing Type. | ||
| Violation PayloadViolation | ||
|
|
||
| // FinalHop if true, indicates that the violation is for the final hop | ||
| // in the route (identified by next hop id), otherwise the violation is | ||
|
|
@@ -33,13 +68,9 @@ func (e ErrInvalidPayload) Error() string { | |
| if e.FinalHop { | ||
| hopType = "final" | ||
| } | ||
| violation := "included" | ||
| if e.Omitted { | ||
| violation = "omitted" | ||
| } | ||
|
|
||
| return fmt.Sprintf("onion payload for %s hop %s record with type %d", | ||
| hopType, violation, e.Type) | ||
| return fmt.Sprintf("onion payload for %s hop %v record with type %d", | ||
| hopType, e.Violation, e.Type) | ||
| } | ||
|
|
||
| // Payload encapsulates all information delivered to a hop in an onion payload. | ||
|
|
@@ -87,13 +118,34 @@ func NewPayloadFromReader(r io.Reader) (*Payload, error) { | |
|
|
||
| parsedTypes, err := tlvStream.DecodeWithParsedTypes(r) | ||
| if err != nil { | ||
| // Promote any required type failures into ErrInvalidPayload. | ||
| if e, required := err.(tlv.ErrUnknownRequiredType); required { | ||
| // If the parser returned an unknown required type | ||
| // failure, we'll first check that the payload is | ||
| // properly formed according to our known set of | ||
| // constraints. If an error is discovered, this | ||
| // overrides the required type failure. | ||
| nextHop := lnwire.NewShortChanIDFromInt(cid) | ||
cfromknecht marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| err = ValidateParsedPayloadTypes(parsedTypes, nextHop) | ||
| if err != nil { | ||
| return nil, err | ||
|
||
| } | ||
|
|
||
| // Otherwise the known constraints were applied | ||
| // successfully, report the invalid type failure | ||
| // returned by the parser. | ||
| return nil, ErrInvalidPayload{ | ||
| Type: tlv.Type(e), | ||
| Violation: RequiredViolation, | ||
| FinalHop: nextHop == Exit, | ||
| } | ||
| } | ||
| return nil, err | ||
| } | ||
|
|
||
| nextHop := lnwire.NewShortChanIDFromInt(cid) | ||
|
|
||
| // Validate whether the sender properly included or omitted tlv records | ||
| // in accordance with BOLT 04. | ||
| nextHop := lnwire.NewShortChanIDFromInt(cid) | ||
| err = ValidateParsedPayloadTypes(parsedTypes, nextHop) | ||
| if err != nil { | ||
| return nil, err | ||
|
|
@@ -133,27 +185,27 @@ func ValidateParsedPayloadTypes(parsedTypes tlv.TypeSet, | |
| // All hops must include an amount to forward. | ||
| case !hasAmt: | ||
| return ErrInvalidPayload{ | ||
| Type: record.AmtOnionType, | ||
| Omitted: true, | ||
| FinalHop: isFinalHop, | ||
| Type: record.AmtOnionType, | ||
| Violation: OmittedViolation, | ||
| FinalHop: isFinalHop, | ||
| } | ||
|
|
||
| // All hops must include a cltv expiry. | ||
| case !hasLockTime: | ||
| return ErrInvalidPayload{ | ||
| Type: record.LockTimeOnionType, | ||
| Omitted: true, | ||
| FinalHop: isFinalHop, | ||
| Type: record.LockTimeOnionType, | ||
| Violation: OmittedViolation, | ||
| FinalHop: isFinalHop, | ||
| } | ||
|
|
||
| // The exit hop should omit the next hop id. If nextHop != Exit, the | ||
| // sender must have included a record, so we don't need to test for its | ||
| // inclusion at intermediate hops directly. | ||
| case isFinalHop && hasNextHop: | ||
| return ErrInvalidPayload{ | ||
| Type: record.NextHopOnionType, | ||
| Omitted: false, | ||
| FinalHop: true, | ||
| Type: record.NextHopOnionType, | ||
| Violation: IncludedViolation, | ||
| FinalHop: true, | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Required and Omitted sound like the same thing. Is the real difference that one happens during tlv decode and the other one on the higher level? If that is indeed the case, maybe the names should reflect that. Or just have one of the two.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no they are different, required means that the sender required us to understand a record, but we don't. omitted means that the receiver expected a field that the sender didn't include.