blobs for keys in db

This commit is contained in:
Sean Darcy 2023-03-14 08:43:07 +11:00
parent 3648ef28b2
commit c95806223d
7 changed files with 91 additions and 26 deletions

View file

@ -154,7 +154,7 @@ T make_from_guts(std::string_view s) {
if (s.size() != sizeof(T))
throw std::runtime_error("Cannot reconstitute type: wrong type content size");
T x;
std::memcpy(&x, s.data(), sizeof(T));
std::memcpy(static_cast<void*>(&x), s.data(), sizeof(T));
return x;
}

View file

@ -16,6 +16,7 @@
#include <string_view>
#include "common/fs.h"
#include <common/string_util.h>
namespace db
{
@ -50,6 +51,36 @@ namespace db
st.bindNoCopy(i, static_cast<const void*>(blob.data()), blob.size());
}
// Wrapper for extracting BLOB values from a query without unnecessary copying. This is intended
// to be called via `db::get` such as:
//
// auto [num, data] = db::get<int, blob>(st);
//
// The `.data` string view here will point to the BLOB data directly. Note that this view remains
// only value while the statement remains active, and so it must be used as needed immediately.
// This also means that this is *unsuitable* for one-shot methods like `prepared_get` (which
// finalize the statement before returning).
struct blob {
std::string_view data;
blob(SQLite::Column&& col)
: data{static_cast<const char*>(col.getBlob()), static_cast<size_t>(col.getBytes())} {}
};
// Takes a primitive struct from which we can directly initialize from the stored blob value. The
// type `T` must be usable with `make_from_guts`. Unlike `blob` this value *is* suitable for use
// in a one-shot method.
template <typename T>
struct blob_guts {
T value;
blob_guts(SQLite::Column&& col)
: value{tools::make_from_guts<T>(blob(std::move(col)).data)} {}
// Implicit rvalue-convertible to `T&&` so that you can use it somewhat transparently, for
// example, allowing implicit conversion from a `std::tuple<..., blob_guts<T>>` into a
// `std::tuple<..., T>`.
operator T&&() && { return std::move(value); }
};
namespace detail
{
template <typename T>

View file

@ -4,6 +4,7 @@
#include "block.hpp"
#include <common/hex.h>
#include <common/string_util.h>
#include <cryptonote_basic/cryptonote_basic.h>
#include <fmt/core.h>
@ -426,27 +427,39 @@ namespace wallet
return prepared_get<int64_t>("SELECT output_count FROM metadata WHERE id=0;");
}
void
WalletDB::save_keys(const std::string& spend_priv_str, const std::string& spend_pub_str, const std::string& view_priv_str, const std::string& view_pub_str)
WalletDB::save_keys(const std::shared_ptr<WalletKeys> keys)
{
const auto [db_spend_priv_str, db_spend_pub_str, db_view_priv_str, db_view_pub_str] = load_keys();
const auto maybe_db_keys = load_keys();
if (maybe_db_keys.has_value())
{
if ((tools::view_guts(maybe_db_keys->spend_privkey()) != tools::view_guts(keys->spend_privkey())) ||
(tools::view_guts(maybe_db_keys->spend_pubkey()) != tools::view_guts(keys->spend_pubkey())) ||
(tools::view_guts(maybe_db_keys->view_privkey()) != tools::view_guts(keys->view_privkey())) ||
(tools::view_guts(maybe_db_keys->view_pubkey()) != tools::view_guts(keys->view_pubkey())))
throw std::runtime_error("provided keys do not match database file");
}
if ((not db_spend_priv_str.empty() && db_spend_priv_str != spend_priv_str) ||
(not db_spend_pub_str.empty() && db_spend_pub_str != spend_pub_str) ||
(not db_view_priv_str.empty() && db_view_priv_str != view_priv_str) ||
(not db_view_pub_str.empty() && db_view_pub_str != view_pub_str))
throw std::runtime_error("provided keys do not match database file");
prepared_exec("UPDATE metadata SET spend_priv = ?, spend_pub = ?, view_priv = ?, view_pub = ? where id = 0;",
spend_priv_str,
spend_pub_str,
view_priv_str,
view_pub_str);
std::string(tools::view_guts(keys->spend_privkey())),
std::string(tools::view_guts(keys->spend_pubkey())),
std::string(tools::view_guts(keys->view_privkey())),
std::string(tools::view_guts(keys->view_pubkey())));
}
std::tuple<std::string, std::string, std::string, std::string>
std::optional<DBKeys>
WalletDB::load_keys()
{
return prepared_get<std::string, std::string, std::string, std::string>("SELECT spend_priv, spend_pub, view_priv, view_pub FROM metadata WHERE id=0");
DBKeys keys;
int32_t spend_key_exists = prepared_get<int32_t>("SELECT count(spend_priv) FROM metadata WHERE spend_priv IS NOT ''");
if (not spend_key_exists)
return std::nullopt;
auto x = prepared_get<db::blob_guts<crypto::secret_key>, db::blob_guts<crypto::public_key>, db::blob_guts<crypto::secret_key>, db::blob_guts<crypto::public_key>>(
"SELECT spend_priv, spend_pub, view_priv, view_pub FROM metadata WHERE id=0");
keys.ssk = std::get<0>(x).value;
keys.spk = std::get<1>(x).value;
keys.vsk = std::get<2>(x).value;
keys.vpk = std::get<3>(x).value;
return keys;
}
} // namespace wallet

View file

@ -4,6 +4,7 @@
#include <sqlitedb/database.hpp>
#include "output.hpp"
#include "walletkeys.hpp"
#include <optional>
@ -95,10 +96,10 @@ namespace wallet
// Saves keys to the database, will check if keys match if already exists and throw if different
void
save_keys(const std::string& spend_priv_str, const std::string& spend_pub_str, const std::string& view_priv_str, const std::string& view_pub_str);
save_keys(const std::shared_ptr<WalletKeys> keys);
// Loads keys from an already created database
std::tuple<std::string, std::string, std::string, std::string>
std::optional<DBKeys>
load_keys();
};
}

View file

@ -10,10 +10,11 @@
#include <optional>
#include "pending_transaction.hpp"
#include "walletkeys.hpp"
namespace wallet
{
class Keyring
class Keyring : public WalletKeys
{
public:
Keyring(
@ -144,6 +145,11 @@ namespace wallet
crypto::secret_key view_private_key;
crypto::public_key view_public_key;
const crypto::secret_key& spend_privkey() const override { return spend_private_key; }
const crypto::public_key& spend_pubkey() const override { return spend_public_key; }
const crypto::secret_key& view_privkey() const override { return view_private_key; }
const crypto::public_key& view_pubkey() const override { return view_public_key; }
private:
hw::core::device_default key_device;

View file

@ -69,17 +69,12 @@ namespace wallet
if (keyring)
{
keys = keyring;
db->save_keys(
tools::type_to_hex(keys->spend_private_key),
tools::type_to_hex(keys->spend_public_key),
tools::type_to_hex(keys->view_private_key),
tools::type_to_hex(keys->view_public_key)
);
db->save_keys(keys);
}
else
{
const auto [spend_priv, spend_pub, view_priv, view_pub] = db->load_keys();
keys = std::make_shared<wallet::Keyring>(spend_priv, spend_pub, view_priv, view_pub, nettype);
const auto db_keys = db->load_keys();
keys = std::make_shared<wallet::Keyring>(db_keys->spend_privkey(), db_keys->spend_pubkey(), db_keys->view_privkey(), db_keys->view_pubkey(), nettype);
tx_scanner = TransactionScanner(keys, db);
}
db->add_address(0, 0, keys->get_main_address());

View file

@ -0,0 +1,19 @@
#pragma once
class WalletKeys {
public:
virtual const crypto::secret_key& spend_privkey() const = 0;
virtual const crypto::public_key& spend_pubkey() const = 0;
virtual const crypto::secret_key& view_privkey() const = 0;
virtual const crypto::public_key& view_pubkey() const = 0;
};
struct DBKeys : public WalletKeys {
crypto::secret_key ssk, vsk;
crypto::public_key spk, vpk;
const crypto::secret_key& spend_privkey() const override { return ssk; }
const crypto::public_key& spend_pubkey() const override { return spk; }
const crypto::secret_key& view_privkey() const override { return vsk; }
const crypto::public_key& view_pubkey() const override { return vpk; }
};