Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates bech32 checksum constant to "M" #3

Merged
merged 4 commits into from
Feb 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 64 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,27 @@ make test
### Installing prerequirements

If the above doesn't work, you probably need to install some
prerequirements. For example, on a fresh Debian Stretch system:
prerequirements. For example, on a fresh Debian 10 ("buster") system:

```
$ sudo apt-get install make gcc g++
sudo apt-get install make gcc g++
```

It is worth getting the latest cmake, so install that the hard way:

```
wget https://cmake.org/files/v3.13/cmake-3.13.2.tar.gz
tar xzf cmake-3.13.2.tar.gz
cd cmake-3.13.2
wget https://cmake.org/files/v3.19/cmake-3.19.4.tar.gz
tar xzf cmake-3.19.4.tar.gz
cd cmake-3.19.4
./configure
make
sudo make install
```

Now you can again try to build libbech32.

## Example Code

### C++ Usage Example

```cpp
Expand All @@ -63,17 +65,20 @@ int main() {
// encode
std::string bstr = bech32::encode(hrp, data);

// prints "hello1w0rldcs7fw6" : "hello" + Bech32.separator + encoded data + 6 char checksum
// prints "hello1w0rldjn365x" : "hello" + Bech32.separator + encoded data + 6 char checksum
std::cout << bstr << std::endl;

// decode
bech32::HrpAndDp hd = bech32::decode(bstr);

assert(hrp == hd.hrp);
assert(data == hd.dp);
assert(bech32::Encoding::Bech32m == hd.encoding);
}
```

For more C++ examples, see `examples/cpp_example.cpp`

### C Usage Example

```C
Expand All @@ -87,24 +92,68 @@ int main() {
char hrp[] = "hello";
unsigned char dp[] = {14, 15, 3, 31, 13};

// create output array for bech32 string
char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0};
// create output for bech32 string
bech32_bstring *bstring = bech32_create_bstring(strlen(hrp), sizeof(dp));

// encode
assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS);
assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS);

// prints "hello1w0rldcs7fw6" : "hello" + Bech32.separator + encoded data + 6 char checksum
// prints "hello1w0rldjn365x" : "hello" + Bech32.separator + encoded data + 6 char checksum
printf("bech32 encoding of human-readable part \'hello\' and data part \'[14, 15, 3, 31, 13]\' is:\n");
printf("%s\n", bstr);
printf("%s\n", bstring->string);

// allocate memory for decoded data
bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr);
bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstring->string);

// decode
assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS);
assert(strcmp(hrpdp->hrp, "hello") == 0);
assert(bech32_decode(hrpdp, bstring->string) == E_BECH32_SUCCESS);
assert(strcmp(hrpdp->hrp, hrp) == 0);
assert(hrpdp->dp[0] == dp[0]);
assert(hrpdp->dp[4] == dp[4]);
assert(ENCODING_BECH32M == hrpdp->encoding);

// free memory
free_HrpAndDp_storage(hrpdp);
bech32_free_HrpAndDp(hrpdp);
bech32_free_bstring(bstring);
}
```

For more C examples, see `examples/c_example.cpp`


## Regarding bech32 checksums

The Bech32 data encoding format was first proposed by Pieter Wuille in early 2017 in
[BIP 0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki). Later, in November 2019, Pieter published
some research regarding that an exponent used in the bech32 checksum algorithm (value = 1) may not be
optimal for the error detecting properties of bech32. In February 2021, Pieter published
[BIP 0350](http://www.example.com) reporting that "exhaustive analysis" showed the best possible exponent value is
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bip350 needs a link

0x2bc830a3. This improved variant of Bech32 is called "Bech32m".

When decoding a possible bech32 encoded string, libbech32 returns an enum value showing whether bech32m or bech32
was used to encode. This can be seen in the exaples above.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: examples


When encoding data, libbech32 defaults to using the new exponent value of 0x2bc830a3. If the original exponent value
of 1 is desired, then the following functions may be used:

### C++ Usage Example

```cpp
/// ... as above ...

// encode
std::string bstr = bech32::encodeUsingOriginalConstant(hrp, data);

/// ... as above ...
```

### C Usage Example

```C
/// ... as above ...

// encode
assert(bech32_encode_using_original_constant(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS);

/// ... as above ...
```
86 changes: 56 additions & 30 deletions examples/c_example.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,55 +12,62 @@ void encodeAndDecode() {
char hrp[] = "example";
unsigned char dp[] = {0, 1, 2, 3, 4, 5, 6, 7, 8};

// create output array for bech32 string
char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0};
// create output for bech32 string
bech32_bstring *bstring = bech32_create_bstring(sizeof(hrp), sizeof(dp));

// expected bech32 string output
char expected[] = "example1qpzry9x8gnylnjs";
char expected[] = "example1qpzry9x8ge8sqgv";

// encode
assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS);
assert(strcmp(expected, bstr) == 0);

printf("bech32 encoding of human-readable part \'example\' and data part \'[0, 1, 2, 3, 4, 5, 6, 7, 8]\' is:\n");
printf("%s\n", bstr);
assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS);
assert(strcmp(expected, bstring->string) == 0);

// allocate memory for decoded data
bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr);
bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstring->string);

// decode
assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS);
assert(bech32_decode(hrpdp, bstring->string) == E_BECH32_SUCCESS);
assert(strcmp(hrpdp->hrp, "example") == 0);
assert(hrpdp->dp[0] == 0);
assert(hrpdp->dp[8] == 8);
assert(ENCODING_BECH32M == hrpdp->encoding);

// free memory
free_HrpAndDp_storage(hrpdp);

bech32_free_HrpAndDp(hrpdp);
bech32_free_bstring(bstring);
}

void decodeAndEncode() {

// bech32 string with extra invalid characters
char bstr[] = " example1:qpz!r--y9#x8&%&%gn-y-lnjs ";
char bstr[] = " example1:qpz!r--y9#x8&%&%ge-8-sqgv ";
// expected bech32 string output
char expected[] = "example1qpzry9x8gnylnjs";
char expected[] = "example1qpzry9x8ge8sqgv";

// strip unwanted chars from bstr
bech32_stripUnknownChars(bstr, sizeof(bstr), bstr, sizeof(bstr));
// make sure to strip invalid characters before allocating storage and trying to decode
assert(bech32_stripUnknownChars(bstr, sizeof(bstr), bstr, sizeof(bstr)) == E_BECH32_SUCCESS);

// allocate memory for decoded data
bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr);
bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr);

// decode
assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS);
assert(bech32_decode(hrpdp, bstr) == E_BECH32_SUCCESS);
assert(strcmp(hrpdp->hrp, "example") == 0);
assert(hrpdp->dp[0] == 0);
assert(hrpdp->dp[8] == 8);

// free memory
free_HrpAndDp_storage(hrpdp);
// create output array for bech32 string
bech32_bstring *bstring = bech32_create_bstring_from_HrpAndDp(hrpdp);

// encode
assert(bech32_encode(bstring, hrpdp->hrp, hrpdp->dp, hrpdp->dplen) == E_BECH32_SUCCESS);

// encoding of "cleaned" decoded data should match expected string
assert(strcmp(expected, bstring->string) == 0);

// free memory
bech32_free_HrpAndDp(hrpdp);
bech32_free_bstring(bstring);
}

void badEncoding() {
Expand All @@ -69,35 +76,54 @@ void badEncoding() {
char hrp[] = "example";
unsigned char dp[] = {0, 1, 2, 3, 4, 5, 6, 7, 33};

// create output array for bech32 string
char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0};
// create output for bech32 string
bech32_bstring *bstring = bech32_create_bstring(sizeof(hrp), sizeof(dp));

// encode
assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_UNKNOWN_ERROR);
assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_UNKNOWN_ERROR);

// free memory
bech32_free_bstring(bstring);
}

void badDecoding() {
void badDecoding_corruptData() {

// bech32 string with extra invalid characters
char bstr[] = "example1qpzry9x8gnylnjs";
// bech32 string
char bstr[] = "example1qpzry9x8ge8sqgv";
// simulate corrupted data--checksum verification will fail
bstr[10] = 'x';

// allocate memory for decoded data
bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr);
bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr);

// decode
assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_INVALID_CHECKSUM);
assert(bech32_decode(hrpdp, bstr) == E_BECH32_INVALID_CHECKSUM);

// free memory
free_HrpAndDp_storage(hrpdp);
bech32_free_HrpAndDp(hrpdp);
}

void badDecoding_corruptChecksum() {

// bech32 string
char bstr[] = "example1qpzry9x8ge8sqgv";
// simulate corrupted checksum--verification will fail
bstr[19] = 'q';

// allocate memory for decoded data
bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstr);

// decode
assert(bech32_decode(hrpdp, bstr) == E_BECH32_INVALID_CHECKSUM);

// free memory
bech32_free_HrpAndDp(hrpdp);
}

int main() {
encodeAndDecode();
decodeAndEncode();
badEncoding();
badDecoding();
badDecoding_corruptData();
badDecoding_corruptChecksum();
}
23 changes: 14 additions & 9 deletions examples/c_usage_example.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// This small example is mainly meant for easy copy/paste into the README.md
#include "libbech32.h"
#include <string.h>
#include <stdio.h>
Expand All @@ -11,23 +12,27 @@ int main() {
char hrp[] = "hello";
unsigned char dp[] = {14, 15, 3, 31, 13};

// create output array for bech32 string
char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0};
// create output for bech32 string
bech32_bstring *bstring = bech32_create_bstring(strlen(hrp), sizeof(dp));

// encode
assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS);
assert(bech32_encode(bstring, hrp, dp, sizeof(dp)) == E_BECH32_SUCCESS);

// prints "hello1w0rldcs7fw6" : "hello" + Bech32.separator + encoded data + 6 char checksum
// prints "hello1w0rldjn365x" : "hello" + Bech32.separator + encoded data + 6 char checksum
printf("bech32 encoding of human-readable part \'hello\' and data part \'[14, 15, 3, 31, 13]\' is:\n");
printf("%s\n", bstr);
printf("%s\n", bstring->string);

// allocate memory for decoded data
bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr);
bech32_HrpAndDp * hrpdp = bech32_create_HrpAndDp(bstring->string);

// decode
assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS);
assert(strcmp(hrpdp->hrp, "hello") == 0);
assert(bech32_decode(hrpdp, bstring->string) == E_BECH32_SUCCESS);
assert(strcmp(hrpdp->hrp, hrp) == 0);
assert(hrpdp->dp[0] == dp[0]);
assert(hrpdp->dp[4] == dp[4]);
assert(ENCODING_BECH32M == hrpdp->encoding);

// free memory
free_HrpAndDp_storage(hrpdp);
bech32_free_HrpAndDp(hrpdp);
bech32_free_bstring(bstring);
}
Loading