Skip to content

BOLT12 Offer Validation: Empty TLV Collections Bypass Required Field Checks #799

@erickcestari

Description

@erickcestari

Discovered via differential fuzzing using bitcoinfuzz against rust-lightning, lightning-kmp, and CLN implementations.

The BOLT12 offer parser incorrectly accepts invalid offers by creating empty collections for TLV fields instead of rejecting malformed input, causing validation to pass when it should fail. This creates a compatibility issue where lightning-kmp accepts offers that other implementations correctly reject.

Offer to Reproduce

lno1qgqpqqq

Expected: Validation failure (matches rust-lightning and CLN behavior)
Actual: Creates OfferChains(chains=[]) and OfferPaths(paths=[]), bypassing validation

Root Cause

The offer string lno1qgqpqqq decodes to TLV bytes [02 00 10 00]:

  • Type 2 (OfferChains) with length 0
  • Type 16 (OfferPaths) with length 0

TLV readers create empty collections instead of failing:

override fun read(input: Input): OfferPaths {
    val paths = ArrayList<ContactInfo.BlindedPath>()
    while (input.availableBytes > 0) { // Never executes when length=0
        paths.add(readPath(input))
    }
    return OfferPaths(paths) // Returns empty list instead of failing
}

This bypasses the validation check:

if (records.get<OfferIssuerId>() == null && records.get<OfferPaths>() == null)
    return Left(MissingRequiredTlv(22))

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions