Allow channel_authorize to use Ed25519 keys

This commit is contained in:
Nik Bougalis
2019-10-16 00:43:02 -07:00
committed by Manoj doshi
parent a3a9dc26b4
commit 113167acf4
6 changed files with 196 additions and 12 deletions

View File

@@ -717,23 +717,43 @@ private:
return parseAccountRaw2 (jvParams, jss::destination_account);
}
// channel_authorize <private_key> <channel_id> <drops>
// channel_authorize: <private_key> [<key_type>] <channel_id> <drops>
Json::Value parseChannelAuthorize (Json::Value const& jvParams)
{
Json::Value jvRequest (Json::objectValue);
jvRequest[jss::secret] = jvParams[0u];
unsigned int index = 0;
if (jvParams.size() == 4)
{
jvRequest[jss::passphrase] = jvParams[index];
index++;
if (!keyTypeFromString(jvParams[index].asString()))
return rpcError (rpcBAD_KEY_TYPE);
jvRequest[jss::key_type] = jvParams[index];
index++;
}
else
{
jvRequest[jss::secret] = jvParams[index];
index++;
}
{
// verify the channel id is a valid 256 bit number
uint256 channelId;
if (!channelId.SetHexExact (jvParams[1u].asString ()))
if (!channelId.SetHexExact (jvParams[index].asString()))
return rpcError (rpcCHANNEL_MALFORMED);
jvRequest[jss::channel_id] = to_string(channelId);
index++;
}
jvRequest[jss::channel_id] = jvParams[1u].asString ();
if (!jvParams[2u].isString() || !to_uint64(jvParams[2u].asString()))
if (!jvParams[index].isString() || !to_uint64(jvParams[index].asString()))
return rpcError(rpcCHANNEL_AMT_MALFORMED);
jvRequest[jss::amount] = jvParams[2u];
jvRequest[jss::amount] = jvParams[index];
// If additional parameters are appended, be sure to increment index here
return jvRequest;
}
@@ -1122,7 +1142,7 @@ public:
{ "account_tx", &RPCParser::parseAccountTransactions, 1, 8 },
{ "book_offers", &RPCParser::parseBookOffers, 2, 7 },
{ "can_delete", &RPCParser::parseCanDelete, 0, 1 },
{ "channel_authorize", &RPCParser::parseChannelAuthorize, 3, 3 },
{ "channel_authorize", &RPCParser::parseChannelAuthorize, 3, 4 },
{ "channel_verify", &RPCParser::parseChannelVerify, 4, 4 },
{ "connect", &RPCParser::parseConnect, 1, 2 },
{ "consensus_info", &RPCParser::parseAsIs, 0, 0 },

View File

@@ -131,7 +131,8 @@ enum error_code_i
rpcINTERNAL = 73, // Generic internal error.
rpcNOT_IMPL = 74,
rpcNOT_SUPPORTED = 75,
rpcLAST = rpcNOT_SUPPORTED // rpcLAST should always equal the last code.
rpcBAD_KEY_TYPE = 76,
rpcLAST = rpcBAD_KEY_TYPE // rpcLAST should always equal the last code.
};
//------------------------------------------------------------------------------

View File

@@ -39,6 +39,7 @@ constexpr static ErrorInfo unorderedErrorInfos[]
{rpcALREADY_SINGLE_SIG, "alreadySingleSig", "Already single-signed."},
{rpcAMENDMENT_BLOCKED, "amendmentBlocked", "Amendment blocked, need upgrade."},
{rpcATX_DEPRECATED, "deprecated", "Use the new API or specify a ledger range."},
{rpcBAD_KEY_TYPE, "badKeyType", "Bad key type."},
{rpcBAD_FEATURE, "badFeature", "Feature unknown or invalid."},
{rpcBAD_ISSUER, "badIssuer", "Issuer account malformed."},
{rpcBAD_MARKET, "badMarket", "No such market."},

View File

@@ -36,16 +36,23 @@ namespace ripple {
// {
// secret_key: <signing_secret_key>
// key_type: optional; either ed25519 or secp256k1 (default to secp256k1)
// channel_id: 256-bit channel id
// drops: 64-bit uint (as string)
// }
Json::Value doChannelAuthorize (RPC::Context& context)
{
auto const& params (context.params);
for (auto const& p : {jss::secret, jss::channel_id, jss::amount})
for (auto const& p : {jss::channel_id, jss::amount})
if (!params.isMember (p))
return RPC::missing_field_error (p);
// Compatibility if a key type isn't specified. If it is, the
// keypairForSignature code will validate parameters and return
// the appropriate error.
if (!params.isMember(jss::key_type) && !params.isMember(jss::secret))
return RPC::missing_field_error (jss::secret);
Json::Value result;
auto const [pk, sk] = RPC::keypairForSignature (params, result);
if (RPC::contains_error (result))

View File

@@ -878,7 +878,8 @@ struct PayChan_test : public beast::unit_test::suite
Env env (*this);
auto const alice = Account ("alice");
auto const bob = Account ("bob");
env.fund (XRP (10000), alice, bob);
auto const charlie = Account("charlie", KeyType::ed25519);
env.fund (XRP (10000), alice, bob, charlie);
auto const pk = alice.pk ();
auto const settleDelay = 3600s;
auto const channelFunds = XRP (1000);
@@ -1036,6 +1037,63 @@ struct PayChan_test : public beast::unit_test::suite
!rv[jss::result][jss::signature_verified].asBool());
}
}
{
// Try to explicitly specify secp256k1 and Ed25519 keys:
env (create (charlie, alice, channelFunds, settleDelay, charlie.pk()));
env.close();
auto const chan = to_string (channel (*env.current (), charlie, alice));
std::string cpk;
{
auto const r =
env.rpc ("account_channels", charlie.human (), alice.human ());
BEAST_EXPECT (r[jss::result][jss::channels].size () == 1);
BEAST_EXPECT (r[jss::result][jss::channels][0u][jss::channel_id] == chan);
BEAST_EXPECT (r[jss::result][jss::validated]);
cpk = r[jss::result][jss::channels][0u][jss::public_key].asString();
}
// Try to authorize without specifying a key type, expect an error:
auto const rs =
env.rpc ("channel_authorize", "charlie", chan, "1000");
auto const sig = rs[jss::result][jss::signature].asString ();
BEAST_EXPECT (!sig.empty ());
{
auto const rv = env.rpc(
"channel_verify", cpk, chan, "1000", sig);
BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
}
// Try to authorize using an unknown key type, except an error:
auto const rs1 =
env.rpc ("channel_authorize", "charlie", "nyx", chan, "1000");
BEAST_EXPECT(rs1[jss::error] == "badKeyType");
// Try to authorize using secp256k1; the authorization _should_
// succeed but the verification should fail:
auto const rs2 =
env.rpc ("channel_authorize", "charlie", "secp256k1", chan, "1000");
auto const sig2 = rs2[jss::result][jss::signature].asString ();
BEAST_EXPECT (!sig2.empty ());
{
auto const rv = env.rpc(
"channel_verify", cpk, chan, "1000", sig2);
BEAST_EXPECT(!rv[jss::result][jss::signature_verified].asBool());
}
// Try to authorize using Ed25519; expect success:
auto const rs3 =
env.rpc ("channel_authorize", "charlie", "ed25519", chan, "1000");
auto const sig3 = rs3[jss::result][jss::signature].asString ();
BEAST_EXPECT (!sig3.empty ());
{
auto const rv = env.rpc(
"channel_verify", cpk, chan, "1000", sig3);
BEAST_EXPECT(rv[jss::result][jss::signature_verified].asBool());
}
}
{
// send malformed amounts rpc requests
auto rs = env.rpc("channel_authorize", "alice", chan1Str, "1000x");
@@ -1044,6 +1102,81 @@ struct PayChan_test : public beast::unit_test::suite
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
rs = env.rpc("channel_authorize", "alice", chan1Str, "x");
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
{
// Missing channel_id
Json::Value args {Json::objectValue};
args[jss::amount] = "2000";
args[jss::key_type] = "secp256k1";
args[jss::passphrase] = "passphrase_can_be_anything";
rs = env.rpc ("json",
"channel_authorize", args.toStyledString())[jss::result];
BEAST_EXPECT(rs[jss::error] == "invalidParams");
}
{
// Missing amount
Json::Value args {Json::objectValue};
args[jss::channel_id] = chan1Str;
args[jss::key_type] = "secp256k1";
args[jss::passphrase] = "passphrase_can_be_anything";
rs = env.rpc ("json",
"channel_authorize", args.toStyledString())[jss::result];
BEAST_EXPECT(rs[jss::error] == "invalidParams");
}
{
// Missing key_type and no secret.
Json::Value args {Json::objectValue};
args[jss::amount] = "2000";
args[jss::channel_id] = chan1Str;
args[jss::passphrase] = "passphrase_can_be_anything";
rs = env.rpc ("json",
"channel_authorize", args.toStyledString())[jss::result];
BEAST_EXPECT(rs[jss::error] == "invalidParams");
}
{
// Both passphrase and seed specified.
Json::Value args {Json::objectValue};
args[jss::amount] = "2000";
args[jss::channel_id] = chan1Str;
args[jss::key_type] = "secp256k1";
args[jss::passphrase] = "passphrase_can_be_anything";
args[jss::seed] = "seed can be anything";
rs = env.rpc ("json",
"channel_authorize", args.toStyledString())[jss::result];
BEAST_EXPECT(rs[jss::error] == "invalidParams");
}
{
// channel_id is not exact hex.
Json::Value args {Json::objectValue};
args[jss::amount] = "2000";
args[jss::channel_id] = chan1Str + "1";
args[jss::key_type] = "secp256k1";
args[jss::passphrase] = "passphrase_can_be_anything";
rs = env.rpc ("json",
"channel_authorize", args.toStyledString())[jss::result];
BEAST_EXPECT(rs[jss::error] == "channelMalformed");
}
{
//amount is not a string
Json::Value args {Json::objectValue};
args[jss::amount] = 2000;
args[jss::channel_id] = chan1Str;
args[jss::key_type] = "secp256k1";
args[jss::passphrase] = "passphrase_can_be_anything";
rs = env.rpc ("json",
"channel_authorize", args.toStyledString())[jss::result];
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
}
{
// Amount is not a decimal string.
Json::Value args {Json::objectValue};
args[jss::amount] = "TwoThousand";
args[jss::channel_id] = chan1Str;
args[jss::key_type] = "secp256k1";
args[jss::passphrase] = "passphrase_can_be_anything";
rs = env.rpc ("json",
"channel_authorize", args.toStyledString())[jss::result];
BEAST_EXPECT(rs[jss::error] == "channelAmtMalformed");
}
}
}

View File

@@ -2222,10 +2222,11 @@ static RPCCallTestData const rpcCallTestArray [] =
"channel_authorize: too many arguments.", __LINE__,
{
"channel_authorize",
"secret_can_be_anything",
"secp256k1",
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
"2000",
"whatever"
"whatever",
"whenever"
},
RPCCallTestData::no_exception,
R"({
@@ -2239,6 +2240,27 @@ static RPCCallTestData const rpcCallTestArray [] =
]
})"
},
{
"channel_authorize: bad key type.", __LINE__,
{
"channel_authorize",
"secp257k1",
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
"2000",
"whatever"
},
RPCCallTestData::no_exception,
R"({
"method" : "channel_authorize",
"params" : [
{
"error" : "badKeyType",
"error_code" : 1,
"error_message" : "Bad key type."
}
]
})"
},
{
"channel_authorize: channel_id too short.", __LINE__,
{