Skip to content

Commit 3db9187

Browse files
committed
zpay32: validate mandatory fields (p, h, s, n) and lengths
Decode function now fail the payment if mandatory field (`p`, `h`, `s`, `n`) does not have the correct length (52, 52, 52, 53). Enforced payment secret field and correct lenght in the validateInvoice function.
1 parent 1a30ac2 commit 3db9187

File tree

2 files changed

+34
-7
lines changed

2 files changed

+34
-7
lines changed

zpay32/decode.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,9 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
301301
}
302302

303303
invoice.PaymentHash, err = parse32Bytes(base32Data)
304+
if err != nil {
305+
return fmt.Errorf("payment hash: %w", err)
306+
}
304307

305308
case fieldTypeS:
306309
if invoice.PaymentAddr.IsSome() {
@@ -311,7 +314,7 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
311314

312315
addr, err := parse32Bytes(base32Data)
313316
if err != nil {
314-
return err
317+
return fmt.Errorf("payment secret: %w", err)
315318
}
316319
if addr != nil {
317320
invoice.PaymentAddr = fn.Some(*addr)
@@ -343,6 +346,9 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
343346
}
344347

345348
invoice.Destination, err = parseDestination(base32Data)
349+
if err != nil {
350+
return fmt.Errorf("destination id: %w", err)
351+
}
346352

347353
case fieldTypeH:
348354
if invoice.DescriptionHash != nil {
@@ -352,6 +358,9 @@ func parseTaggedFields(invoice *Invoice, fields []byte, net *chaincfg.Params) er
352358
}
353359

354360
invoice.DescriptionHash, err = parse32Bytes(base32Data)
361+
if err != nil {
362+
return fmt.Errorf("description hash: %w", err)
363+
}
355364

356365
case fieldTypeX:
357366
if invoice.expiry != nil {
@@ -446,10 +455,10 @@ func parseFieldDataLength(data []byte) (uint16, error) {
446455
func parse32Bytes(data []byte) (*[32]byte, error) {
447456
var paymentHash [32]byte
448457

449-
// As BOLT-11 states, a reader must skip over the 32-byte fields if
450-
// it does not have a length of 52, so avoid returning an error.
458+
// As BOLT-11 states, a reader must fail over the 32-byte fields if
459+
// it does not have a length of 52.
451460
if len(data) != hashBase32Len {
452-
return nil, nil
461+
return nil, ErrInvalidFieldLength
453462
}
454463

455464
hash, err := bech32.ConvertBits(data, 5, 8, false)
@@ -488,10 +497,10 @@ func parseMetadata(data []byte) ([]byte, error) {
488497
// parseDestination converts the data (encoded in base32) into a 33-byte public
489498
// key of the payee node.
490499
func parseDestination(data []byte) (*btcec.PublicKey, error) {
491-
// As BOLT-11 states, a reader must skip over the destination field
492-
// if it does not have a length of 53, so avoid returning an error.
500+
// As BOLT-11 states, a reader must fail over the destination field
501+
// if it does not have a length of 53.
493502
if len(data) != pubKeyBase32Len {
494-
return nil, nil
503+
return nil, ErrInvalidFieldLength
495504
}
496505

497506
base256Data, err := bech32.ConvertBits(data, 5, 8, false)

zpay32/invoice.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,24 @@ func validateInvoice(invoice *Invoice) error {
381381
return fmt.Errorf("no payment hash found")
382382
}
383383

384+
// The invoice must include a payment secret.
385+
// If blinded paths are used, a payment secret is not required because
386+
// the PathID in the final hop serves the same purpose.
387+
if len(invoice.BlindedPaymentPaths) == 0 {
388+
payAddr, err := invoice.PaymentAddr.UnwrapOrErr(
389+
fmt.Errorf("payment secret not found"),
390+
)
391+
if err != nil {
392+
return err
393+
}
394+
if len(payAddr) != 32 {
395+
return fmt.Errorf(
396+
"unsupported payment secret length: %d",
397+
len(payAddr),
398+
)
399+
}
400+
}
401+
384402
if len(invoice.RouteHints) != 0 &&
385403
len(invoice.BlindedPaymentPaths) != 0 {
386404

0 commit comments

Comments
 (0)