Skip to content

Commit

Permalink
Improve misconfigured BIP353 detection
Browse files Browse the repository at this point in the history
The BIP353 resolver should now return proper errors when
DNSSEC is disabled or the payment instructions hosted on
the DNS is not valid. The Android app will output a more
relevant error message.
  • Loading branch information
dpad85 committed Jul 12, 2024
1 parent e88eb22 commit 7044826
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,8 @@ private fun ScanErrorView(
is Scan.BadRequestReason.InvalidLnurl -> stringResource(R.string.scan_error_lnurl_invalid)
is Scan.BadRequestReason.UnsupportedLnurl -> stringResource(R.string.scan_error_lnurl_unsupported)
is Scan.BadRequestReason.UnknownFormat -> stringResource(R.string.scan_error_invalid_generic)
is Scan.BadRequestReason.InvalidBip353 -> "invalid bip-353 response: ${reason.path}"
is Scan.BadRequestReason.Bip353InvalidOffer -> stringResource(id = R.string.scan_error_bip353_invalid_offer)
is Scan.BadRequestReason.Bip353NoDNSSEC -> stringResource(id = R.string.scan_error_bip353_dnssec)
}
Dialog(
onDismiss = onErrorDialogDismiss,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@
<string name="scan_error_lnurl_invalid">Error al procesar este enlace LNURL. Comprueba que sea válido.</string>
<string name="scan_error_lnurl_unsupported">Este tipo de LNURL aún no es compatible.</string>
<string name="scan_error_invalid_generic">Estos datos usan un formato desconocido por lo que no se pueden procesar.</string>
<string name="scan_error_bip353_invalid_offer">Esta dirección utiliza una oferta Bolt12 no válida.</string>
<string name="scan_error_bip353_dnssec">Esta dirección está alojada en un DNS no seguro. DNSSEC debe estar habilitado.</string>

<!-- validation -->

Expand Down
2 changes: 2 additions & 0 deletions phoenix-android/src/main/res/values-cs/important_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@
<string name="scan_error_lnurl_invalid">Tento LNURL odkaz se nepodařilo zpracovat. Ujistěte se, že je platný.</string>
<string name="scan_error_lnurl_unsupported">Tento typ LNURL zatím není podporován.</string>
<string name="scan_error_invalid_generic">Tato data používají neznámý formát a nelze je zpracovat.</string>
<string name="scan_error_bip353_invalid_offer">Tato adresa používá neplatnou nabídku Bolt12.</string>
<string name="scan_error_bip353_dnssec">Tato adresa je hostována na nezabezpečeném DNS. DNSSEC musí být povolen.</string>

<!-- receive: Tor warning -->

Expand Down
2 changes: 2 additions & 0 deletions phoenix-android/src/main/res/values-de/important_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@
<string name="scan_error_lnurl_invalid">Dieser LNURL-Link konnte nicht verarbeitet werden. Stellen Sie sicher, dass er gültig ist.</string>
<string name="scan_error_lnurl_unsupported">Dieser LNURL-Typ wird noch nicht unterstützt.</string>
<string name="scan_error_invalid_generic">Diese Daten haben ein unbekanntes Format und können nicht verarbeitet werden.</string>
<string name="scan_error_bip353_invalid_offer">Diese Adresse verwendet ein ungültiges Bolt12-Angebot.</string>
<string name="scan_error_bip353_dnssec">Diese Adresse wird über einen unsicheren DNS gehostet. DNSSEC muss aktiviert sein.</string>

<!-- validation -->

Expand Down
2 changes: 2 additions & 0 deletions phoenix-android/src/main/res/values-es/important_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@
<string name="scan_error_lnurl_invalid">No se ha podido procesar este enlace LNURL. Asegúrese de que es válido.</string>
<string name="scan_error_lnurl_unsupported">Este tipo de LNURL aún no se admite.</string>
<string name="scan_error_invalid_generic">Estos datos utilizan un formato desconocido y no pueden ser procesados.</string>
<string name="scan_error_bip353_invalid_offer">Esta dirección utiliza una oferta Bolt12 no válida.</string>
<string name="scan_error_bip353_dnssec">Esta dirección está alojada en un DNS no seguro. DNSSEC debe estar habilitado.</string>

<!-- validation -->

Expand Down
2 changes: 2 additions & 0 deletions phoenix-android/src/main/res/values-fr/important_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@
<string name="scan_error_lnurl_invalid">Ce lien LNURL n\'a pas pu être traité. Assurez-vous qu\'il soit valide.</string>
<string name="scan_error_lnurl_unsupported">Ce type de lien LNURL n\'est pas supporté.</string>
<string name="scan_error_invalid_generic">Ce contenu est mal formatté ou bien n\'est pas géré par Phoenix.</string>
<string name="scan_error_bip353_invalid_offer">Cette adresse utilise une offre Bolt12 invalide.</string>
<string name="scan_error_bip353_dnssec">Cette adresse est hébergée sur un DNS non sécurisé. DNSSEC doit être activé.</string>

<!-- validation -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@
<string name="scan_error_lnurl_invalid">Falha ao processar esse link LNURL. Verifique se ele é válido.</string>
<string name="scan_error_lnurl_unsupported">Este tipo de LNURL ainda não é suportado.</string>
<string name="scan_error_invalid_generic">Esses dados usam um formato desconhecido e não podem ser processados.</string>
<string name="scan_error_bip353_invalid_offer">Este endereço usa uma oferta Bolt12 inválida.</string>
<string name="scan_error_bip353_dnssec">Este endereço está hospedado em um DNS não seguro. O DNSSEC deve estar ativado.</string>

<!-- validation -->

Expand Down
2 changes: 2 additions & 0 deletions phoenix-android/src/main/res/values-sk/important_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@
<string name="scan_error_lnurl_invalid">Tento LNURL odkaz sa nepodarilo spracovať. Uistite sa, že je platný.</string>
<string name="scan_error_lnurl_unsupported">Tento typ LNURL zatiaľ nie je podporovaný.</string>
<string name="scan_error_invalid_generic">Tieto údaje používajú neznámy formát a nemožno ich spracovať.</string>
<string name="scan_error_bip353_invalid_offer">Táto adresa používa neplatnú ponuku Bolt12.</string>
<string name="scan_error_bip353_dnssec">Táto adresa je umiestnená na nezabezpečenom DNS. DNSSEC musí byť povolený.</string>

<!-- validation -->

Expand Down
2 changes: 2 additions & 0 deletions phoenix-android/src/main/res/values-vi/important_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@
<string name="scan_error_lnurl_invalid">Không thể xử lý liên kết LNURL này. Hãy đảm bảo liên kết này có hiệu lực.</string>
<string name="scan_error_lnurl_unsupported">Loại LNURL này chưa được hỗ trợ.</string>
<string name="scan_error_invalid_generic">Dữ liệu này sử dụng định dạng không xác định và không thể xử lý được.</string>
<string name="scan_error_bip353_invalid_offer">Địa chỉ này sử dụng ưu đãi Bolt12 không hợp lệ.</string>
<string name="scan_error_bip353_dnssec">Địa chỉ này được lưu trữ trên một DNS không an toàn. DNSSEC phải được bật.</string>

<!-- validation -->

Expand Down
2 changes: 2 additions & 0 deletions phoenix-android/src/main/res/values/important_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@
<string name="scan_error_lnurl_invalid">Failed to process this LNURL link. Make sure it is valid.</string>
<string name="scan_error_lnurl_unsupported">This type of LNURL is not supported yet.</string>
<string name="scan_error_invalid_generic">This data uses an unknown format and cannot be processed.</string>
<string name="scan_error_bip353_invalid_offer">This address uses an invalid Bolt12 offer.</string>
<string name="scan_error_bip353_dnssec">This address is hosted on an unsecure DNS. DNSSEC must be enabled.</string>

<!-- validation -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@ data class MaxFees(

object Scan {

sealed class BadRequestReason {
sealed class BadRequestReason : Exception() {
object UnknownFormat : BadRequestReason()
object AlreadyPaidInvoice : BadRequestReason()
data class Expired(val timestampSeconds: Long, val expirySeconds: Long) : BadRequestReason()
data class ChainMismatch(val expected: Chain) : BadRequestReason()
data class ServiceError(val url: Url, val error: LnurlError.RemoteFailure) : BadRequestReason()
data class InvalidLnurl(val url: Url) : BadRequestReason()
data class InvalidBip353(val path: String) : BadRequestReason()
data class Bip353InvalidOffer(val path: String) : BadRequestReason()
data class Bip353NoDNSSEC(val path: String) : BadRequestReason()
data class UnsupportedLnurl(val url: Url) : BadRequestReason()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,23 +128,31 @@ class AppScanController(
) {
val input = Parser.removeExcessInput(intent.request)

Parser.readBolt11Invoice(input)?.let {
processBolt11Invoice(it)
} ?: Parser.readOffer(input)?.let {
processOffer(it)
} ?: readEmailLikeAddress(input)?.let {
when (it) {
is Either.Left -> processOffer(it.value)
is Either.Right -> processLnurl(it.value)
try {
Parser.readBolt11Invoice(input)?.let {
processBolt11Invoice(it)
} ?: Parser.readOffer(input)?.let {
processOffer(it)
} ?: readEmailLikeAddress(input)?.let {
when (it) {
is Either.Left -> processOffer(it.value)
is Either.Right -> processLnurl(it.value)
}
} ?: readLnurl(input)?.let {
processLnurl(it)
} ?: readBitcoinAddress(input)?.let {
processBitcoinAddress(input, it)
} ?: readLNURLFallback(input)?.let {
processLnurl(it)
} ?: run {
model(Scan.Model.BadRequest(request = intent.request, reason = Scan.BadRequestReason.UnknownFormat))
}
} catch (e: Exception) {
if (e is Scan.BadRequestReason) {
model(Scan.Model.BadRequest(request = intent.request, reason = e))
} else {
model(Scan.Model.BadRequest(request = intent.request, reason = Scan.BadRequestReason.UnknownFormat))
}
} ?: readLnurl(input)?.let {
processLnurl(it)
} ?: readBitcoinAddress(input)?.let {
processBitcoinAddress(input, it)
} ?: readLNURLFallback(input)?.let {
processLnurl(it)
} ?: run {
model(Scan.Model.BadRequest(request = intent.request, reason = Scan.BadRequestReason.UnknownFormat))
}
}

Expand Down Expand Up @@ -564,45 +572,39 @@ class AppScanController(
model(Scan.Model.ResolvingBip353)
val dnsPath = "$username.user._bitcoin-payment.$domain."

try {
val json = DnsResolvers.getRandom().getTxtRecord(dnsPath)
logger.debug { "dns resolved to ${json.toString().take(100)}" }
val json = DnsResolvers.getRandom().getTxtRecord(dnsPath)
logger.debug { "dns resolved to ${json.toString().take(100)}" }

val status = json["Status"]?.jsonPrimitive?.intOrNull
if (status == null || status > 0) throw RuntimeException("invalid status=$status")
val status = json["Status"]?.jsonPrimitive?.intOrNull
if (status == null || status > 0) return null

val ad = json["AD"]?.jsonPrimitive?.booleanOrNull
if (ad != true) {
logger.info { "AD false, abort dns lookup" }
return null
}
val records = json["Answer"]?.jsonArray
if (records.isNullOrEmpty()) {
logger.debug { "no records for $dnsPath" }
return null
}

val records = json["Answer"]?.jsonArray
if (records.isNullOrEmpty()) {
logger.debug { "no records for $dnsPath" }
return null
}
val matchingRecord = records.filterIsInstance<JsonObject>().firstOrNull {
logger.debug { "inspecting record=$it" }
it["name"]?.jsonPrimitive?.content == dnsPath
} ?: return null

val matchingRecord = records.filterIsInstance<JsonObject>().firstOrNull {
logger.debug { "inspecting record=$it" }
it["name"]?.jsonPrimitive?.content == dnsPath
} ?: return null

val data = matchingRecord["data"]?.jsonPrimitive?.content ?: return null
if (!data.startsWith("bitcoin:")) return null
val offerString = data.substringAfter("lno=").substringBefore("?")
if (offerString.isBlank()) return null

return when (val offer = OfferTypes.Offer.decode(offerString)) {
is Try.Success -> { offer.result }
is Try.Failure -> {
model(Scan.Model.BadRequest(request = dnsPath, reason = Scan.BadRequestReason.InvalidBip353(dnsPath)))
null
}
val ad = json["AD"]?.jsonPrimitive?.booleanOrNull
if (ad != true) {
logger.debug { "AD false, abort dns lookup" }
throw Scan.BadRequestReason.Bip353NoDNSSEC(dnsPath)
}

val data = matchingRecord["data"]?.jsonPrimitive?.content ?: return null
if (!data.startsWith("bitcoin:")) throw Scan.BadRequestReason.Bip353InvalidOffer(dnsPath)
val offerString = data.substringAfter("lno=").substringBefore("?")
if (offerString.isBlank()) throw Scan.BadRequestReason.Bip353InvalidOffer(dnsPath)

return when (val offer = OfferTypes.Offer.decode(offerString)) {
is Try.Success -> { offer.result }
is Try.Failure -> {
throw Scan.BadRequestReason.Bip353InvalidOffer(dnsPath)
}
} catch (e: Exception) {
logger.error(e) { "error when resolving offer on $dnsPath: ${e.message}" }
return null
}
}

Expand Down

0 comments on commit 7044826

Please sign in to comment.