diff --git a/src/secrets.cpp b/src/secrets.cpp index 07f3567..37d41b0 100644 --- a/src/secrets.cpp +++ b/src/secrets.cpp @@ -63,15 +63,32 @@ ristretto255::scalar DeriveSecret(const scalar &sc, uint_fast64_t i) { } namespace ristretto255 { - extended_private_key::extended_private_key(extended_private_key& parent, std::span path) noexcept: + extended_public_key::extended_public_key(point&& p, hash<256>&& h) : + publickey(p), + chain(h) { + }; + extended_private_key::extended_private_key(scalar&& s, hash<256>&& h) : + privatekey(s), + chain(h) { + }; + extended_private_key::~extended_private_key()noexcept { + wxSecretValue::Wipe(sizeof(*this), this); + }; + extended_public_key::~extended_public_key()noexcept { + wxSecretValue::Wipe(sizeof(*this), this); + }; + extended_private_key::extended_private_key( + extended_private_key& parent, + std::span path + ) noexcept: privatekey(parent.privatekey* scalar(hash<512>(parent.chain, path))), - chain(parent.chain, path) - {}; + chain(parent.chain, path){ + }; extended_private_key::extended_private_key(hash<512>&& s) noexcept : privatekey(s), - chain(s) - {}; + chain(s){ + }; extended_private_key::extended_private_key(const char* const passwd) noexcept : extended_private_key(DeriveStrongSecretHash(passwd)) diff --git a/src/secrets.h b/src/secrets.h index b3ffad8..466ff71 100644 --- a/src/secrets.h +++ b/src/secrets.h @@ -14,6 +14,7 @@ constexpr int rounded_log2(const T val) noexcept { } namespace ristretto255 { + class extended_public_key; class extended_private_key { /* This is the same name and concept as BIP0032 and BIP0044 extended private and public keys, and used for the same purpose, to deterministically @@ -30,22 +31,37 @@ namespace ristretto255 { for the path, but since I want to allow names, among other things, I use spans of bytes. */ extended_private_key(hash<512>&&)noexcept; + extended_private_key(scalar&&, hash<256>&&); public: + friend class extended_public_key; scalar privatekey; hash<256> chain; static constexpr unsigned int type_indentifier = 3; - public: - extended_private_key()=default; + extended_private_key() = default; extended_private_key(extended_private_key& parent, std::span path) noexcept; extended_private_key(const char* const passwd) noexcept; - ~extended_private_key()noexcept { wxSecretValue::Wipe(sizeof(*this), this); } - template auto operator()(T psz) { - return privatekey*scalar(hash<512>(chain, psz)); + ~extended_private_key()noexcept; + template scalar operator()(T psz) { + return privatekey * scalar(hash<512>(chain, psz)); + } + template scalar child_private_key(T psz) { + return this->operator()(psz); + } + template point child_public_key(T psz) { + return (this->child_private_key(psz)).timesBase(); + } + template auto child_extended_private_key(T psz) { + return extended_private_key(this->child_private_key(psz), hash<256>(chain, psz)); + } + template extended_public_key child_extended_public_key(T psz); + bool operator==(const extended_private_key& pt) const& { + return this->privatekey == pt.privatekey && this->chain == pt.chain; } - bool operator==(const extended_private_key& pt) const& { return this->privatekey == pt.privatekey && this->chain == pt.chain; } }; - - static_assert(sizeof(extended_private_key) == sizeof(hash<256>) + sizeof(scalar)); + static_assert( + sizeof(extended_private_key) == sizeof(hash<256>) + sizeof(scalar), + "assuming efficient packing when we convert to and from base58" + ); class extended_public_key { /* This is the same name and concept as BIP0032 and BIP0044 extended private @@ -53,22 +69,36 @@ namespace ristretto255 { create a hierarchical tree of secrets each defined by a path from the master extended key, but utterly incompatible. */ + extended_public_key(point&&, hash<256>&&); public: + friend class extended_private_key; point publickey; hash<256> chain; static constexpr unsigned int type_indentifier = 4; extended_public_key()=default; extended_public_key(extended_public_key& parent, std::span path) noexcept; extended_public_key(extended_private_key&) noexcept; - ~extended_public_key()noexcept { wxSecretValue::Wipe(sizeof(*this), this); } - template auto operator()(T psz) { - scalar& t(*this); + ~extended_public_key()noexcept; + template point operator()(T psz) { return publickey * scalar(hash<512>(chain, psz)); } - bool operator==(const extended_public_key& pt) const& { return this->publickey == pt.publickey && this->chain == pt.chain; } + template point child_public_key(T psz) { + return this->operator()(psz); + } + template auto child_extended_public_key(T psz) { + return extended_public_key(this->child_public_key(psz), hash<256>(chain, psz)); + } + bool operator==(const extended_public_key& pt) const& { + return this->publickey == pt.publickey && this->chain == pt.chain; + } }; - - static_assert(sizeof(extended_public_key) == sizeof(hash<256>) + sizeof(point)); + static_assert( + sizeof(extended_public_key) == sizeof(hash<256>) + sizeof(point), + "assuming efficient packing when we convert to and from base58" + ); + template extended_public_key extended_private_key::child_extended_public_key(T psz) { + return extended_public_key(this->child_public_key(psz), hash<256>(chain, psz)); + } std::span serialize(const extended_private_key& pt); std::span serialize(const extended_public_key& pt); } diff --git a/src/unit_test.cpp b/src/unit_test.cpp index 3fac73e..00ba51e 100644 --- a/src/unit_test.cpp +++ b/src/unit_test.cpp @@ -651,6 +651,12 @@ namespace ristretto255 { if (s1 != s2) { throw MyException("Round trip for extended_private_key to and from base 58 representation failed", __LINE__, __func__, SrcFilename); } + auto time_taken{ std::chrono::duration_cast (end_time - start_time) }; + wxLogMessage( + "\t\tStrong secret extended private key derivation took %lld microseconds", + time_taken.count() + ); + /* test conversion of public keys to Base58 and expected results*/ extended_public_key p1(s1); std::string str_p1((char*)base58(p1)); wxLogMessage("\t\textended public key: %s", str_p1); @@ -662,8 +668,26 @@ namespace ristretto255 { if (p1 != p2) { throw MyException("Round trip for extended_public_key to and from base 58 representation failed", __LINE__, __func__, SrcFilename); } - auto time_taken{ std::chrono::duration_cast (end_time - start_time) }; - wxLogMessage("\t\tStrong secret extended private key derivation took %lld microseconds", time_taken.count()); + { // test if public extended key tree follows private extended key tree + std::array path1, path2; + randombytes_buf(&path1[0], size(path1)); + auto p1_path1 = s1.child_extended_public_key(path1); + auto s1_path1 = s1.child_extended_private_key(path1); + randombytes_buf(&path2[0], size(path2)); + auto sk_path1_path2 = s1_path1.child_private_key(path2); + auto pk_path1_path2 = s1_path1.child_public_key(path2); + if( + p1_path1 != extended_public_key(s1_path1) || + p1_path1 != p1.child_extended_public_key(path1) || + pk_path1_path2 != sk_path1_path2.timesBase() || + pk_path1_path2 != p1_path1.child_public_key(path2) + ) { + throw MyException( + "public and private extended key derivation fails to parallel", + __LINE__, __func__, SrcFilename + ); + } + } } } else {