mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-11-08 08:01:09 +00:00
Merge pull request #964 from Chessing234/codex/bech32-canonical-padding
Reject noncanonical Bech32 padding and invalid prefix lengths
This commit is contained in:
commit
f7dc80fb2b
@ -12,6 +12,7 @@ object Bech32 {
|
||||
* Encode data with HRP (Human Readable Part)
|
||||
*/
|
||||
fun encode(hrp: String, data: ByteArray): String {
|
||||
require(hrp.length in 1..83) { "Invalid HRP length" }
|
||||
val values = convertBits(data, 8, 5, true).toList()
|
||||
val checksum = createChecksum(hrp, values)
|
||||
val combined = values + checksum
|
||||
@ -25,7 +26,7 @@ object Bech32 {
|
||||
*/
|
||||
fun decode(bech32String: String): Pair<String, ByteArray> {
|
||||
val separatorIndex = bech32String.lastIndexOf('1')
|
||||
require(separatorIndex >= 0) { "No separator found" }
|
||||
require(separatorIndex in 1..83) { "Invalid HRP length or missing separator" }
|
||||
|
||||
val hrp = bech32String.substring(0, separatorIndex)
|
||||
val dataString = bech32String.substring(separatorIndex + 1)
|
||||
@ -76,6 +77,9 @@ object Bech32 {
|
||||
|
||||
if (pad && bits > 0) {
|
||||
result.add((acc shl (toBits - bits)) and maxv)
|
||||
} else if (!pad) {
|
||||
require(bits < fromBits) { "Excess padding" }
|
||||
require(((acc shl (toBits - bits)) and maxv) == 0) { "Nonzero padding" }
|
||||
}
|
||||
|
||||
return result.toIntArray()
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
package com.bitchat.android.nostr
|
||||
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertThrows
|
||||
import org.junit.Test
|
||||
|
||||
class Bech32PaddingTest {
|
||||
@Test
|
||||
fun `a literal canonical npub decodes to its key`() {
|
||||
val npub = "npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzqujme"
|
||||
val (hrp, key) = Bech32.decode(npub)
|
||||
assertEquals("npub", hrp)
|
||||
assertArrayEquals(ByteArray(32), key)
|
||||
assertEquals(npub, Bech32.encode(hrp, key))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `valid checksum does not permit nonzero discarded padding`() {
|
||||
// The last payload symbol differs by one bit from the canonical zero key.
|
||||
val npub = "npub1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqplkg8xt"
|
||||
assertThrows(IllegalArgumentException::class.java) { Bech32.decode(npub) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a whole extra padding symbol cannot alias an empty payload`() {
|
||||
assertThrows(IllegalArgumentException::class.java) { Bech32.decode("test1q6j8x73") }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty and oversized human readable prefixes are rejected`() {
|
||||
for (value in listOf("10a06t8", "x".repeat(84) + "1e5y3yu")) {
|
||||
assertThrows(IllegalArgumentException::class.java) { Bech32.decode(value) }
|
||||
}
|
||||
for (hrp in listOf("", "x".repeat(84))) {
|
||||
assertThrows(IllegalArgumentException::class.java) { Bech32.encode(hrp, byteArrayOf()) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `all byte lengths preserve their canonical padding`() {
|
||||
for (length in 0..64) {
|
||||
val bytes = ByteArray(length) { (it * 37).toByte() }
|
||||
assertArrayEquals(bytes, Bech32.decode(Bech32.encode("test", bytes)).second)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,6 +36,12 @@ randomization used by senders. Android caps outbound seal and gift-wrap
|
||||
randomization at 22h, leaving 2 hours of slack inside iOS's 24-hour
|
||||
subscription window, while retaining its 48-hour receive lookback.
|
||||
|
||||
Bech32 byte decoding must reject nonzero discarded bits and a residual whole
|
||||
five-bit symbol. A valid checksum alone does not make byte padding canonical.
|
||||
Human-readable prefixes contain 1–83 characters; Nostr's extended data payloads
|
||||
are not subject to Bitcoin address length limits. `Bech32PaddingTest` pins these
|
||||
rules with literal checksum-valid positive and negative vectors.
|
||||
|
||||
## Rewrite acceptance gate
|
||||
|
||||
From a configured Android development environment, run:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user