aboutsummaryrefslogtreecommitdiff
path: root/src/key.h
diff options
context:
space:
mode:
authorPieter Wuille <pieter@wuille.net>2023-09-18 12:10:19 -0400
committerPieter Wuille <pieter@wuille.net>2023-09-27 15:05:26 -0400
commit6ef405ddb195bbf1b28a906d8c8bb877f0c17d7b (patch)
tree23752c8b48e61f1ae65a6dd85eb5ac41ee8bf979 /src/key.h
parentd9841a7ac634472c1a9105f81f8e7b55e4bd1a4a (diff)
key: don't allocate secure mem for null (invalid) key
Instead of storing the key material as an std::vector (with secure allocator), use a secure_unique_ptr to a 32-byte array, and use nullptr for invalid keys. This means a smaller CKey type, and no secure/dynamic memory usage for invalid keys.
Diffstat (limited to 'src/key.h')
-rw-r--r--src/key.h60
1 files changed, 40 insertions, 20 deletions
diff --git a/src/key.h b/src/key.h
index 8382b0a670..785059da02 100644
--- a/src/key.h
+++ b/src/key.h
@@ -46,57 +46,77 @@ public:
"COMPRESSED_SIZE is larger than SIZE");
private:
- //! Whether this private key is valid. We check for correctness when modifying the key
- //! data, so fValid should always correspond to the actual state.
- bool fValid{false};
+ /** Internal data container for private key material. */
+ using KeyType = std::array<unsigned char, 32>;
//! Whether the public key corresponding to this private key is (to be) compressed.
bool fCompressed{false};
- //! The actual byte data
- std::vector<unsigned char, secure_allocator<unsigned char> > keydata;
+ //! The actual byte data. nullptr for invalid keys.
+ secure_unique_ptr<KeyType> keydata;
//! Check whether the 32-byte array pointed to by vch is valid keydata.
bool static Check(const unsigned char* vch);
+ void MakeKeyData()
+ {
+ if (!keydata) keydata = make_secure_unique<KeyType>();
+ }
+
+ void ClearKeyData()
+ {
+ keydata.reset();
+ }
+
public:
- //! Construct an invalid private key.
- CKey()
+ CKey() noexcept = default;
+ CKey(CKey&&) noexcept = default;
+ CKey& operator=(CKey&&) noexcept = default;
+
+ CKey& operator=(const CKey& other)
{
- // Important: vch must be 32 bytes in length to not break serialization
- keydata.resize(32);
+ if (other.keydata) {
+ MakeKeyData();
+ *keydata = *other.keydata;
+ } else {
+ ClearKeyData();
+ }
+ fCompressed = other.fCompressed;
+ return *this;
}
+ CKey(const CKey& other) { *this = other; }
+
friend bool operator==(const CKey& a, const CKey& b)
{
return a.fCompressed == b.fCompressed &&
a.size() == b.size() &&
- memcmp(a.keydata.data(), b.keydata.data(), a.size()) == 0;
+ memcmp(a.data(), b.data(), a.size()) == 0;
}
//! Initialize using begin and end iterators to byte data.
template <typename T>
void Set(const T pbegin, const T pend, bool fCompressedIn)
{
- if (size_t(pend - pbegin) != keydata.size()) {
- fValid = false;
+ if (size_t(pend - pbegin) != std::tuple_size_v<KeyType>) {
+ ClearKeyData();
} else if (Check(&pbegin[0])) {
- memcpy(keydata.data(), (unsigned char*)&pbegin[0], keydata.size());
- fValid = true;
+ MakeKeyData();
+ memcpy(keydata->data(), (unsigned char*)&pbegin[0], keydata->size());
fCompressed = fCompressedIn;
} else {
- fValid = false;
+ ClearKeyData();
}
}
//! Simple read-only vector-like interface.
- unsigned int size() const { return (fValid ? keydata.size() : 0); }
- const std::byte* data() const { return reinterpret_cast<const std::byte*>(keydata.data()); }
- const unsigned char* begin() const { return keydata.data(); }
- const unsigned char* end() const { return keydata.data() + size(); }
+ unsigned int size() const { return keydata ? keydata->size() : 0; }
+ const std::byte* data() const { return keydata ? reinterpret_cast<const std::byte*>(keydata->data()) : nullptr; }
+ const unsigned char* begin() const { return keydata ? keydata->data() : nullptr; }
+ const unsigned char* end() const { return begin() + size(); }
//! Check whether this private key is valid.
- bool IsValid() const { return fValid; }
+ bool IsValid() const { return !!keydata; }
//! Check whether the public key corresponding to this private key is (to be) compressed.
bool IsCompressed() const { return fCompressed; }