sipa / bech32

Code snippets and analysis of the Bech32 format
191 stars 107 forks source link

Haskell implementation of `bech32Encode` generates invalid output if the HRP contains upper case characters. #49

Open jonathanknowles opened 5 years ago

jonathanknowles commented 5 years ago

Overview

When bech32Encode is called with a human-readable part that contains one or more upper-case characters, it produces in an invalid Bech32 string that cannot be decoded with the bech32Decode function.

Analysis

As part of the encoding process, the human-readable part is converted to lower case:

https://github.com/sipa/bech32/blob/a4e672df3b79be54a9b14a33ae5fdb7d10a1dd26/ref/haskell/src/Codec/Binary/Bech32.hs#L85

However, the checksum is calculated before the conversion to lower case takes place:

https://github.com/sipa/bech32/blob/a4e672df3b79be54a9b14a33ae5fdb7d10a1dd26/ref/haskell/src/Codec/Binary/Bech32.hs#L83

This contradicts the Bech32 specification, which states:

"The lowercase form is used when determining a character's value for checksum purposes."

Therefore, if the original human-readable part contains one or more upper case characters:

  1. the generated checksum will be inconsistent with the human-readable prefix of the output string
  2. the output string will fail to decode.

Example

Consider the following two calls to bech32Encode, differing only in the case of the human-readable part:

> bech32Encode "test" []
> bech32Encode "TEST" []

Expected Behaviour

Both calls to bech32Encode should result in the same output string:

> bech32Encode "test" []
Just "test12hrzfj"
> bech32Encode "TEST" []
Just "test12hrzfj"
> bech32Encode "test" [] == bech32Encode "TEST" [] 
True

Actual Behaviour

The above calls to bech32Encode actually result in different output strings:

> bech32Encode "test" []
Just "test12hrzfj"
> bech32Encode "TEST" []
Just "test13jgcyw"
> bech32Encode "test" [] == bech32Encode "TEST" [] 
False

Attempting to decode the string produced by bech32Encode "TEST" [] results in Nothing:

> bech32Decode "test13jgcyw"
Nothing
real-or-random commented 5 years ago

I haven't looked at the details but seems related to #38.

jonathanknowles commented 5 years ago

I've created a PR that fixes the issue here: https://github.com/sipa/bech32/pull/50