Allow channel_verify to specify public key in hex (RIPD-1467)

This commit is contained in:
seelabs
2017-07-28 14:29:41 -04:00
committed by Nikolaos D. Bougalis
parent ad4ba44394
commit a307d2d03f
3 changed files with 224 additions and 49 deletions

View File

@@ -19,6 +19,7 @@
#include <BeastConfig.h>
#include <ripple/app/main/Application.h>
#include <ripple/basics/StringUtilities.h>
#include <ripple/net/RPCCall.h>
#include <ripple/net/RPCErr.h>
#include <ripple/basics/contract.h>
@@ -37,6 +38,7 @@
#include <ripple/beast/core/LexicalCast.h>
#include <beast/core/string.hpp>
#include <boost/asio/streambuf.hpp>
#include <boost/optional.hpp>
#include <boost/regex.hpp>
#include <array>
#include <iostream>
@@ -82,6 +84,33 @@ std::string createHTTPPost (
return s.str ();
}
static
boost::optional<std::uint64_t>
to_uint64(std::string const& s)
{
if (s.empty())
return boost::none;
for (auto c : s)
{
if (!isdigit(c))
return boost::none;
}
try
{
std::size_t pos{};
auto const drops = std::stoul(s, &pos);
if (s.size() != pos)
return boost::none;
return drops;
}
catch (std::exception const&)
{
return boost::none;
}
}
class RPCParser
{
private:
@@ -680,16 +709,9 @@ private:
}
jvRequest[jss::channel_id] = jvParams[1u].asString ();
try
{
auto const drops = std::stoul (jvParams[2u].asString ());
(void)drops; // just used for error checking
jvRequest[jss::amount] = jvParams[2u];
}
catch (std::exception const&)
{
return rpcError (rpcCHANNEL_AMT_MALFORMED);
}
if (!jvParams[2u].isString() || !to_uint64(jvParams[2u].asString()))
return rpcError(rpcCHANNEL_AMT_MALFORMED);
jvRequest[jss::amount] = jvParams[2u];
return jvRequest;
}
@@ -699,7 +721,21 @@ private:
{
std::string const strPk = jvParams[0u].asString ();
if (!parseBase58<PublicKey> (TokenType::TOKEN_ACCOUNT_PUBLIC, strPk))
bool const validPublicKey = [&strPk]{
if (parseBase58<PublicKey> (TokenType::TOKEN_ACCOUNT_PUBLIC, strPk))
return true;
std::pair<Blob, bool> pkHex(strUnHex (strPk));
if (!pkHex.second)
return false;
if (!publicKeyType(makeSlice(pkHex.first)))
return false;
return true;
}();
if (!validPublicKey)
return rpcError (rpcPUBLIC_MALFORMED);
Json::Value jvRequest (Json::objectValue);
@@ -712,16 +748,11 @@ private:
return rpcError (rpcCHANNEL_MALFORMED);
}
jvRequest[jss::channel_id] = jvParams[1u].asString ();
try
{
auto const drops = std::stoul (jvParams[2u].asString ());
(void)drops; // just used for error checking
jvRequest[jss::amount] = jvParams[2u];
}
catch (std::exception const&)
{
return rpcError (rpcCHANNEL_AMT_MALFORMED);
}
if (!jvParams[2u].isString() || !to_uint64(jvParams[2u].asString()))
return rpcError(rpcCHANNEL_AMT_MALFORMED);
jvRequest[jss::amount] = jvParams[2u];
jvRequest[jss::signature] = jvParams[3u].asString ();
return jvRequest;

View File

@@ -31,8 +31,37 @@
#include <ripple/rpc/impl/RPCHelpers.h>
#include <ripple/rpc/impl/Tuning.h>
#include <boost/optional.hpp>
namespace ripple {
static
boost::optional<std::uint64_t>
to_uint64(std::string const& s)
{
if (s.empty())
return boost::none;
for (auto c : s)
{
if (!isdigit(c))
return boost::none;
}
try
{
std::size_t pos{};
auto const drops = std::stoul(s, &pos);
if (s.size() != pos)
return boost::none;
return drops;
}
catch (std::exception const&)
{
return boost::none;
}
}
// {
// secret_key: <signing_secret_key>
// channel_id: 256-bit channel id
@@ -54,15 +83,15 @@ Json::Value doChannelAuthorize (RPC::Context& context)
if (!channelId.SetHexExact (params[jss::channel_id].asString ()))
return rpcError (rpcCHANNEL_MALFORMED);
std::uint64_t drops = 0;
try
{
drops = std::stoul (params[jss::amount].asString ());
}
catch (std::exception const&)
{
boost::optional<std::uint64_t> const optDrops =
params[jss::amount].isString()
? to_uint64(params[jss::amount].asString())
: boost::none;
if (!optDrops)
return rpcError (rpcCHANNEL_AMT_MALFORMED);
}
std::uint64_t const drops = *optDrops;
Serializer msg;
serializePayChanAuthorization (msg, channelId, XRPAmount (drops));
@@ -90,29 +119,40 @@ Json::Value doChannelVerify (RPC::Context& context)
{
auto const& params (context.params);
for (auto const& p :
{jss::public_key, jss::channel_id, jss::amount, jss::signature})
{jss::public_key, jss::channel_id, jss::amount, jss::signature})
if (!params.isMember (p))
return RPC::missing_field_error (p);
std::string const strPk = params[jss::public_key].asString ();
auto const pk =
parseBase58<PublicKey> (TokenType::TOKEN_ACCOUNT_PUBLIC, strPk);
if (!pk)
return rpcError (rpcPUBLIC_MALFORMED);
boost::optional<PublicKey> pk;
{
std::string const strPk = params[jss::public_key].asString();
pk = parseBase58<PublicKey>(TokenType::TOKEN_ACCOUNT_PUBLIC, strPk);
if (!pk)
{
std::pair<Blob, bool> pkHex(strUnHex (strPk));
if (!pkHex.second)
return rpcError(rpcPUBLIC_MALFORMED);
auto const pkType = publicKeyType(makeSlice(pkHex.first));
if (!pkType)
return rpcError(rpcPUBLIC_MALFORMED);
pk.emplace(makeSlice(pkHex.first));
}
}
uint256 channelId;
if (!channelId.SetHexExact (params[jss::channel_id].asString ()))
return rpcError (rpcCHANNEL_MALFORMED);
std::uint64_t drops = 0;
try
{
drops = std::stoul (params[jss::amount].asString ());
}
catch (std::exception const&)
{
boost::optional<std::uint64_t> const optDrops =
params[jss::amount].isString()
? to_uint64(params[jss::amount].asString())
: boost::none;
if (!optDrops)
return rpcError (rpcCHANNEL_AMT_MALFORMED);
}
std::uint64_t const drops = *optDrops;
std::pair<Blob, bool> sig(strUnHex (params[jss::signature].asString ()));
if (!sig.second || !sig.first.size ())

View File

@@ -766,15 +766,99 @@ struct PayChan_test : public beast::unit_test::suite
BEAST_EXPECT (r[jss::result][jss::channels][0u][jss::channel_id] == c ||
r[jss::result][jss::channels][1u][jss::channel_id] == c);
}
auto sliceToHex = [](Slice const& slice) {
std::string s;
s.reserve(2 * slice.size());
for (int i = 0; i < slice.size(); ++i)
{
s += "0123456789ABCDEF"[((slice[i] & 0xf0) >> 4)];
s += "0123456789ABCDEF"[((slice[i] & 0x0f) >> 0)];
}
return s;
};
{
// Verify chan1 auth
auto const rs =
env.rpc ("channel_authorize", "alice", chan1Str, "1000");
auto const sig = rs[jss::result][jss::signature].asString ();
BEAST_EXPECT (!sig.empty ());
auto const rv = env.rpc (
"channel_verify", chan1PkStr, chan1Str, "1000", sig);
BEAST_EXPECT (rv[jss::result][jss::signature_verified].asBool ());
{
auto const rv = env.rpc(
"channel_verify", chan1PkStr, chan1Str, "1000", sig);
BEAST_EXPECT(rv[jss::result][jss::signature_verified].asBool());
}
{
// use pk hex to verify
auto const pkAsHex = sliceToHex(pk.slice());
auto const rv = env.rpc (
"channel_verify", pkAsHex, chan1Str, "1000", sig);
BEAST_EXPECT (rv[jss::result][jss::signature_verified].asBool ());
}
{
// malformed amount
auto const pkAsHex = sliceToHex(pk.slice());
auto rv =
env.rpc("channel_verify", pkAsHex, chan1Str, "1000x", sig);
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000 ", sig);
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "x1000", sig);
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "x", sig);
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
rv = env.rpc("channel_verify", pkAsHex, chan1Str, " ", sig);
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1000 1000", sig);
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "1,000", sig);
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
rv = env.rpc("channel_verify", pkAsHex, chan1Str, " 1000", sig);
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
rv = env.rpc("channel_verify", pkAsHex, chan1Str, "", sig);
BEAST_EXPECT(rv[jss::error] == "channelAmtMalformed");
}
{
// malformed channel
auto const pkAsHex = sliceToHex(pk.slice());
auto chan1StrBad = chan1Str;
chan1StrBad.pop_back();
auto rv = env.rpc("channel_verify", pkAsHex, chan1StrBad, "1000", sig);
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
rv = env.rpc ("channel_authorize", "alice", chan1StrBad, "1000");
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
chan1StrBad = chan1Str;
chan1StrBad.push_back('0');
rv = env.rpc("channel_verify", pkAsHex, chan1StrBad, "1000", sig);
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
rv = env.rpc ("channel_authorize", "alice", chan1StrBad, "1000");
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
chan1StrBad = chan1Str;
chan1StrBad.back() = 'x';
rv = env.rpc("channel_verify", pkAsHex, chan1StrBad, "1000", sig);
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
rv = env.rpc ("channel_authorize", "alice", chan1StrBad, "1000");
BEAST_EXPECT(rv[jss::error] == "channelMalformed");
}
{
// give an ill formed base 58 public key
auto illFormedPk = chan1PkStr.substr(0, chan1PkStr.size() - 1);
auto const rv = env.rpc(
"channel_verify", illFormedPk, chan1Str, "1000", sig);
BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
}
{
// give an ill formed hex public key
auto const pkAsHex = sliceToHex(pk.slice());
auto illFormedPk = pkAsHex.substr(0, chan1PkStr.size() - 1);
auto const rv = env.rpc(
"channel_verify", illFormedPk, chan1Str, "1000", sig);
BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
}
}
{
// Try to verify chan2 auth with chan1 key
@@ -782,9 +866,29 @@ struct PayChan_test : public beast::unit_test::suite
env.rpc ("channel_authorize", "alice", chan2Str, "1000");
auto const sig = rs[jss::result][jss::signature].asString ();
BEAST_EXPECT (!sig.empty ());
auto const rv =
env.rpc ("channel_verify", chan1PkStr, chan1Str, "1000", sig);
BEAST_EXPECT (!rv[jss::result][jss::signature_verified].asBool ());
{
auto const rv = env.rpc(
"channel_verify", chan1PkStr, chan1Str, "1000", sig);
BEAST_EXPECT(
!rv[jss::result][jss::signature_verified].asBool());
}
{
// use pk hex to verify
auto const pkAsHex = sliceToHex(pk.slice());
auto const rv = env.rpc(
"channel_verify", pkAsHex, chan1Str, "1000", sig);
BEAST_EXPECT(
!rv[jss::result][jss::signature_verified].asBool());
}
}
{
// send malformed amounts rpc requests
auto rs = env.rpc("channel_authorize", "alice", chan1Str, "1000x");
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
rs = env.rpc("channel_authorize", "alice", chan1Str, "x1000");
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
rs = env.rpc("channel_authorize", "alice", chan1Str, "x");
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
}
}