Merge pull request #3505 from Crypt-iQ/invoice-param-checks-0913

zpay32: check route+hop hints while decoding
This commit is contained in:
Wilmer Paulino 2019-09-24 15:22:30 -07:00 committed by GitHub
commit 811c2df75a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 15 deletions

@ -3,6 +3,7 @@ package zpay32
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"strings"
"time"
@ -71,12 +72,20 @@ const (
// fieldType9 contains one or more bytes for signaling features
// supported or required by the receiver.
fieldType9 = 5
// maxInvoiceLength is the maximum total length an invoice can have.
// This is chosen to be the maximum number of bytes that can fit into a
// single QR code: https://en.wikipedia.org/wiki/QR_code#Storage
maxInvoiceLength = 7089
)
var (
// InvoiceFeatures holds the set of all known feature bits that are
// exposed as BOLT 11 features.
InvoiceFeatures = map[lnwire.FeatureBit]string{}
// ErrInvoiceTooLarge is returned when an invoice exceeds maxInvoiceLength.
ErrInvoiceTooLarge = errors.New("invoice is too large")
)
// MessageSigner is passed to the Encode method to provide a signature
@ -263,6 +272,12 @@ func NewInvoice(net *chaincfg.Params, paymentHash [32]byte,
func Decode(invoice string, net *chaincfg.Params) (*Invoice, error) {
decodedInvoice := Invoice{}
// Before bech32 decoding the invoice, make sure that it is not too large.
// This is done as an anti-DoS measure since bech32 decoding is expensive.
if len(invoice) > maxInvoiceLength {
return nil, ErrInvoiceTooLarge
}
// Decode the invoice using the modified bech32 decoder.
hrp, data, err := decodeBech32(invoice)
if err != nil {
@ -467,6 +482,12 @@ func (invoice *Invoice) Encode(signer MessageSigner) (string, error) {
return "", err
}
// Before returning, check that the bech32 encoded string is not greater
// than our largest supported invoice size.
if len(b32) > maxInvoiceLength {
return "", ErrInvoiceTooLarge
}
return b32, nil
}
@ -518,21 +539,6 @@ func validateInvoice(invoice *Invoice) error {
return fmt.Errorf("neither description nor description hash set")
}
// We'll restrict invoices to include up to 20 different private route
// hints. We do this to avoid overly large invoices.
if len(invoice.RouteHints) > 20 {
return fmt.Errorf("too many private routes: %d",
len(invoice.RouteHints))
}
// Each route hint can have at most 20 hops.
for i, routeHint := range invoice.RouteHints {
if len(routeHint) > 20 {
return fmt.Errorf("route hint %d has too many extra "+
"hops: %d", i, len(routeHint))
}
}
// Check that we support the field lengths.
if len(invoice.PaymentHash) != 32 {
return fmt.Errorf("unsupported payment hash length: %d",
@ -870,6 +876,7 @@ func parseRouteHint(data []byte) ([]HopHint, error) {
return nil, err
}
// Check that base256Data is a multiple of hopHintLen.
if len(base256Data)%hopHintLen != 0 {
return nil, fmt.Errorf("expected length multiple of %d bytes, "+
"got %d", hopHintLen, len(base256Data))

File diff suppressed because one or more lines are too long