Skip to content

Commit 74641a7

Browse files
authored
Bech32: fix handling of invalid characters (#158)
We did not properly check for invalid characters (i.e. not in the bech32 character set) in the data part.
1 parent ca1cf2f commit 74641a7

File tree

2 files changed

+14
-3
lines changed

2 files changed

+14
-3
lines changed

src/commonMain/kotlin/fr/acinq/bitcoin/Bech32.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,14 @@ public object Bech32 {
7272
values.forEach { v ->
7373
val b = chk shr 25
7474
chk = ((chk and 0x1ffffff) shl 5) xor v.toInt()
75-
for (i in 0..5) {
75+
for (i in GEN.indices) {
7676
if (((b shr i) and 1) != 0) chk = chk xor GEN[i]
7777
}
7878
}
7979
values1.forEach { v ->
8080
val b = chk shr 25
8181
chk = ((chk and 0x1ffffff) shl 5) xor v.toInt()
82-
for (i in 0..5) {
82+
for (i in GEN.indices) {
8383
if (((b shr i) and 1) != 0) chk = chk xor GEN[i]
8484
}
8585
}
@@ -121,11 +121,12 @@ public object Bech32 {
121121
@JvmStatic
122122
public fun decode(bech32: String, noChecksum: Boolean = false): Triple<String, Array<Int5>, Encoding> {
123123
require(bech32.lowercase() == bech32 || bech32.uppercase() == bech32) { "mixed case strings are not valid bech32" }
124-
bech32.forEach { require(it.code in 33..126) { "invalid character " } }
125124
val input = bech32.lowercase()
126125
val pos = input.lastIndexOf('1')
127126
val hrp = input.take(pos)
127+
hrp.forEach { require(it.code in 33..126) { "invalid character in hrp" } }
128128
require(hrp.length in 1..83) { "hrp must contain 1 to 83 characters" }
129+
input.drop(pos + 1).forEach { require(alphabet.contains(it)) { "invalid character" } }
129130
val data = Array<Int5>(input.length - pos - 1) { 0 }
130131
for (i in 0..data.lastIndex) data[i] = map[input[pos + 1 + i].code]
131132
return if (noChecksum) {

src/commonTest/kotlin/fr/acinq/bitcoin/Bech32TestsCommon.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,16 @@ class Bech32TestsCommon {
159159
}
160160
}
161161

162+
@Test
163+
fun `reject invalid character`() {
164+
// '(' is not a valid bech32 character
165+
val encoded = "lno1zcss88lll8vlpqqqqqqclllllllvwvcqpq8qllllgqrqqgqq8s(q8888"
166+
val error = assertFails {
167+
Bech32.decode(encoded)
168+
}
169+
assertEquals("invalid character", error.message)
170+
}
171+
162172
@Test
163173
fun `encode and decode arbitrary data`() {
164174
val bin = listOf(

0 commit comments

Comments
 (0)