diff --git a/README.md b/README.md index b1bc7db..9919b8a 100644 --- a/README.md +++ b/README.md @@ -48,3 +48,63 @@ sudo make install Now you can again try to build libbech32. +### C++ Usage Example + +```cpp +#include "libbech32.h" +#include +#include + +int main() { + // simple human readable part with some data + std::string hrp = "hello"; + std::vector data = {14, 15, 3, 31, 13}; + + // encode + std::string bstr = bech32::encode(hrp, data); + + // prints "hello1w0rldcs7fw6" : "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); +} +``` + +### C Usage Example + +```C +#include "libbech32.h" +#include +#include +#include + +int main() { + // simple human readable part with some data + 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}; + + // encode + assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS); + + // prints "hello1w0rldcs7fw6" : "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); + + // allocate memory for decoded data + bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + + // decode + assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS); + assert(strcmp(hrpdp->hrp, "hello") == 0); + + // free memory + free_HrpAndDp_storage(hrpdp); +} +``` diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 438f616..af8ad32 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -7,6 +7,17 @@ set_target_properties(bech32_cpp_example PROPERTIES CXX_EXTENSIONS OFF) target_link_libraries(bech32_cpp_example bech32) +## + +add_executable(bech32_cpp_usage_example cpp_usage_example.cpp) + +target_compile_features(bech32_cpp_usage_example PRIVATE cxx_std_11) +target_compile_options(bech32_cpp_usage_example PRIVATE ${DCD_CXX_FLAGS}) +set_target_properties(bech32_cpp_usage_example PROPERTIES CXX_EXTENSIONS OFF) + +target_link_libraries(bech32_cpp_usage_example bech32) + +## add_executable(bech32_c_example c_example.c) @@ -14,3 +25,12 @@ target_compile_features(bech32_c_example PRIVATE c_std_99) set_target_properties(bech32_c_example PROPERTIES C_EXTENSIONS OFF) target_link_libraries(bech32_c_example bech32) + +## + +add_executable(bech32_c_usage_example c_usage_example.c) + +target_compile_features(bech32_c_usage_example PRIVATE c_std_99) +set_target_properties(bech32_c_usage_example PROPERTIES C_EXTENSIONS OFF) + +target_link_libraries(bech32_c_usage_example bech32) diff --git a/examples/c_example.c b/examples/c_example.c index fdcbc32..58f4d04 100644 --- a/examples/c_example.c +++ b/examples/c_example.c @@ -6,7 +6,7 @@ #undef NDEBUG #include -int main() { +void encodeAndDecode() { // hrp and data to encode char hrp[] = "example"; @@ -16,7 +16,7 @@ int main() { char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0}; // expected bech32 string output - char expected[] = "example1qpzry9x8gvmqvdw"; + char expected[] = "example1qpzry9x8gnylnjs"; // encode assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS); @@ -38,3 +38,66 @@ int main() { free_HrpAndDp_storage(hrpdp); } + +void decodeAndEncode() { + + // bech32 string with extra invalid characters + char bstr[] = " example1:qpz!r--y9#x8&%&%gn-y-lnjs "; + // expected bech32 string output + char expected[] = "example1qpzry9x8gnylnjs"; + + // strip unwanted chars from bstr + bech32_stripUnknownChars(bstr, sizeof(bstr), bstr, sizeof(bstr)); + + // allocate memory for decoded data + bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + + // decode + assert(bech32_decode(hrpdp, bstr, sizeof(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); + +} + +void badEncoding() { + + // hrp and data to encode + 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}; + + // encode + assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_UNKNOWN_ERROR); + +} + +void badDecoding() { + + // bech32 string with extra invalid characters + char bstr[] = "example1qpzry9x8gnylnjs"; + // simulate corrupted data--checksum verification will fail + bstr[10] = 'x'; + + // allocate memory for decoded data + bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + + // decode + assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_INVALID_CHECKSUM); + + // free memory + free_HrpAndDp_storage(hrpdp); + +} + +int main() { + encodeAndDecode(); + decodeAndEncode(); + badEncoding(); + badDecoding(); +} diff --git a/examples/c_usage_example.c b/examples/c_usage_example.c new file mode 100644 index 0000000..e4ba431 --- /dev/null +++ b/examples/c_usage_example.c @@ -0,0 +1,33 @@ +#include "libbech32.h" +#include +#include + +// make sure we can check these examples even when building a release version +#undef NDEBUG +#include + +int main() { + // simple human readable part with some data + 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}; + + // encode + assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS); + + // prints "hello1w0rldcs7fw6" : "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); + + // allocate memory for decoded data + bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + + // decode + assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_SUCCESS); + assert(strcmp(hrpdp->hrp, "hello") == 0); + + // free memory + free_HrpAndDp_storage(hrpdp); +} diff --git a/examples/cpp_example.cpp b/examples/cpp_example.cpp index 957cf04..a5c62eb 100644 --- a/examples/cpp_example.cpp +++ b/examples/cpp_example.cpp @@ -15,36 +15,79 @@ void encodeAndDecode() { // encode std::string bstr = bech32::encode(hrp, data); - // will print "example1qpzry9x8gvmqvdw" ... last 6 characters are the checksum + // will print "example1qpzry9x8gnylnjs" ... last 6 characters are the checksum std::cout << R"(bech32 encoding of human-readable part 'example' and data part '[0, 1, 2, 3, 4, 5, 6, 7, 8]' is:)" << std::endl; std::cout << bstr << std::endl; // decode - bech32::HrpAndDp b = bech32::decode(bstr); + bech32::HrpAndDp hd = bech32::decode(bstr); - assert(hrp == b.hrp); - assert(data == b.dp); + assert(hrp == hd.hrp); + assert(data == hd.dp); } void decodeAndEncode() { // bech32 string with extra invalid characters - std::string bstr = "tx1:yjk!0-uq#ay-z%u4x-nk6u&-pc"; - std::string expected = "tx1yjk0uqayzu4xnk6upc"; + std::string bstr = " example1:qpz!r--y9#x8&%&%gn-y-lnjs "; + std::string expected = "example1qpzry9x8gnylnjs"; // decode - make sure to strip invalid characters before trying to decode - bech32::HrpAndDp b = bech32::decode(bech32::stripUnknownChars(bstr)); + bech32::HrpAndDp hd = bech32::decode(bech32::stripUnknownChars(bstr)); + + // verify decoding + assert(!hd.hrp.empty() && !hd.dp.empty()); // encode - bstr = bech32::encode(b.hrp, b.dp); + bstr = bech32::encode(hd.hrp, hd.dp); // encoding of "cleaned" decoded data should match expected string assert(bstr == expected); + + // simulate corrupted data--checksum verification will fail + bstr[10] = 'x'; + + // decode - make sure to strip invalid characters before trying to decode + hd = bech32::decode(bech32::stripUnknownChars(bstr)); + + // verify decoding failed + assert(hd.hrp.empty() && hd.dp.empty()); + } -int main() { +void badEncoding() { - encodeAndDecode(); + // human-readable part + std::string hrp = "example"; + // data values can be 0-31 + std::vector data = {0, 1, 2, 3, 4, 5, 6, 7, 33}; - decodeAndEncode(); + // encode + try { + std::string bstr = bech32::encode(hrp, data); + } + catch (std::exception &e) { + assert(std::string(e.what()) == "data value is out of range"); + } +} + +void badDecoding() { + + // valid bech32 string + std::string bstr = "example1qpzry9x8gnylnjs"; + // simulate corrupted data--checksum verification will fail + bstr[10] = 'x'; + + // decode + bech32::HrpAndDp hd = bech32::decode(bstr); + // verify decoding failed + assert(hd.hrp.empty() && hd.dp.empty()); + +} + +int main() { + encodeAndDecode(); + decodeAndEncode(); + badEncoding(); + badDecoding(); } diff --git a/examples/cpp_usage_example.cpp b/examples/cpp_usage_example.cpp new file mode 100644 index 0000000..99cf672 --- /dev/null +++ b/examples/cpp_usage_example.cpp @@ -0,0 +1,25 @@ +#include "libbech32.h" +#include + +// make sure we can check these examples even when building a release version +#undef NDEBUG +#include + +int main() { + // simple human readable part with some data + std::string hrp = "hello"; + std::vector data = {14, 15, 3, 31, 13}; + + // encode + std::string bstr = bech32::encode(hrp, data); + + // prints "hello1w0rldcs7fw6" : "hello" + Bech32.separator + encoded data + 6 char checksum + std::cout << R"(bech32 encoding of human-readable part 'hello' and data part '[14, 15, 3, 31, 13]' is:)" << std::endl; + std::cout << bstr << std::endl; + + // decode + bech32::HrpAndDp hd = bech32::decode(bstr); + + assert(hrp == hd.hrp); + assert(data == hd.dp); +} diff --git a/include/libbech32/libbech32.h b/include/libbech32/libbech32.h index c05a188..9957c32 100644 --- a/include/libbech32/libbech32.h +++ b/include/libbech32/libbech32.h @@ -89,6 +89,7 @@ typedef enum bech32_error_e E_BECH32_UNKNOWN_ERROR, E_BECH32_NULL_ARGUMENT, E_BECH32_LENGTH_TOO_SHORT, + E_BECH32_INVALID_CHECKSUM, E_BECH32_MAX_ERROR } bech32_error; diff --git a/libbech32/bech32.cpp b/libbech32/bech32.cpp index 3997af4..b8a5429 100644 --- a/libbech32/bech32.cpp +++ b/libbech32/bech32.cpp @@ -6,6 +6,9 @@ namespace { using namespace bech32::limits; + // exponent used in checksum generation, taken from recommendation + // in: https://gist.github.com/sipa/a9845b37c1b298a7301c33a04090b2eb + const unsigned int M = 0x3FFFFFFF; /** The Bech32 character set for encoding. The index into this string gives the char * each value is mapped to, i.e., 0 -> 'q', 10 -> '2', etc. This comes from the table @@ -112,6 +115,18 @@ namespace { } } + // using the charset of valid chars, map the incoming data + std::string mapToCharset(std::vector &data) { + std::string ret; + ret.reserve(data.size()); + for (unsigned char c : data) { + if(c > VALID_CHARSET_SIZE - 1) + throw std::runtime_error("data part contains invalid character"); + ret += charset[c]; + } + return ret; + } + // "expand" the HRP -- adapted from example in BIP-0173 // // To expand the chars of the HRP means to create a new collection of @@ -154,7 +169,7 @@ namespace { } bool verifyChecksum(const std::string &hrp, const std::vector &dp) { - return polymod(cat(expandHrp(hrp), dp)) == 1; + return polymod(cat(expandHrp(hrp), dp)) == M; } void stripChecksum(std::vector &dp) { @@ -165,7 +180,7 @@ namespace { createChecksum(const std::string &hrp, const std::vector &dp) { std::vector c = cat(expandHrp(hrp), dp); c.resize(c.size() + CHECKSUM_LENGTH); - uint32_t mod = polymod(c) ^ 1u; + uint32_t mod = polymod(c) ^ M; std::vector ret(CHECKSUM_LENGTH); for(std::vector::size_type i = 0; i < CHECKSUM_LENGTH; ++i) { ret[i] = static_cast((mod >> (5 * (5 - i))) & 31u); @@ -274,6 +289,7 @@ const char *bech32_errordesc[] = { "Unknown error", "Function argument was null", "Function argument length was too short", + "Invalid Checksum", "Max error" }; @@ -462,6 +478,9 @@ bech32_error bech32_decode(bech32_HrpAndDp *output, char const *bstr, size_t bst return E_BECH32_UNKNOWN_ERROR; } + if(hrpAndDp.hrp.empty() && hrpAndDp.dp.empty()) + return E_BECH32_INVALID_CHECKSUM; + if(hrpAndDp.hrp.size() > output->hrplen-1) return E_BECH32_LENGTH_TOO_SHORT; if(hrpAndDp.dp.size() > output->dplen) diff --git a/test/testbech32/CMakeLists.txt b/test/testbech32/CMakeLists.txt index 5771191..95380ea 100644 --- a/test/testbech32/CMakeLists.txt +++ b/test/testbech32/CMakeLists.txt @@ -20,6 +20,7 @@ add_executable(bech32_c_api_tests ) target_compile_features(bech32_c_api_tests PRIVATE c_std_99) +target_compile_options(bech32_c_api_tests PRIVATE "-fPIC") set_target_properties(bech32_c_api_tests PROPERTIES C_EXTENSIONS OFF) target_link_libraries(bech32_c_api_tests PUBLIC bech32) diff --git a/test/testbech32/bech32_c_api_tests.c b/test/testbech32/bech32_c_api_tests.c index fdc690f..e51e87c 100644 --- a/test/testbech32/bech32_c_api_tests.c +++ b/test/testbech32/bech32_c_api_tests.c @@ -91,7 +91,7 @@ void decode_withBadArgs_isUnsuccessful() { } { // output is null - char bstr[] = "xyz1pzr9dvupm"; + char bstr[] = "xyz1pzr6jnr79"; bech32_HrpAndDp *hrpdp = NULL; @@ -99,7 +99,7 @@ void decode_withBadArgs_isUnsuccessful() { } { // hrp is null - char bstr[] = "xyz1pzr9dvupm"; + char bstr[] = "xyz1pzr6jnr79"; bech32_HrpAndDp *hrpdp = malloc(sizeof(bech32_HrpAndDp)); hrpdp->dplen = 10; @@ -113,7 +113,7 @@ void decode_withBadArgs_isUnsuccessful() { } { // dp is null - char bstr[] = "xyz1pzr9dvupm"; + char bstr[] = "xyz1pzr6jnr79"; bech32_HrpAndDp *hrpdp = malloc(sizeof(bech32_HrpAndDp)); hrpdp->hrplen = 1; @@ -127,7 +127,7 @@ void decode_withBadArgs_isUnsuccessful() { } { // allocated hrp is too short - char bstr[] = "xyz1pzr9dvupm"; + char bstr[] = "xyz1pzr6jnr79"; bech32_HrpAndDp *hrpdp = malloc(sizeof(bech32_HrpAndDp)); hrpdp->hrplen = 1; @@ -143,7 +143,7 @@ void decode_withBadArgs_isUnsuccessful() { } { // allocated dp is too short - char bstr[] = "xyz1pzr9dvupm"; + char bstr[] = "xyz1pzr6jnr79"; bech32_HrpAndDp *hrpdp = malloc(sizeof(bech32_HrpAndDp)); hrpdp->hrplen = 10; @@ -160,7 +160,7 @@ void decode_withBadArgs_isUnsuccessful() { } void decode_minimalExample_isSuccessful() { - char bstr[] = "a12uel5l"; + char bstr[] = "a14rxqtp"; char expectedHrp[] = "a"; bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); @@ -172,7 +172,7 @@ void decode_minimalExample_isSuccessful() { } void decode_longExample_isSuccessful() { - char bstr[] = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; + char bstr[] = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; char expectedHrp[] = "abcdef"; bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); @@ -185,6 +185,17 @@ void decode_longExample_isSuccessful() { free_HrpAndDp_storage(hrpdp); } +void decode_minimalExampleBadChecksum_isUnsuccessful() { + char bstr[] = "a14rxqtq"; // last 'q' should be a 'p' + char expectedHrp[] = "a"; + + bech32_HrpAndDp * hrpdp = create_HrpAndDp_storage(bstr); + + assert(bech32_decode(hrpdp, bstr, sizeof(bstr)) == E_BECH32_INVALID_CHECKSUM); + + free_HrpAndDp_storage(hrpdp); +} + void decode_whenMethodThrowsException_isUnsuccessful() { // bech32 string can only have HRPs that are 83 chars or less. Attempt to decode a string // with more than 83 chars and make sure that the exception thrown in the C++ code is caught @@ -235,10 +246,19 @@ void encode_withBadArgs_isUnsuccessful() { } +void encode_emptyExample_isUnsuccessful() { + char hrp[] = ""; + unsigned char dp[] = {}; + char expected[] = "a14rxqtp"; + char bstr[sizeof(hrp) + 1 + 6] = {0}; + + assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_UNKNOWN_ERROR); +} + void encode_minimalExample_isSuccessful() { char hrp[] = "a"; unsigned char dp[] = {}; - char expected[] = "a12uel5l"; + char expected[] = "a14rxqtp"; char bstr[sizeof(hrp) + 1 + 6] = {0}; assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS); @@ -248,7 +268,7 @@ void encode_minimalExample_isSuccessful() { void encode_smallExample_isSuccessful() { char hrp[] = "xyz"; unsigned char dp[] = {1,2,3}; - char expected[] = "xyz1pzr9dvupm"; + char expected[] = "xyz1pzr6jnr79"; char bstr[sizeof(hrp) + 1 + sizeof(dp) + 6] = {0}; assert(bech32_encode(bstr, sizeof(bstr), hrp, sizeof(hrp), dp, sizeof(dp)) == E_BECH32_SUCCESS); @@ -267,7 +287,7 @@ void encode_whenMethodThrowsException_isUnsuccessful() { } void decode_and_encode_minimalExample_producesSameResult() { - char bstr1[] = "a12uel5l"; + char bstr1[] = "a14rxqtp"; char expectedHrp[] = "a"; const size_t expectedDpSize = 0; // 0 = num chars after '1', minus 6 for checksum chars @@ -287,7 +307,7 @@ void decode_and_encode_minimalExample_producesSameResult() { } void decode_and_encode_smallExample_producesSameResult() { - char bstr1[] = "xyz1pzr9dvupm"; + char bstr1[] = "xyz1pzr6jnr79"; char expectedHrp[] = "xyz"; const size_t expectedDpSize = 3; // 3 = num chars after '1', minus 6 for checksum chars @@ -307,7 +327,7 @@ void decode_and_encode_smallExample_producesSameResult() { } void decode_and_encode_longExample_producesSameResult() { - char bstr1[] = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; + char bstr1[] = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; char expectedHrp[] = "abcdef"; const size_t expectedDpSize = 32; // 32 = num chars after '1', minus 6 for checksum chars @@ -342,9 +362,11 @@ int main() { decode_whenMethodThrowsException_isUnsuccessful(); decode_minimalExample_isSuccessful(); decode_longExample_isSuccessful(); + decode_minimalExampleBadChecksum_isUnsuccessful(); encode_withBadArgs_isUnsuccessful(); encode_whenMethodThrowsException_isUnsuccessful(); + encode_emptyExample_isUnsuccessful(); encode_minimalExample_isSuccessful(); encode_smallExample_isSuccessful(); diff --git a/test/testbech32/test_Bech32.cpp b/test/testbech32/test_Bech32.cpp index 0e40f0b..4d34add 100644 --- a/test/testbech32/test_Bech32.cpp +++ b/test/testbech32/test_Bech32.cpp @@ -436,37 +436,37 @@ TEST(Bech32Test, polymod) { // check the verifyChecksum method TEST(Bech32Test, verifyChecksum_good) { - std::string data("a12uel5l"); + std::string data("a14rxqtp"); bech32::HrpAndDp b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_TRUE(verifyChecksum(b.hrp, b.dp)); - data = "A12UEL5L"; + data = "A14RXQTP"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_TRUE(verifyChecksum(b.hrp, b.dp)); - data = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; + data = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_TRUE(verifyChecksum(b.hrp, b.dp)); - data = "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w"; + data = "split1checkupstagehandshakeupstreamerranterredcaperred4m6xws"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_TRUE(verifyChecksum(b.hrp, b.dp)); - data = "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs"; + data = "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio155t5hw"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_TRUE(verifyChecksum(b.hrp, b.dp)); - data = "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j"; + data = "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq8c42pv"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); @@ -477,37 +477,37 @@ TEST(Bech32Test, verifyChecksum_good) { // check the verifyChecksum method // these are simply the "good" tests from above with a single character changed TEST(Bech32Test, verifyChecksum_bad) { - std::string data("a12uel5m"); + std::string data("a14rxqtm"); bech32::HrpAndDp b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_FALSE(verifyChecksum(b.hrp, b.dp)); - data = "C12UEL5L"; + data = "C14RXQTP"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_FALSE(verifyChecksum(b.hrp, b.dp)); - data = "abcdefg1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; + data = "abcdefg1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_FALSE(verifyChecksum(b.hrp, b.dp)); - data = "split1dheckupstagehandshakeupstreamerranterredcaperred2y9e3w"; + data = "split1dheckupstagehandshakeupstreamerranterredcaperred4m6xws"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_FALSE(verifyChecksum(b.hrp, b.dp)); - data = "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgt"; + data = "an83characterlonghumanreadablepartthatcontainsthenumber1andhheexcludedcharactersbio155t5hw"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); ASSERT_FALSE(verifyChecksum(b.hrp, b.dp)); - data = "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j"; + data = "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq8c42pc"; b = splitString(data); convertToLowercase(b.hrp); mapDP(b.dp); @@ -516,17 +516,17 @@ TEST(Bech32Test, verifyChecksum_bad) { // check the main bech32 decode method TEST(Bech32Test, decode_good) { - std::string data("a12uel5l"); + std::string data("a14rxqtp"); bech32::HrpAndDp b = bech32::decode(data); ASSERT_EQ(b.hrp, "a"); ASSERT_TRUE(b.dp.empty()); - data = "A12UEL5L"; + data = "A14RXQTP"; b = bech32::decode(data); ASSERT_EQ(b.hrp, "a"); ASSERT_TRUE(b.dp.empty()); - data = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; + data = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; b = bech32::decode(data); ASSERT_EQ(b.hrp, "abcdef"); ASSERT_EQ(b.dp.size(), 32); @@ -536,16 +536,104 @@ TEST(Bech32Test, decode_good) { } TEST(Bech32Test, create_checksum) { - std::string hrp = "A"; + std::string hrp = "a"; std::vector data; std::vector checksum = createChecksum(hrp, data); - ASSERT_EQ(checksum[0], '\b'); - ASSERT_EQ(checksum[1], '\x1e'); - ASSERT_EQ(checksum[2], '\x10'); - ASSERT_EQ(checksum[3], '\b'); - ASSERT_EQ(checksum[4], '\r'); - ASSERT_EQ(checksum[5], '\a'); + ASSERT_EQ(checksum[0], '\x15'); + ASSERT_EQ(checksum[1], '\x03'); + ASSERT_EQ(checksum[2], '\x06'); + ASSERT_EQ(checksum[3], '\x00'); + ASSERT_EQ(checksum[4], '\x0B'); + ASSERT_EQ(checksum[5], '\x01'); + + std::string mapped = mapToCharset(checksum); + + ASSERT_EQ(mapped, "4rxqtp"); + + //// + + hrp = "abcdef"; + data = {'q','p','z','r','y','9','x','8','g','f','2','t','v','d','w','0','s','3','j', + 'n','5','4','k','h','c','e','6','m','u','a','7','l'}; + + mapDP(data); + checksum = createChecksum(hrp, data); + + ASSERT_EQ(checksum[0], '\x04'); + ASSERT_EQ(checksum[1], '\x1F'); + ASSERT_EQ(checksum[2], '\x1F'); + ASSERT_EQ(checksum[3], '\x1F'); + ASSERT_EQ(checksum[4], '\x19'); + ASSERT_EQ(checksum[5], '\x10'); + + mapped = mapToCharset(checksum); + + ASSERT_EQ(mapped, "yllles"); + + //// + + hrp = "split"; + data = {'c','h','e','c','k','u','p','s','t','a','g','e','h','a','n','d','s','h','a', + 'k','e','u','p','s','t','r','e','a','m','e','r','r','a','n','t','e','r','r', + 'e','d','c','a','p','e','r','r','e','d'}; + + mapDP(data); + checksum = createChecksum(hrp, data); + + ASSERT_EQ(checksum[0], '\x15'); + ASSERT_EQ(checksum[1], '\x1B'); + ASSERT_EQ(checksum[2], '\x1A'); + ASSERT_EQ(checksum[3], '\x06'); + ASSERT_EQ(checksum[4], '\x0E'); + ASSERT_EQ(checksum[5], '\x10'); + + mapped = mapToCharset(checksum); + + ASSERT_EQ(mapped, "4m6xws"); + + //// + + hrp = "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio"; + data = {}; + + mapDP(data); + checksum = createChecksum(hrp, data); + + ASSERT_EQ(checksum[0], '\x14'); + ASSERT_EQ(checksum[1], '\x14'); + ASSERT_EQ(checksum[2], '\x0B'); + ASSERT_EQ(checksum[3], '\x14'); + ASSERT_EQ(checksum[4], '\x17'); + ASSERT_EQ(checksum[5], '\x0E'); + + mapped = mapToCharset(checksum); + + ASSERT_EQ(mapped, "55t5hw"); + + //// + + hrp = "1"; + data = {'q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q', + 'q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q', + 'q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q', + 'q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q','q', + 'q','q','q','q','q','q'}; + + mapDP(data); + checksum = createChecksum(hrp, data); + + ASSERT_EQ(checksum[0], '\x07'); + ASSERT_EQ(checksum[1], '\x18'); + ASSERT_EQ(checksum[2], '\x15'); + ASSERT_EQ(checksum[3], '\x0A'); + ASSERT_EQ(checksum[4], '\x01'); + ASSERT_EQ(checksum[5], '\x0C'); + + mapped = mapToCharset(checksum); + + ASSERT_EQ(mapped, "8c42pv"); + } // check the main bech32 encode method @@ -553,29 +641,29 @@ TEST(Bech32Test, encode_good) { std::string hrp = "a"; std::vector data; std::string b = bech32::encode(hrp, data); - ASSERT_EQ(b, "a12uel5l"); + ASSERT_EQ(b, "a14rxqtp"); hrp = "A"; b = bech32::encode(hrp, data); - ASSERT_EQ(b, "a12uel5l"); + ASSERT_EQ(b, "a14rxqtp"); } // check that we can decode and then encode back to the original TEST(Bech32Test, check_decode_encode) { - std::string data("a12uel5l"); + std::string data("a14rxqtp"); bech32::HrpAndDp bs = bech32::decode(data); ASSERT_EQ(bs.hrp, "a"); ASSERT_TRUE(bs.dp.empty()); std::string enc = bech32::encode(bs.hrp, bs.dp); ASSERT_EQ(enc, data); - data = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw"; + data = "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lyllles"; bs = bech32::decode(data); ASSERT_EQ(bs.hrp, "abcdef"); enc = bech32::encode(bs.hrp, bs.dp); ASSERT_EQ(enc, data); - data = "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w"; + data = "split1checkupstagehandshakeupstreamerranterredcaperred4m6xws"; bs = bech32::decode(data); ASSERT_EQ(bs.hrp, "split"); enc = bech32::encode(bs.hrp, bs.dp);