diff --git a/include/mcl/conversion.hpp b/include/mcl/conversion.hpp index b5faa50..5ba7de0 100644 --- a/include/mcl/conversion.hpp +++ b/include/mcl/conversion.hpp @@ -131,23 +131,31 @@ inline uint32_t decToU32(const char *p, size_t size, bool *pb) return x; } +inline bool hexCharToUint8(uint8_t *v, char _c) +{ + uint32_t c = uint8_t(_c); // cast is necessary + if (c - '0' <= '9' - '0') { + c = c - '0'; + } else if (c - 'a' <= 'f' - 'a') { + c = (c - 'a') + 10; + } else if (c - 'A' <= 'F' - 'A') { + c = (c - 'A') + 10; + } else { + return false; + } + *v = c; + return true; +} + template bool hexToUint(UT *px, const char *p, size_t size) { assert(0 < size && size <= sizeof(UT) * 2); UT x = 0; for (size_t i = 0; i < size; i++) { - UT c = static_cast(p[i]); - if (c - 'A' <= 'F' - 'A') { - c = (c - 'A') + 10; - } else if (c - 'a' <= 'f' - 'a') { - c = (c - 'a') + 10; - } else if (c - '0' <= '9' - '0') { - c = c - '0'; - } else { - return false; - } - x = x * 16 + c; + uint8_t v; + if (!hexCharToUint8(&v, p[i])) return false; + x = x * 16 + v; } *px = x; return true; @@ -438,4 +446,43 @@ size_t strToArray(bool *pIsMinus, UT *x, size_t xN, const char *buf, size_t bufS } } +/* + convert src[0, n) to (n * 2) byte hex string and write it to os + return true if success else flase +*/ +template +bool writeHexStr(OutputStream& os, const void *src, size_t n) +{ + bool b; + const uint8_t *p = (const uint8_t *)src; + for (size_t i = 0; i < n; i++) { + char hex[2]; + cybozu::itohex(hex, sizeof(hex), p[i], false); + cybozu::write(&b, os, hex, sizeof(hex)); + if (!b) return false; + } + return true; +} +/* + read hex string from is and convert it to byte array + return written buffer size +*/ +template +inline size_t readHexStr(void *buf, size_t n, InputStream& is) +{ + bool b; + uint8_t *dst = (uint8_t *)buf; + for (size_t i = 0; i < n; i++) { + uint8_t L, H; + char c[2]; + if (cybozu::readSome(c, sizeof(c), is) != sizeof(c)) return i; + b = local::hexCharToUint8(&H, c[0]); + if (!b) return i; + b = local::hexCharToUint8(&L, c[1]); + if (!b) return i; + dst[i] = (H << 4) | L; + } + return n; +} + } } // mcl::fp diff --git a/include/mcl/op.hpp b/include/mcl/op.hpp index b0581bf..56c1389 100644 --- a/include/mcl/op.hpp +++ b/include/mcl/op.hpp @@ -101,7 +101,8 @@ enum IoMode { IoEcCompY = 256, // 1-bit y representation of elliptic curve IoSerialize = 512, // use MBS for 1-bit y IoFixedSizeByteSeq = IoSerialize, // obsolete - IoEcProj = 1024 // projective or jacobi coordinate + IoEcProj = 1024, // projective or jacobi coordinate + IoSerializeHexStr = 2048 // printable hex string }; namespace fp { diff --git a/test/conversion_test.cpp b/test/conversion_test.cpp index 0f39907..06f31af 100644 --- a/test/conversion_test.cpp +++ b/test/conversion_test.cpp @@ -55,3 +55,40 @@ CYBOZU_TEST_AUTO(arrayToDec) CYBOZU_TEST_EQUAL_ARRAY(xx, x, xn); } } + +CYBOZU_TEST_AUTO(writeHexStr) +{ + const char *hex1tbl = "0123456789abcdef"; + const char *hex2tbl = "0123456789ABCDEF"; + for (size_t i = 0; i < 16; i++) { + uint8_t v = 0xff; + CYBOZU_TEST_ASSERT(mcl::fp::local::hexCharToUint8(&v, hex1tbl[i])); + CYBOZU_TEST_EQUAL(v, i); + CYBOZU_TEST_ASSERT(mcl::fp::local::hexCharToUint8(&v, hex2tbl[i])); + CYBOZU_TEST_EQUAL(v, i); + } + const struct Tbl { + const char *bin; + size_t n; + const char *hex; + } tbl[] = { + { "", 0, "" }, + { "\x12\x34\xab", 3, "1234ab" }, + { "\xff\xfc\x00\x12", 4, "fffc0012" }, + }; + for (size_t i = 0; i < CYBOZU_NUM_OF_ARRAY(tbl); i++) { + char buf[32]; + cybozu::MemoryOutputStream os(buf, sizeof(buf)); + const char *bin = tbl[i].bin; + const char *hex = tbl[i].hex; + size_t n = tbl[i].n; + CYBOZU_TEST_ASSERT(mcl::fp::writeHexStr(os, bin, n)); + CYBOZU_TEST_EQUAL(os.getPos(), n * 2); + CYBOZU_TEST_EQUAL_ARRAY(buf, hex, n * 2); + + cybozu::MemoryInputStream is(hex, n * 2); + size_t w = mcl::fp::readHexStr(buf, n, is); + CYBOZU_TEST_EQUAL(w, n); + CYBOZU_TEST_EQUAL_ARRAY(buf, bin, n); + } +}