diff --git a/app/src/main/java/com/bitchat/android/nostr/Bech32.kt b/app/src/main/java/com/bitchat/android/nostr/Bech32.kt index 3e55ab67..7ad59f57 100644 --- a/app/src/main/java/com/bitchat/android/nostr/Bech32.kt +++ b/app/src/main/java/com/bitchat/android/nostr/Bech32.kt @@ -22,14 +22,23 @@ object Bech32 { /** * Decode bech32 string * Returns (hrp, data) pair + * + * Per BIP-173 a bech32 string is either all lowercase or all uppercase; + * the uppercase form is what QR encoders emit, since it fits the compact + * alphanumeric mode. Mixed case is invalid and is rejected. */ fun decode(bech32String: String): Pair { - val separatorIndex = bech32String.lastIndexOf('1') + val hasLower = bech32String.any { it.isLowerCase() } + val hasUpper = bech32String.any { it.isUpperCase() } + require(!(hasLower && hasUpper)) { "Mixed case" } + val normalized = bech32String.lowercase() + + val separatorIndex = normalized.lastIndexOf('1') require(separatorIndex >= 0) { "No separator found" } - - val hrp = bech32String.substring(0, separatorIndex) - val dataString = bech32String.substring(separatorIndex + 1) - + + val hrp = normalized.substring(0, separatorIndex) + val dataString = normalized.substring(separatorIndex + 1) + // Validate HRP contains only ASCII require(hrp.all { it.code < 128 }) { "Invalid HRP characters" }