mirror of
https://github.com/vtnerd/monero-lws.git
synced 2025-04-02 20:09:03 +00:00
Allow subaddresses in place of addresses in API
caveat: a user can see if another user's subaddress is stored on the server by querying with a bad view key
This commit is contained in:
parent
6706aad5c7
commit
bfc3df8dcf
5 changed files with 38 additions and 18 deletions
|
@ -59,7 +59,7 @@ namespace db
|
|||
template<typename F, typename T>
|
||||
void map_account_address(F& format, T& self)
|
||||
{
|
||||
wire::object(format, WIRE_FIELD(spend_public), WIRE_FIELD(view_public));
|
||||
wire::object(format, WIRE_FIELD(spend_public), WIRE_FIELD(view_public), WIRE_FIELD(is_subaddress));
|
||||
}
|
||||
}
|
||||
WIRE_DEFINE_OBJECT(account_address, map_account_address);
|
||||
|
@ -221,5 +221,13 @@ namespace db
|
|||
std::memcmp(std::addressof(left.tx_hash), std::addressof(right.tx_hash), sizeof(left.tx_hash)) <= 0 :
|
||||
left.height < right.height;
|
||||
}
|
||||
|
||||
crypto::secret_key get_secret_view_key(const lws::db::view_key key)
|
||||
{
|
||||
crypto::secret_key copy{};
|
||||
static_assert(sizeof(copy) == sizeof(key), "bad memcpy");
|
||||
std::memcpy(std::addressof(unwrap(copy)), std::addressof(key), sizeof(copy));
|
||||
return copy;
|
||||
}
|
||||
} // db
|
||||
} // lws
|
||||
|
|
|
@ -100,14 +100,17 @@ namespace db
|
|||
|
||||
struct view_key : crypto::ec_scalar {};
|
||||
// wire::is_blob trait below
|
||||
crypto::secret_key get_secret_view_key(const lws::db::view_key key);
|
||||
|
||||
//! The public keys of a monero address
|
||||
struct account_address
|
||||
{
|
||||
crypto::public_key view_public; //!< Must be first for LMDB optimizations.
|
||||
crypto::public_key spend_public;
|
||||
bool is_subaddress;
|
||||
char reserved[7];
|
||||
};
|
||||
static_assert(sizeof(account_address) == 64, "padding in account_address");
|
||||
static_assert(sizeof(account_address) == 64 + 1 + 7, "padding in account_address");
|
||||
WIRE_DECLARE_OBJECT(account_address);
|
||||
|
||||
struct account
|
||||
|
@ -122,7 +125,7 @@ namespace db
|
|||
account_flags flags; //!< Additional account info bitmask.
|
||||
char reserved[3];
|
||||
};
|
||||
static_assert(sizeof(account) == (4 * 2) + 64 + 32 + (8 * 2) + (4 * 2), "padding in account");
|
||||
static_assert(sizeof(account) == (4 * 2) + 64 + 1 + 7 + 32 + (8 * 2) + (4 * 2), "padding in account");
|
||||
void write_bytes(wire::writer&, const account&, bool show_key = false);
|
||||
|
||||
struct block_info
|
||||
|
@ -234,7 +237,7 @@ namespace db
|
|||
account_flags creation_flags; //!< Generated locally?
|
||||
char reserved[3];
|
||||
};
|
||||
static_assert(sizeof(request_info) == 64 + 32 + 8 + (4 * 2), "padding in request_info");
|
||||
static_assert(sizeof(request_info) == 64 + 1 + 7 + 32 + 8 + (4 * 2), "padding in request_info");
|
||||
void write_bytes(wire::writer& dest, const request_info& self, bool show_key = false);
|
||||
|
||||
inline constexpr bool operator==(output_id left, output_id right) noexcept
|
||||
|
|
|
@ -76,7 +76,7 @@ namespace db
|
|||
account_address address; //!< Must be first for LMDB optimizations
|
||||
account_lookup lookup;
|
||||
};
|
||||
static_assert(sizeof(account_by_address) == 64 + 4 + 1 + 3, "padding in account_by_address");
|
||||
static_assert(sizeof(account_by_address) == 64 + 1 + 7 + 4 + 1 + 3, "padding in account_by_address");
|
||||
|
||||
constexpr const unsigned blocks_version = 0;
|
||||
constexpr const unsigned by_address_version = 0;
|
||||
|
@ -1196,13 +1196,10 @@ namespace db
|
|||
{
|
||||
expect<void> do_add_account(MDB_cursor& accounts_cur, MDB_cursor& accounts_ba_cur, MDB_cursor& accounts_bh_cur, account const& user) noexcept
|
||||
{
|
||||
if (!user.address.is_subaddress)
|
||||
{
|
||||
crypto::secret_key copy{};
|
||||
crypto::secret_key copy = get_secret_view_key(user.key);
|
||||
crypto::public_key verify{};
|
||||
static_assert(sizeof(copy) == sizeof(user.key), "bad memcpy");
|
||||
std::memcpy(
|
||||
std::addressof(unwrap(copy)), std::addressof(user.key), sizeof(copy)
|
||||
);
|
||||
|
||||
if (!crypto::secret_key_to_public_key(copy, verify))
|
||||
return {lws::error::bad_view_key};
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace db
|
|||
address.spend_public, address.view_public
|
||||
};
|
||||
return cryptonote::get_account_address_as_str(
|
||||
lws::config::network, false, address_
|
||||
lws::config::network, address.is_subaddress, address_
|
||||
);
|
||||
}
|
||||
expect<account_address>
|
||||
|
@ -51,11 +51,11 @@ namespace db
|
|||
|
||||
if (!cryptonote::get_account_address_from_str(info, lws::config::network, std::string{address}))
|
||||
return {lws::error::bad_address};
|
||||
if (info.is_subaddress || info.has_payment_id)
|
||||
if (info.has_payment_id)
|
||||
return {lws::error::bad_address};
|
||||
|
||||
return account_address{
|
||||
info.address.m_view_public_key, info.address.m_spend_public_key
|
||||
info.address.m_view_public_key, info.address.m_spend_public_key, info.is_subaddress
|
||||
};
|
||||
}
|
||||
} // db
|
||||
|
|
|
@ -106,11 +106,19 @@ namespace lws
|
|||
|
||||
bool key_check(const rpc::account_credentials& creds)
|
||||
{
|
||||
crypto::public_key verify{};
|
||||
if (!crypto::secret_key_to_public_key(creds.key, verify))
|
||||
return false;
|
||||
if (verify != creds.address.view_public)
|
||||
return false;
|
||||
// key check on subaddresses here is not possible because need the primary
|
||||
// public spend key (B) in order to derive subaddresses (C = a*D, D = B + m*G)
|
||||
// [1]: https://monerodocs.org/public-address/subaddress/#private-view-key
|
||||
// [2]: https://github.com/monero-project/monero/blob/319b831e65437f1c8e5ff4b4cb9be03f091f6fc6/src/device/device_default.cpp#L127-L208
|
||||
// [3]: https://monero.stackexchange.com/questions/10674/how-are-subaddresses-and-account-addresses-generated-from-master-wallet-keys/10676#10676
|
||||
if (!creds.address.is_subaddress)
|
||||
{
|
||||
crypto::public_key verify{};
|
||||
if (!crypto::secret_key_to_public_key(creds.key, verify))
|
||||
return false;
|
||||
if (verify != creds.address.view_public)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -129,6 +137,8 @@ namespace lws
|
|||
return user.error();
|
||||
if (is_hidden(user->first))
|
||||
return {lws::error::account_not_found};
|
||||
if (get_secret_view_key(user->second.key) != creds.key)
|
||||
return {lws::error::bad_view_key};
|
||||
return {std::make_pair(user->second, std::move(*reader))};
|
||||
}
|
||||
|
||||
|
@ -611,6 +621,8 @@ namespace lws
|
|||
{
|
||||
if (is_hidden(account->first))
|
||||
return {lws::error::account_not_found};
|
||||
if (get_secret_view_key(account->second.key) != req.creds.key)
|
||||
return {lws::error::bad_view_key};
|
||||
|
||||
// Do not count a request for account creation as login
|
||||
return response{false, bool(account->second.flags & db::account_generated_locally)};
|
||||
|
|
Loading…
Reference in a new issue