Add Escrow support:

Escrow replaces the existing SusPay implementation with improved
code that also adds hashlock support to escrow payments, making
RCL ILP enabled.

The new functionality is under the `Escrow` amendment, which
supersedes and replaces the `SusPay` amendment.

This commit also deprecates the `CryptoConditions` amendment
which is replaced by the `CryptoConditionSuite` amendment which,
once enabled, will allow use of cryptoconditions others than
hashlocks.
This commit is contained in:
Nik Bougalis
2017-02-28 07:45:05 -08:00
committed by seelabs
parent 0c97dda276
commit cfde591ac9
38 changed files with 1267 additions and 3705 deletions

View File

@@ -1325,6 +1325,12 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\CreateTicket.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\Escrow.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\Escrow.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\tx\impl\Offer.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\OfferStream.cpp">
@@ -1375,12 +1381,6 @@
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\SignerEntries.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\SusPay.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\SusPay.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\Taker.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -1821,36 +1821,26 @@
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Condition.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Ed25519.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Fulfillment.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\impl\base64.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\conditions\impl\Condition.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\Ed25519.cpp">
<ClCompile Include="..\..\src\ripple\conditions\impl\error.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\conditions\impl\error.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\conditions\impl\Fulfillment.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\RsaSha256.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClInclude Include="..\..\src\ripple\conditions\impl\PreimageSha256.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\impl\utils.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\PrefixSha256.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\PreimageSha256.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\RsaSha256.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\core\Config.h">
</ClInclude>
<ClInclude Include="..\..\src\ripple\core\ConfigSections.h">
@@ -4187,6 +4177,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\app\Escrow_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\app\Flow_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -4255,10 +4249,6 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\app\SusPay_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\app\Taker_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
@@ -4399,22 +4389,10 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\Ed25519_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\PrefixSha256_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\PreimageSha256_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\RsaSha256_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\src\test\core\Config_test.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>

View File

@@ -1839,6 +1839,12 @@
<ClInclude Include="..\..\src\ripple\app\tx\impl\CreateTicket.h">
<Filter>ripple\app\tx\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\Escrow.cpp">
<Filter>ripple\app\tx\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\Escrow.h">
<Filter>ripple\app\tx\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\app\tx\impl\Offer.h">
<Filter>ripple\app\tx\impl</Filter>
</ClInclude>
@@ -1890,12 +1896,6 @@
<ClInclude Include="..\..\src\ripple\app\tx\impl\SignerEntries.h">
<Filter>ripple\app\tx\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\SusPay.cpp">
<Filter>ripple\app\tx\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\app\tx\impl\SusPay.h">
<Filter>ripple\app\tx\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\tx\impl\Taker.cpp">
<Filter>ripple\app\tx\impl</Filter>
</ClCompile>
@@ -2439,39 +2439,27 @@
<ClInclude Include="..\..\src\ripple\conditions\Condition.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Ed25519.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\Fulfillment.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\impl\base64.h">
<Filter>ripple\conditions\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\conditions\impl\Condition.cpp">
<Filter>ripple\conditions\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\Ed25519.cpp">
<ClCompile Include="..\..\src\ripple\conditions\impl\error.cpp">
<Filter>ripple\conditions\impl</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ripple\conditions\impl\error.h">
<Filter>ripple\conditions\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\conditions\impl\Fulfillment.cpp">
<Filter>ripple\conditions\impl</Filter>
</ClCompile>
<ClCompile Include="..\..\src\ripple\conditions\impl\RsaSha256.cpp">
<ClInclude Include="..\..\src\ripple\conditions\impl\PreimageSha256.h">
<Filter>ripple\conditions\impl</Filter>
</ClCompile>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\impl\utils.h">
<Filter>ripple\conditions\impl</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\PrefixSha256.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\PreimageSha256.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\conditions\RsaSha256.h">
<Filter>ripple\conditions</Filter>
</ClInclude>
<ClInclude Include="..\..\src\ripple\core\Config.h">
<Filter>ripple\core</Filter>
</ClInclude>
@@ -4983,6 +4971,9 @@
<ClCompile Include="..\..\src\test\app\Discrepancy_test.cpp">
<Filter>test\app</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\app\Escrow_test.cpp">
<Filter>test\app</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\app\Flow_test.cpp">
<Filter>test\app</Filter>
</ClCompile>
@@ -5034,9 +5025,6 @@
<ClCompile Include="..\..\src\test\app\SHAMapStore_test.cpp">
<Filter>test\app</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\app\SusPay_test.cpp">
<Filter>test\app</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\app\Taker_test.cpp">
<Filter>test\app</Filter>
</ClCompile>
@@ -5142,18 +5130,9 @@
<ClCompile Include="..\..\src\test\beast\SemanticVersion_test.cpp">
<Filter>test\beast</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\Ed25519_test.cpp">
<Filter>test\conditions</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\PrefixSha256_test.cpp">
<Filter>test\conditions</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\PreimageSha256_test.cpp">
<Filter>test\conditions</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\conditions\RsaSha256_test.cpp">
<Filter>test\conditions</Filter>
</ClCompile>
<ClCompile Include="..\..\src\test\core\Config_test.cpp">
<Filter>test\core</Filter>
</ClCompile>

View File

@@ -42,7 +42,6 @@ supportedAmendments ()
{ "C6970A8B603D8778783B61C0D445C23D1633CCFAEF0D43E7DBCD1521D34BD7C3 SHAMapV2" },
{ "4C97EBA926031A7CF7D7B36FDE3ED66DDA5421192D63DE53FFB46E43B9DC8373 MultiSign" },
{ "C1B8D934087225F509BEB5A8EC24447854713EE447D277F69545ABFA0E0FD490 Tickets" },
{ "DA1BD556B42D85EA9C84066D028D355B52416734D3283F85E216EA5DA6DB7E13 SusPay" },
{ "6781F8368C4771B83E8B821D88F580202BCB4228075297B19E4FDC5233F1EFDC TrustSetAuth" },
{ "42426C4D4F1009EE67080A9B7965B44656D7714D104A72F9B4369F97ABF044EE FeeEscalation" },
{ "9178256A980A86CF3D70D0260A7DA6402AAFE43632FDBCB88037978404188871 OwnerPaysFee" },
@@ -50,7 +49,9 @@ supportedAmendments ()
{ "740352F2412A9909880C23A559FCECEDA3BE2126FED62FC7660D628A06927F11 Flow" },
{ "1562511F573A19AE9BD103B5D6B9E01B3B46805AEC5D3C4805C902B514399146 CryptoConditions" },
{ "532651B4FD58DF8922A49BA101AB3E996E5BFBF95A913B3E392504863E63B164 TickSize" },
{ "67431BE4A5F355C45E36EED94B38DFACC1F111F547360F444642512CBC0E18E1 RIPD1368" }
{ "67431BE4A5F355C45E36EED94B38DFACC1F111F547360F444642512CBC0E18E1 RIPD1368" },
{ "07D43DCE529B15A10827E5E04943B496762F9A88E3268269D69C44BE49E21104 Escrow" },
{ "86E83A7D2ECE3AD5FA87AB2195AE015C950469ABF0B72EAACED318F74886AE90 CryptoConditionsSuite" }
};
}

View File

@@ -18,7 +18,7 @@
//==============================================================================
#include <BeastConfig.h>
#include <ripple/app/tx/impl/SusPay.h>
#include <ripple/app/tx/impl/Escrow.h>
#include <ripple/app/misc/HashRouter.h>
#include <ripple/basics/chrono.h>
#include <ripple/basics/Log.h>
@@ -32,7 +32,7 @@
#include <ripple/protocol/XRPAmount.h>
#include <ripple/ledger/View.h>
// During a SusPayFinish, the transaction must specify both
// During an EscrowFinish, the transaction must specify both
// a condition and a fulfillment. We track whether that
// fulfillment matches and validates the condition.
#define SF_CF_INVALID SF_PRIVATE5
@@ -41,88 +41,88 @@
namespace ripple {
/*
SuspendedPayment
Escrow allows an account holder to sequester any amount
of XRP in its own ledger entry, until the escrow process
either finishes or is canceled.
A suspended payment ("SusPay") sequesters XRP in its
own ledger entry until a SusPayFinish or a SusPayCancel
transaction mentioning the ledger entry is successfully
applied to the ledger. If the SusPayFinish succeeds,
the destination account (which must exist) receives the
XRP. If the SusPayCancel succeeds, the account which
created the SusPay is credited the XRP.
If the escrow process finishes successfully, then the
destination account (which must exist) will receives the
sequestered XRP. If the escrow is, instead, canceled,
the account which created the escrow will receive the
sequestered XRP back instead.
SusPayCreate
EscrowCreate
When the SusPay is created, an optional condition may
be attached. The condition is specified by providing
the cryptographic digest of the condition to be met.
When an escrow is created, an optional condition may
be attached. If present, that condition must be
fulfilled for the escrow to successfully finish.
At the time of creation, one or both of the fields
sfCancelAfter and sfFinishAfter may be provided. If
neither field is specified, the transaction is
malformed.
Since the SusPay eventually becomes a payment, an
Since the escrow eventually becomes a payment, an
optional DestinationTag and an optional SourceTag
is supported in the SusPayCreate transaction.
are supported in the EscrowCreate transaction.
Validation rules:
sfCondition
If present, specifies a condition, and the
If present, specifies a condition; the same
condition along with its matching fulfillment
is required on a SusPayFinish.
are required during EscrowFinish.
sfCancelAfter
If present, SusPay may be canceled after the
If present, escrow may be canceled after the
specified time (seconds after the Ripple epoch).
sfFinishAfter
If present, must be prior to sfCancelAfter.
A SusPayFinish succeeds only in ledgers after
A EscrowFinish succeeds only in ledgers after
sfFinishAfter but before sfCancelAfter.
If absent, same as parentCloseTime
Malformed if both sfCancelAfter, sfFinishAfter
are absent.
are absent.
Malformed if both sfFinishAfter, sfCancelAfter
specified and sfCancelAfter <= sfFinishAfter
specified and sfCancelAfter <= sfFinishAfter
SusPayFinish
EscrowFinish
Any account may submit a SusPayFinish. If the SusPay
ledger entry specifies a condition, the SusPayFinish
Any account may submit a EscrowFinish. If the escrow
ledger entry specifies a condition, the EscrowFinish
must provide the same condition and its associated
fulfillment in the sfFulfillment field, or else the
SusPayFinish will fail.
fulfillment in the sfCondition and sfFulfillment
fields, or else the EscrowFinish will fail.
If the SusPay ledger entry specifies sfFinishAfter, the
If the escrow ledger entry specifies sfFinishAfter, the
transaction will fail if parentCloseTime <= sfFinishAfter.
SusPayFinish transactions must be submitted before a
SusPay's sfCancelAfter if present.
EscrowFinish transactions must be submitted before
the escrow's sfCancelAfter if present.
If the SusPay ledger entry specifies sfCancelAfter, the
If the escrow ledger entry specifies sfCancelAfter, the
transaction will fail if sfCancelAfter <= parentCloseTime.
NOTE: The reason the condition must be specified again
is because it must always be possible to verify
the condition without retrieving the SusPay
the condition without retrieving the escrow
ledger entry.
SusPayCancel
EscrowCancel
Any account may submit a SusPayCancel transaction.
Any account may submit a EscrowCancel transaction.
If the SusPay ledger entry does not specify a
If the escrow ledger entry does not specify a
sfCancelAfter, the cancel transaction will fail.
If parentCloseTime <= sfCancelAfter, the transaction
will fail.
When a SusPay is canceled, the funds are returned to
When a escrow is canceled, the funds are returned to
the source account.
By careful selection of fields in each transaction,
@@ -135,15 +135,15 @@ namespace ripple {
//------------------------------------------------------------------------------
XRPAmount
SusPayCreate::calculateMaxSpend(STTx const& tx)
EscrowCreate::calculateMaxSpend(STTx const& tx)
{
return tx[sfAmount].xrp();
}
TER
SusPayCreate::preflight (PreflightContext const& ctx)
EscrowCreate::preflight (PreflightContext const& ctx)
{
if (! ctx.rules.enabled(featureSusPay))
if (! ctx.rules.enabled(featureEscrow))
return temDISABLED;
auto const ret = preflight1 (ctx);
@@ -166,43 +166,30 @@ SusPayCreate::preflight (PreflightContext const& ctx)
if (auto const cb = ctx.tx[~sfCondition])
{
if (! ctx.rules.enabled(featureCryptoConditions))
return temDISABLED;
using namespace ripple::cryptoconditions;
// TODO: Remove this try/catch once cryptoconditions
// no longer use exceptions.
try
{
auto condition = loadCondition(*cb);
if (!condition)
return temMALFORMED;
{
// TODO: This is here temporarily to ensure
// that the condition given doesn't
// contain unnecessary trailing junk.
// The new parsing API will simplify
// the checking here.
auto b = to_blob(*condition);
if (*cb != makeSlice(b))
return temMALFORMED;
}
}
catch (...)
std::error_code ec;
auto condition = Condition::deserialize(*cb, ec);
if (!condition)
{
JLOG(ctx.j.debug()) <<
"Malformed condition during escrow creation: " << ec.message();
return temMALFORMED;
}
// Conditions other than PrefixSha256 require the
// "CryptoConditionsSuite" amendment:
if (condition->type != Type::preimageSha256 &&
!ctx.rules.enabled(featureCryptoConditionsSuite))
return temDISABLED;
}
return preflight2 (ctx);
}
TER
SusPayCreate::doApply()
EscrowCreate::doApply()
{
auto const closeTime = ctx_.view ().info ().parentCloseTime;
@@ -252,9 +239,9 @@ SusPayCreate::doApply()
return tecNO_TARGET;
}
// Create SusPay in ledger
// Create escrow in ledger
auto const slep = std::make_shared<SLE>(
keylet::susPay(account, (*sle)[sfSequence] - 1));
keylet::escrow(account, (*sle)[sfSequence] - 1));
(*slep)[sfAmount] = ctx_.tx[sfAmount];
(*slep)[sfAccount] = account;
(*slep)[~sfCondition] = ctx_.tx[~sfCondition];
@@ -266,7 +253,7 @@ SusPayCreate::doApply()
ctx_.view().insert(slep);
// Add SusPay to owner directory
// Add escrow to owner directory
{
uint64_t page;
auto result = dirAdd(ctx_.view(), page,
@@ -293,48 +280,23 @@ checkCondition (Slice f, Slice c)
{
using namespace ripple::cryptoconditions;
// TODO: Remove this try/catch once cryptoconditions
// no longer use exceptions.
try
{
auto condition = loadCondition(c);
std::error_code ec;
if (!condition)
return false;
auto fulfillment = loadFulfillment(f);
if (!fulfillment)
return false;
{
// TODO: This is here temporarily to ensure
// that the condition & fulfillment
// given don't contain unnecessary
// trailing junk. The new parsing API
// will simplify the checking here.
auto cb = to_blob(*condition);
if (c != makeSlice(cb))
return false;
auto fb = to_blob(*fulfillment);
if (f != makeSlice(fb))
return false;
}
return validateTrigger (*fulfillment, *condition);
}
catch (...)
{
auto condition = Condition::deserialize(c, ec);
if (!condition)
return false;
}
auto fulfillment = Fulfillment::deserialize(f, ec);
if (!fulfillment)
return false;
return validate (*fulfillment, *condition);
}
TER
SusPayFinish::preflight (PreflightContext const& ctx)
EscrowFinish::preflight (PreflightContext const& ctx)
{
if (! ctx.rules.enabled(featureSusPay))
if (! ctx.rules.enabled(featureEscrow))
return temDISABLED;
{
@@ -346,12 +308,6 @@ SusPayFinish::preflight (PreflightContext const& ctx)
auto const cb = ctx.tx[~sfCondition];
auto const fb = ctx.tx[~sfFulfillment];
if (cb || fb)
{
if (! ctx.rules.enabled(featureCryptoConditions))
return temDISABLED;
}
// If you specify a condition, then you must also specify
// a fulfillment.
if (static_cast<bool>(cb) != static_cast<bool>(fb))
@@ -388,7 +344,7 @@ SusPayFinish::preflight (PreflightContext const& ctx)
}
std::uint64_t
SusPayFinish::calculateBaseFee (PreclaimContext const& ctx)
EscrowFinish::calculateBaseFee (PreclaimContext const& ctx)
{
std::uint64_t extraFee = 0;
@@ -401,12 +357,10 @@ SusPayFinish::calculateBaseFee (PreclaimContext const& ctx)
return Transactor::calculateBaseFee (ctx) + extraFee;
}
TER
SusPayFinish::doApply()
EscrowFinish::doApply()
{
// peek SusPay SLE
auto const k = keylet::susPay(
auto const k = keylet::escrow(
ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
auto const slep = ctx_.view().peek(k);
if (! slep)
@@ -473,7 +427,7 @@ SusPayFinish::doApply()
AccountID const account = (*slep)[sfAccount];
// Remove SusPay from owner directory
// Remove escrow from owner directory
{
auto const page = (*slep)[sfOwnerNode];
TER const ter = dirDelete(ctx_.view(), true,
@@ -501,7 +455,7 @@ SusPayFinish::doApply()
(*sle)[sfOwnerCount] = (*sle)[sfOwnerCount] - 1;
ctx_.view().update(sle);
// Remove SusPay from ledger
// Remove escrow from ledger
ctx_.view().erase(slep);
return tesSUCCESS;
@@ -510,9 +464,9 @@ SusPayFinish::doApply()
//------------------------------------------------------------------------------
TER
SusPayCancel::preflight (PreflightContext const& ctx)
EscrowCancel::preflight (PreflightContext const& ctx)
{
if (! ctx.rules.enabled(featureSusPay))
if (! ctx.rules.enabled(featureEscrow))
return temDISABLED;
auto const ret = preflight1 (ctx);
@@ -523,10 +477,9 @@ SusPayCancel::preflight (PreflightContext const& ctx)
}
TER
SusPayCancel::doApply()
EscrowCancel::doApply()
{
// peek SusPay SLE
auto const k = keylet::susPay(
auto const k = keylet::escrow(
ctx_.tx[sfOwner], ctx_.tx[sfOfferSequence]);
auto const slep = ctx_.view().peek(k);
if (! slep)
@@ -540,7 +493,7 @@ SusPayCancel::doApply()
AccountID const account = (*slep)[sfAccount];
// Remove SusPay from owner directory
// Remove escrow from owner directory
{
auto const page = (*slep)[sfOwnerNode];
TER const ter = dirDelete(ctx_.view(), true,
@@ -557,7 +510,7 @@ SusPayCancel::doApply()
(*sle)[sfOwnerCount] = (*sle)[sfOwnerCount] - 1;
ctx_.view().update(sle);
// Remove SusPay from ledger
// Remove escrow from ledger
ctx_.view().erase(slep);
return tesSUCCESS;

View File

@@ -17,19 +17,19 @@
*/
//==============================================================================
#ifndef RIPPLE_TX_SUSPAY_H_INCLUDED
#define RIPPLE_TX_SUSPAY_H_INCLUDED
#ifndef RIPPLE_TX_ESCROW_H_INCLUDED
#define RIPPLE_TX_ESCROW_H_INCLUDED
#include <ripple/app/tx/impl/Transactor.h>
namespace ripple {
class SusPayCreate
class EscrowCreate
: public Transactor
{
public:
explicit
SusPayCreate (ApplyContext& ctx)
EscrowCreate (ApplyContext& ctx)
: Transactor(ctx)
{
}
@@ -48,12 +48,12 @@ public:
//------------------------------------------------------------------------------
class SusPayFinish
class EscrowFinish
: public Transactor
{
public:
explicit
SusPayFinish (ApplyContext& ctx)
EscrowFinish (ApplyContext& ctx)
: Transactor(ctx)
{
}
@@ -72,12 +72,12 @@ public:
//------------------------------------------------------------------------------
class SusPayCancel
class EscrowCancel
: public Transactor
{
public:
explicit
SusPayCancel (ApplyContext& ctx)
EscrowCancel (ApplyContext& ctx)
: Transactor(ctx)
{
}

View File

@@ -25,12 +25,12 @@
#include <ripple/app/tx/impl/Change.h>
#include <ripple/app/tx/impl/CreateOffer.h>
#include <ripple/app/tx/impl/CreateTicket.h>
#include <ripple/app/tx/impl/Escrow.h>
#include <ripple/app/tx/impl/Payment.h>
#include <ripple/app/tx/impl/SetAccount.h>
#include <ripple/app/tx/impl/SetRegularKey.h>
#include <ripple/app/tx/impl/SetSignerList.h>
#include <ripple/app/tx/impl/SetTrust.h>
#include <ripple/app/tx/impl/SusPay.h>
#include <ripple/app/tx/impl/PayChan.h>
namespace ripple {
@@ -45,9 +45,9 @@ invoke_preflight (PreflightContext const& ctx)
case ttOFFER_CANCEL: return CancelOffer ::preflight(ctx);
case ttOFFER_CREATE: return CreateOffer ::preflight(ctx);
case ttPAYMENT: return Payment ::preflight(ctx);
case ttSUSPAY_CREATE: return SusPayCreate ::preflight(ctx);
case ttSUSPAY_FINISH: return SusPayFinish ::preflight(ctx);
case ttSUSPAY_CANCEL: return SusPayCancel ::preflight(ctx);
case ttESCROW_CREATE: return EscrowCreate ::preflight(ctx);
case ttESCROW_FINISH: return EscrowFinish ::preflight(ctx);
case ttESCROW_CANCEL: return EscrowCancel ::preflight(ctx);
case ttREGULAR_KEY_SET: return SetRegularKey ::preflight(ctx);
case ttSIGNER_LIST_SET: return SetSignerList ::preflight(ctx);
case ttTICKET_CANCEL: return CancelTicket ::preflight(ctx);
@@ -118,9 +118,9 @@ invoke_preclaim (PreclaimContext const& ctx)
case ttOFFER_CANCEL: return invoke_preclaim<CancelOffer>(ctx);
case ttOFFER_CREATE: return invoke_preclaim<CreateOffer>(ctx);
case ttPAYMENT: return invoke_preclaim<Payment>(ctx);
case ttSUSPAY_CREATE: return invoke_preclaim<SusPayCreate>(ctx);
case ttSUSPAY_FINISH: return invoke_preclaim<SusPayFinish>(ctx);
case ttSUSPAY_CANCEL: return invoke_preclaim<SusPayCancel>(ctx);
case ttESCROW_CREATE: return invoke_preclaim<EscrowCreate>(ctx);
case ttESCROW_FINISH: return invoke_preclaim<EscrowFinish>(ctx);
case ttESCROW_CANCEL: return invoke_preclaim<EscrowCancel>(ctx);
case ttREGULAR_KEY_SET: return invoke_preclaim<SetRegularKey>(ctx);
case ttSIGNER_LIST_SET: return invoke_preclaim<SetSignerList>(ctx);
case ttTICKET_CANCEL: return invoke_preclaim<CancelTicket>(ctx);
@@ -147,9 +147,9 @@ invoke_calculateBaseFee(PreclaimContext const& ctx)
case ttOFFER_CANCEL: return CancelOffer::calculateBaseFee(ctx);
case ttOFFER_CREATE: return CreateOffer::calculateBaseFee(ctx);
case ttPAYMENT: return Payment::calculateBaseFee(ctx);
case ttSUSPAY_CREATE: return SusPayCreate::calculateBaseFee(ctx);
case ttSUSPAY_FINISH: return SusPayFinish::calculateBaseFee(ctx);
case ttSUSPAY_CANCEL: return SusPayCancel::calculateBaseFee(ctx);
case ttESCROW_CREATE: return EscrowCreate::calculateBaseFee(ctx);
case ttESCROW_FINISH: return EscrowFinish::calculateBaseFee(ctx);
case ttESCROW_CANCEL: return EscrowCancel::calculateBaseFee(ctx);
case ttREGULAR_KEY_SET: return SetRegularKey::calculateBaseFee(ctx);
case ttSIGNER_LIST_SET: return SetSignerList::calculateBaseFee(ctx);
case ttTICKET_CANCEL: return CancelTicket::calculateBaseFee(ctx);
@@ -189,9 +189,9 @@ invoke_calculateConsequences(STTx const& tx)
case ttOFFER_CANCEL: return invoke_calculateConsequences<CancelOffer>(tx);
case ttOFFER_CREATE: return invoke_calculateConsequences<CreateOffer>(tx);
case ttPAYMENT: return invoke_calculateConsequences<Payment>(tx);
case ttSUSPAY_CREATE: return invoke_calculateConsequences<SusPayCreate>(tx);
case ttSUSPAY_FINISH: return invoke_calculateConsequences<SusPayFinish>(tx);
case ttSUSPAY_CANCEL: return invoke_calculateConsequences<SusPayCancel>(tx);
case ttESCROW_CREATE: return invoke_calculateConsequences<EscrowCreate>(tx);
case ttESCROW_FINISH: return invoke_calculateConsequences<EscrowFinish>(tx);
case ttESCROW_CANCEL: return invoke_calculateConsequences<EscrowCancel>(tx);
case ttREGULAR_KEY_SET: return invoke_calculateConsequences<SetRegularKey>(tx);
case ttSIGNER_LIST_SET: return invoke_calculateConsequences<SetSignerList>(tx);
case ttTICKET_CANCEL: return invoke_calculateConsequences<CancelTicket>(tx);
@@ -220,9 +220,9 @@ invoke_apply (ApplyContext& ctx)
case ttOFFER_CANCEL: { CancelOffer p(ctx); return p(); }
case ttOFFER_CREATE: { CreateOffer p(ctx); return p(); }
case ttPAYMENT: { Payment p(ctx); return p(); }
case ttSUSPAY_CREATE: { SusPayCreate p(ctx); return p(); }
case ttSUSPAY_FINISH: { SusPayFinish p(ctx); return p(); }
case ttSUSPAY_CANCEL: { SusPayCancel p(ctx); return p(); }
case ttESCROW_CREATE: { EscrowCreate p(ctx); return p(); }
case ttESCROW_FINISH: { EscrowFinish p(ctx); return p(); }
case ttESCROW_CANCEL: { EscrowCancel p(ctx); return p(); }
case ttREGULAR_KEY_SET: { SetRegularKey p(ctx); return p(); }
case ttSIGNER_LIST_SET: { SetSignerList p(ctx); return p(); }
case ttTICKET_CANCEL: { CancelTicket p(ctx); return p(); }

View File

@@ -20,73 +20,91 @@
#ifndef RIPPLE_CONDITIONS_CONDITION_H
#define RIPPLE_CONDITIONS_CONDITION_H
#include <ripple/conditions/impl/base64.h> // use Beast implementation
#include <ripple/basics/Buffer.h>
#include <ripple/basics/Slice.h>
#include <ripple/conditions/impl/utils.h>
#include <boost/optional.hpp>
#include <array>
#include <cstdint>
#include <set>
#include <string>
#include <system_error>
#include <vector>
namespace ripple {
namespace cryptoconditions {
// NIKB-TODO: These should move to a separate file:
std::uint16_t constexpr condition_hashlock = 0;
std::uint16_t constexpr condition_prefix_sha256 = 1;
std::uint16_t constexpr condition_threshold_sha256 = 2;
std::uint16_t constexpr condition_rsa_sha256 = 3;
std::uint16_t constexpr condition_ed25519 = 4;
// NIKB-TODO: These should be `enum class : std::uint32_t`
std::uint32_t constexpr feature_sha256 = 1;
std::uint32_t constexpr feature_preimage = 2;
std::uint32_t constexpr feature_prefix = 4;
std::uint32_t constexpr feature_threshold = 8;
std::uint32_t constexpr feature_rsa_pss = 16;
std::uint32_t constexpr feature_ed25519 = 32;
/** The list of all feature suited defined in the RFC */
std::uint32_t constexpr definedFeatures =
feature_sha256 |
feature_preimage |
feature_prefix |
feature_threshold |
feature_rsa_pss |
feature_ed25519;
/** The largest fulfillment supported by this implementation.
Fulfillments larger than this value cannot be processed
and will not be generated.
*/
constexpr std::size_t maxSupportedFulfillmentLength = 65535;
struct Condition
enum class Type
: std::uint8_t
{
std::uint16_t type;
preimageSha256 = 0,
prefixSha256 = 1,
thresholdSha256 = 2,
rsaSha256 = 3,
ed25519Sha256 = 4
};
/** The maximum length of a fulfillment for this condition.
class Condition
{
public:
/** The largest binary condition we support.
While it is possible for a fulfillment to be smaller
it can never be bigger than this.
@note This value will be increased in the future, but it
must never decrease, as that could cause conditions
that were previously considered valid to no longer
be allowed.
*/
std::uint16_t maxFulfillmentLength;
static constexpr std::size_t maxSerializedCondition = 128;
/** The features suites required to process a fulfillment. */
std::uint32_t featureBitmask;
/** Load a condition from its binary form
@param s The buffer containing the fulfillment to load.
@param ec Set to the error, if any occurred.
The binary format for a condition is specified in the
cryptoconditions RFC. See:
https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.2
*/
static
std::unique_ptr<Condition>
deserialize(Slice s, std::error_code& ec);
public:
Type type;
/** An identifier for this condition.
This fingerprint is meant to be unique only with
respect to other conditions of the same type.
*/
std::array<std::uint8_t, 32> fingerprint;
Buffer fingerprint;
// Can this be deleted?
Condition () = default;
/** The cost associated with this condition. */
std::uint32_t cost;
Condition (Condition const&) = default;
Condition (Condition&&) = default;
/** For compound conditions, set of conditions includes */
std::set<Type> subtypes;
Condition(Type t, std::uint32_t c, Slice fp)
: type(t)
, fingerprint(fp)
, cost(c)
{
}
Condition(Type t, std::uint32_t c, Buffer&& fp)
: type(t)
, fingerprint(std::move(fp))
, cost(c)
{
}
~Condition() = default;
Condition(Condition const&) = default;
Condition(Condition&&) = default;
Condition() = delete;
};
inline
@@ -95,9 +113,9 @@ operator== (Condition const& lhs, Condition const& rhs)
{
return
lhs.type == rhs.type &&
lhs.featureBitmask == rhs.featureBitmask &&
lhs.maxFulfillmentLength == rhs.maxFulfillmentLength &&
lhs.fingerprint == rhs.fingerprint;
lhs.cost == rhs.cost &&
lhs.subtypes == rhs.subtypes &&
lhs.fingerprint == rhs.fingerprint;
}
inline
@@ -107,99 +125,6 @@ operator!= (Condition const& lhs, Condition const& rhs)
return !(lhs == rhs);
}
/** Determine if a given condition is valid.
@note this function checks whether it understands the
type of the condition, and if so, whether it meets
the requirements mandated by the RFC.
*/
inline
bool
validate (Condition const& c)
{
// This check can never trigger because of the range of
// the maxFulfillmentLength type. It's here in case the
// type is changed in the future.
if (c.maxFulfillmentLength > maxSupportedFulfillmentLength)
return false;
if (c.type == condition_hashlock)
return (c.featureBitmask == (feature_sha256 | feature_preimage));
// A prefix condition contains a subfulfillment; it
// requires all the features its child may require.
if (c.type == condition_prefix_sha256)
{
auto const mask = (feature_sha256 | feature_prefix);
// We need to have at least our own feature suites:
auto const cf1 = c.featureBitmask & mask;
if (cf1 != mask)
return false;
// And at least one more feature suite for our
// subfulfillment (since you need to terminate a
// chain of prefix conditions with a non-prefix)
auto const cf2 = c.featureBitmask & ~mask;
if (cf2 == 0)
return false;
return (cf2 & definedFeatures) == cf2;
}
if (c.type == condition_rsa_sha256)
return (c.featureBitmask == (feature_rsa_pss | feature_sha256));
if (c.type == condition_ed25519)
return (c.featureBitmask == feature_ed25519);
return false;
}
/** True if condition type is specified in the RFC.
@note: this function may return true even if the type
of condition isn't presently supported by this
implementation.
*/
inline
bool
isCondition (std::uint16_t type)
{
switch(type)
{
case condition_hashlock:
case condition_prefix_sha256:
case condition_threshold_sha256:
case condition_rsa_sha256:
case condition_ed25519:
return true;
default:
return false;
}
}
/** Load a serialized condition either from its string or binary form */
/** @{ */
boost::optional<Condition>
loadCondition(std::string const& s);
boost::optional<Condition>
loadCondition(Slice s);
/** @} */
// Convert a condition to its string form
std::string
to_string (Condition const& c);
// Convert a condition to its binary form
std::vector<std::uint8_t>
to_blob (Condition const& c);
}
}

View File

@@ -1,100 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CONDITIONS_ED25519_H
#define RIPPLE_CONDITIONS_ED25519_H
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/SecretKey.h>
#include <array>
#include <cstdint>
namespace ripple {
namespace cryptoconditions {
class Ed25519 final
: public Fulfillment
{
static std::size_t constexpr signature_size_ = 64;
static std::size_t constexpr pubkey_size_ = 32;
std::array<std::uint8_t,
pubkey_size_ + signature_size_> payload_;
public:
Ed25519 () = default;
/** Create a fulfillment given a keypair and the message */
Ed25519 (
SecretKey const& secretKey,
PublicKey const& publicKey,
Slice message);
/** Create a fulfillment given a secret key and the message */
Ed25519 (
SecretKey const& secretKey,
Slice message);
Condition
condition() const override;
std::uint16_t
type () const override
{
return condition_ed25519;
}
std::uint32_t
features () const override
{
return feature_ed25519;
}
bool
ok () const override
{
return true;
}
std::size_t
payloadSize () const override
{
return payload_.size();
}
Buffer
payload() const override
{
return { payload_.data(), payload_.size() };
}
bool
validate (Slice data) const override;
bool
parsePayload (Slice s) override;
};
}
}
#endif

View File

@@ -31,69 +31,86 @@ namespace cryptoconditions {
struct Fulfillment
{
public:
/** The largest binary fulfillment we support.
@note This value will be increased in the future, but it
must never decrease, as that could cause fulfillments
that were previously considered valid to no longer
be allowed.
*/
static constexpr std::size_t maxSerializedFulfillment = 256;
/** Load a fulfillment from its binary form
@param s The buffer containing the fulfillment to load.
@param ec Set to the error, if any occurred.
The binary format for a fulfillment is specified in the
cryptoconditions RFC. See:
https://tools.ietf.org/html/draft-thomas-crypto-conditions-02#section-7.3
*/
static
std::unique_ptr<Fulfillment>
deserialize(
Slice s,
std::error_code& ec);
public:
virtual ~Fulfillment() = default;
Fulfillment () = default;
/** Returns the fulfillment's fingerprint:
/** Returns the size of the fulfillment's payload. */
virtual
std::size_t
payloadSize() const = 0;
/** Returns the fulfillment's payload */
The fingerprint is an octet string uniquely
representing this fulfillment's condition
with respect to other conditions of the
same type.
*/
virtual
Buffer
payload() const = 0;
fingerprint() const = 0;
/** Generates the condition */
/** Returns the type of this condition. */
virtual
Condition
condition() const = 0;
/** Returns the type */
virtual
std::uint16_t
Type
type () const = 0;
/** Returns the features suites required.
For any given fulfillment, the result includes all
the feature suites that an implementation must
support in order to be able to successfully parse
the fulfillment.
@note fulfillments of the same type may require
different features.
*/
virtual
std::uint32_t
features () const = 0;
/** Determines if this fulfillment is well-formed */
virtual
bool
ok () const = 0;
/** Validates a fulfillment. */
virtual
bool
validate (Slice data) const = 0;
/** Parses the fulfillment's payload. */
/** Calculates the cost associated with this fulfillment. *
The cost function is deterministic and depends on the
type and properties of the condition and the fulfillment
that the condition is generated from.
*/
virtual
bool
parsePayload (Slice s) = 0;
std::uint32_t
cost() const = 0;
/** Returns the condition associated with the given fulfillment.
This process is completely deterministic. All implementations
will, if compliant, produce the identical condition for the
same fulfillment.
*/
virtual
Condition
condition() const = 0;
};
inline
bool
operator== (Fulfillment const& lhs, Fulfillment const& rhs)
{
// FIXME: for compound conditions, need to also check subtypes
return
lhs.type() == rhs.type() &&
lhs.ok() == rhs.ok() &&
lhs.payload() == rhs.payload();
lhs.cost() == rhs.cost() &&
lhs.fingerprint() == rhs.fingerprint();
}
inline
@@ -103,37 +120,9 @@ operator!= (Fulfillment const& lhs, Fulfillment const& rhs)
return !(lhs == rhs);
}
/** Load a fulfillment from its string serialization.
The format is specified in Section 2.5.1 of the
cryptoconditions RFC:
https://tools.ietf.org/html/draft-thomas-crypto-conditions-00#section-2.5.1
*/
std::unique_ptr<Fulfillment>
loadFulfillment (std::string const& s);
/** Load a fulfillment from its binary serialization.
The format is specified in Section 2.5.2 of the
cryptoconditions RFC:
https://tools.ietf.org/html/draft-thomas-crypto-conditions-00#section-2.5.2
*/
std::unique_ptr<Fulfillment>
loadFulfillment (Slice s);
// Convert a fulfillment to its string form
std::string
to_string (Fulfillment const& f);
// Convert a fulfillment to its binary form
std::vector<std::uint8_t>
to_blob (Fulfillment const& f);
/** Determine whether a fulfillment fulfills a given condition */
/** Determine whether the given fulfillment and condition match */
bool
fulfills (
match (
Fulfillment const& f,
Condition const& c);
@@ -141,10 +130,11 @@ fulfills (
@param f The fulfillment
@param c The condition
@param m The message; note that the message is not
relevant for some conditions (e.g. hashlocks)
and a fulfillment will successfully satisfy its
condition for any given message.
@param m The message
@note the message is not relevant for some conditions
and a fulfillment will successfully satisfy its
condition for any given message.
*/
bool
validate (
@@ -166,7 +156,7 @@ validate (
@param c The condition
*/
bool
validateTrigger (
validate (
Fulfillment const& f,
Condition const& c);

View File

@@ -1,218 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CONDITIONS_PREFIX_SHA256_H
#define RIPPLE_CONDITIONS_PREFIX_SHA256_H
#include <ripple/basics/base_uint.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/impl/base64.h>
#include <ripple/protocol/digest.h>
#include <algorithm>
#include <iterator>
#include <memory>
#include <vector>
namespace ripple {
namespace cryptoconditions {
class PrefixSha256 final
: public Fulfillment
{
Buffer prefix_;
std::unique_ptr<Fulfillment> subfulfillment_;
public:
PrefixSha256 () = default;
std::size_t
payloadSize () const override
{
return
oer::predict_octetstring_size(prefix_.size()) +
subfulfillment_->payloadSize();
}
Buffer
payload() const override
{
// We should never have a condition in a state that
// isn't ok to call payload on.
if (!ok())
return {};
auto const subpayload = to_blob (*subfulfillment_);
Buffer b (subpayload.size() +
oer::predict_octetstring_size (prefix_.size()));
auto out = oer::encode_octetstring (
prefix_.size(),
prefix_.data(),
prefix_.data() + prefix_.size(),
b.data());
std::memcpy (out, subpayload.data(), subpayload.size());
return b;
}
Condition
condition() const override
{
auto const sc = subcondition();
auto const blob = to_blob (sc);
Buffer b (blob.size() +
oer::predict_octetstring_size (prefix_.size()));
auto out = oer::encode_octetstring (
prefix_.size(),
prefix_.data(),
prefix_.data() + prefix_.size(),
b.data());
std::memcpy (out, blob.data(), blob.size());
sha256_hasher h;
h (b.data(), b.size());
Condition cc;
cc.type = type();
cc.featureBitmask = features();
cc.maxFulfillmentLength = payloadSize();
cc.fingerprint = static_cast<sha256_hasher::result_type>(h);
return cc;
}
std::uint16_t
type () const override
{
return condition_prefix_sha256;
}
std::uint32_t
features () const override
{
return
feature_sha256 |
feature_prefix |
subfulfillment_->features();
}
bool
ok () const override
{
return static_cast<bool>(subfulfillment_);
}
bool
validate (Slice m) const override
{
if (!ok())
return false;
// Prepend the prefix to the message:
Buffer b (prefix_.size() + m.size());
if (prefix_.size())
std::memcpy (b.data(), prefix_.data(), prefix_.size());
if (m.size())
std::memcpy (b.data() + prefix_.size(), m.data(), m.size());
return subfulfillment_->validate (b);
}
Fulfillment const&
subfulfillment () const
{
return *subfulfillment_;
}
Condition
subcondition () const
{
return subfulfillment_->condition();
}
bool
parsePayload (Slice s) override
{
// The payload consists of the prefix, followed by
// a subfulfillment. It cannot be empty:
if (s.empty())
return false;
auto start = s.data();
auto finish = s.data() + s.size();
std::size_t len;
std::tie (start, len) = oer::decode_length (
start, finish);
if (len != 0)
{
if (std::distance (start, finish) < len)
return false;
std::memcpy (prefix_.alloc (len), start, len);
std::advance (start, len);
}
s += std::distance (s.data(), start);
// The remaining bytes in the slice are a fulfillment
// so we parse it as such. If we can, then we've
// succeeded.
subfulfillment_ = loadFulfillment (s);
if (!subfulfillment_)
{
prefix_.clear();
return false;
}
return true;
}
void setPrefix (Slice prefix)
{
prefix_ = prefix;
}
Slice prefix() const
{
return prefix_;
}
void setSubfulfillment (std::unique_ptr<Fulfillment> subfulfillment)
{
subfulfillment_ = std::move (subfulfillment);
}
};
}
}
#endif

View File

@@ -1,123 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CONDITIONS_PREIMAGE_SHA256_H
#define RIPPLE_CONDITIONS_PREIMAGE_SHA256_H
#include <ripple/basics/base_uint.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/impl/base64.h>
#include <ripple/protocol/digest.h>
#include <algorithm>
#include <iterator>
#include <vector>
namespace ripple {
namespace cryptoconditions {
class PreimageSha256 final
: public Fulfillment
{
Buffer payload_;
public:
PreimageSha256() = default;
PreimageSha256 (Slice s)
: payload_ (s)
{
// TODO: We don't want to throw. Devise better
// interface for constructing hashlock from
// given buffer.
if (payload_.size() > maxSupportedFulfillmentLength)
throw std::length_error (
"Maximum fulfillment length exceeded");
}
std::size_t
payloadSize () const override
{
return payload_.size();
}
Buffer
payload() const override
{
return { payload_.data(), payload_.size() };
}
Condition
condition() const override
{
sha256_hasher h;
h (payload_.data(), payload_.size());
Condition cc;
cc.type = type();
cc.featureBitmask = features();
cc.maxFulfillmentLength = payload_.size();
cc.fingerprint = static_cast<sha256_hasher::result_type>(h);
return cc;
}
std::uint16_t
type () const override
{
return condition_hashlock;
}
std::uint32_t
features () const override
{
return feature_sha256 | feature_preimage;
}
bool
ok () const override
{
return true;
}
bool
validate (Slice) const override
{
// Perhaps counterintuitively, the message isn't
// relevant.
return true;
}
bool
parsePayload (Slice s) override
{
// The payload may be empty
if (s.size() > maxSupportedFulfillmentLength)
return false;
payload_ = s;
return true;
}
};
}
}
#endif

View File

@@ -20,6 +20,7 @@
#include <ripple/basics/contract.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/impl/PreimageSha256.h>
#include <ripple/conditions/impl/utils.h>
#include <boost/regex.hpp>
#include <boost/optional.hpp>
@@ -29,160 +30,212 @@
namespace ripple {
namespace cryptoconditions {
boost::optional<Condition>
loadCondition(std::string const& s)
namespace detail {
// The binary encoding of conditions differs based on their
// type. All types define at least a fingerprint and cost
// sub-field. Some types, such as the compound condition
// types, define additional sub-fields that are required to
// convey essential properties of the cryptocondition (such
// as the sub-types used by sub-conditions in the case of
// the compound types).
//
// Conditions are encoded as follows:
//
// Condition ::= CHOICE {
// preimageSha256 [0] SimpleSha256Condition,
// prefixSha256 [1] CompoundSha256Condition,
// thresholdSha256 [2] CompoundSha256Condition,
// rsaSha256 [3] SimpleSha256Condition,
// ed25519Sha256 [4] SimpleSha256Condition
// }
//
// SimpleSha256Condition ::= SEQUENCE {
// fingerprint OCTET STRING (SIZE(32)),
// cost INTEGER (0..4294967295)
// }
//
// CompoundSha256Condition ::= SEQUENCE {
// fingerprint OCTET STRING (SIZE(32)),
// cost INTEGER (0..4294967295),
// subtypes ConditionTypes
// }
//
// ConditionTypes ::= BIT STRING {
// preImageSha256 (0),
// prefixSha256 (1),
// thresholdSha256 (2),
// rsaSha256 (3),
// ed25519Sha256 (4)
// }
constexpr std::size_t fingerprintSize = 32;
std::unique_ptr<Condition>
loadSimpleSha256(Type type, Slice s, std::error_code& ec)
{
static boost::regex const re_current (
"^" // start of line
"cc:" // 'cc' for cryptocondition
"([1-9a-f][0-9a-f]{0,3}|0):" // type (hexadecimal)
"([1-9a-f][0-9a-f]{0,15}):" // feature bitmask (hexadecimal)
"([a-zA-Z0-9_-]{0,86}):" // fingerprint (base64url)
"([1-9][0-9]{0,17}|0)" // fulfillment length (decimal)
"$" // end of line
, boost::regex_constants::optimize
);
using namespace der;
boost::smatch match;
auto p = parsePreamble(s, ec);
if (!boost::regex_match (s, match, re_current))
return boost::none;
if (ec)
return {};
try
if (!isPrimitive(p) || !isContextSpecific(p))
{
Condition c;
c.type = parse_hexadecimal<std::uint16_t> (match[1]);
if (!isCondition (c.type))
return boost::none;
c.featureBitmask = parse_hexadecimal<std::uint32_t>(match[2]);
c.maxFulfillmentLength = parse_decimal<std::uint16_t>(match[4]);
if (c.maxFulfillmentLength > maxSupportedFulfillmentLength)
return boost::none;
// TODO: Avoid copying by decoding directly
// into the condition's buffer
auto fingerprint = base64url_decode(match[3]);
if (fingerprint.size() != c.fingerprint.size())
return boost::none;
std::memcpy(
c.fingerprint.data(),
fingerprint.data(),
fingerprint.size());
return c;
ec = error::incorrect_encoding;
return {};
}
catch (std::exception const&)
if (p.tag != 0)
{
return boost::none;
ec = error::unexpected_tag;
return {};
}
}
boost::optional<Condition>
loadCondition(Slice s)
{
if (s.empty())
return boost::none;
try
if (p.length != fingerprintSize)
{
auto start = s.data();
auto finish = s.data() + s.size();
ec = error::fingerprint_size;
return {};
}
Condition c;
Buffer b = parseOctetString(s, p.length, ec);
std::tie (start, c.type) =
oer::decode_integer<std::uint16_t> (
start, finish);
if (ec)
return {};
if (!isCondition (c.type))
return boost::none;
p = parsePreamble(s, ec);
std::tie (start, c.featureBitmask) =
oer::decode_varuint<std::uint32_t> (
start, finish);
if (ec)
return {};
if (!isPrimitive(p) || !isContextSpecific(p))
{
ec = error::malformed_encoding;
return{};
}
if (p.tag != 1)
{
ec = error::unexpected_tag;
return {};
}
auto cost = parseInteger<std::uint32_t>(s, p.length, ec);
if (ec)
return {};
if (!s.empty())
{
ec = error::trailing_garbage;
return {};
}
switch (type)
{
case Type::preimageSha256:
if (cost > PreimageSha256::maxPreimageLength)
{
std::size_t len;
std::tie (start, len) =
oer::decode_length (start, finish);
// Incorrect signature length
if (len != c.fingerprint.size())
return boost::none;
// Short buffer
if (std::distance (start, finish) < len)
return boost::none;
auto p = c.fingerprint.data();
while (len--)
*p++ = *start++;
ec = error::preimage_too_long;
return {};
}
break;
if (start == finish)
return boost::none;
std::tie (start, c.maxFulfillmentLength) =
oer::decode_varuint<std::uint16_t> (
start, finish);
// The maximum supported length of a fulfillment is
// the largest allowable value, so checking here is
// not helpful.
return c;
default:
break;
}
catch (std::exception const&)
return std::make_unique<Condition>(type, cost, std::move(b));
}
}
std::unique_ptr<Condition>
Condition::deserialize(Slice s, std::error_code& ec)
{
// Per the RFC, in a condition we choose a type based
// on the tag of the item we contain:
//
// Condition ::= CHOICE {
// preimageSha256 [0] SimpleSha256Condition,
// prefixSha256 [1] CompoundSha256Condition,
// thresholdSha256 [2] CompoundSha256Condition,
// rsaSha256 [3] SimpleSha256Condition,
// ed25519Sha256 [4] SimpleSha256Condition
// }
if (s.empty())
{
return boost::none;
ec = error::buffer_empty;
return {};
}
}
std::string
to_string (Condition const& c)
{
return std::string("cc:") +
to_hex (c.type) + ":" +
to_hex (c.featureBitmask) + ":" +
base64url_encode(c.fingerprint) + ":" +
to_dec (c.maxFulfillmentLength);
}
using namespace der;
std::vector<std::uint8_t>
to_blob (Condition const& c)
{
// TODO: optimize this
std::vector<std::uint8_t> v;
v.reserve (48);
auto const p = parsePreamble(s, ec);
if (ec)
return {};
oer::encode_integer (
c.type,
std::back_inserter(v));
// All fulfillments are context-specific, constructed
// types
if (!isConstructed(p) || !isContextSpecific(p))
{
ec = error::malformed_encoding;
return {};
}
oer::encode_varuint (
c.featureBitmask,
std::back_inserter(v));
if (p.length > s.size())
{
ec = error::buffer_underfull;
return {};
}
oer::encode_octetstring (
c.fingerprint.size(),
c.fingerprint.begin(),
c.fingerprint.end(),
std::back_inserter(v));
if (s.size() > maxSerializedCondition)
{
ec = error::large_size;
return {};
}
oer::encode_varuint (
c.maxFulfillmentLength,
std::back_inserter(v));
std::unique_ptr<Condition> c;
return v;
switch (p.tag)
{
case 0: // PreimageSha256
c = detail::loadSimpleSha256(
Type::preimageSha256,
Slice(s.data(), p.length), ec);
if (!ec)
s += p.length;
break;
case 1: // PrefixSha256
ec = error::unsupported_type;
return {};
case 2: // ThresholdSha256
ec = error::unsupported_type;
return {};
case 3: // RsaSha256
ec = error::unsupported_type;
return {};
case 4: // Ed25519Sha256
ec = error::unsupported_type;
return {};
default:
ec = error::unknown_type;
return {};
}
if (!s.empty())
{
ec = error::trailing_garbage;
return {};
}
return c;
}
}
}

View File

@@ -1,115 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/Ed25519.h>
#include <ripple/conditions/impl/base64.h>
#include <ripple/basics/contract.h>
#include <ed25519-donna/ed25519.h>
namespace ripple {
namespace cryptoconditions {
Ed25519::Ed25519 (
SecretKey const& secretKey,
PublicKey const& publicKey,
Slice message)
{
if (publicKeyType (publicKey) != KeyType::ed25519)
LogicError ("An Ed25519 public key is required.");
// When PublicKey wraps an Ed25519 key it prefixes
// the key itself with a 0xED byte. We carefully
// skip that byte.
std::memcpy (
payload_.data(),
publicKey.data() + 1,
publicKey.size() - 1);
// Now sign:
ed25519_sign (
message.data(),
message.size(),
secretKey.data(),
payload_.data(),
payload_.data() + pubkey_size_);
}
/** Create a fulfillment given a secret key and the message */
Ed25519::Ed25519 (
SecretKey const& secretKey,
Slice message)
{
// First derive the public key, and place it in the
// payload:
ed25519_publickey (
secretKey.data(),
payload_.data());
ed25519_sign (
message.data(),
message.size(),
secretKey.data(),
payload_.data(),
payload_.data() + pubkey_size_);
}
Condition
Ed25519::condition() const
{
Condition cc;
cc.type = type();
cc.featureBitmask = features();
cc.maxFulfillmentLength = payloadSize();
std::memcpy (
cc.fingerprint.data(),
payload_.data(),
pubkey_size_);
return cc;
}
bool
Ed25519::validate (Slice data) const
{
return ed25519_sign_open (
data.data(),
data.size(),
payload_.data(),
payload_.data() + pubkey_size_) == 0;
}
bool
Ed25519::parsePayload (Slice s)
{
// The payload consists of 96 consecutive bytes:
// The public key is the first 32 and the
// remaining 64 bytes are the signature.
if (s.size() != sizeof(payload_))
return false;
std::memcpy (payload_.data(), s.data(), s.size());
return true;
}
}
}

View File

@@ -19,10 +19,7 @@
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/PreimageSha256.h>
#include <ripple/conditions/PrefixSha256.h>
#include <ripple/conditions/RsaSha256.h>
#include <ripple/conditions/Ed25519.h>
#include <ripple/conditions/impl/PreimageSha256.h>
#include <ripple/conditions/impl/utils.h>
#include <boost/regex.hpp>
#include <boost/optional.hpp>
@@ -33,7 +30,7 @@ namespace ripple {
namespace cryptoconditions {
bool
fulfills (
match (
Fulfillment const& f,
Condition const& c)
{
@@ -42,16 +39,9 @@ fulfills (
if (f.type() != c.type)
return false;
// Ensure that the condition is well-formed
if (!validate (c))
return false;
// The fulfillment payload can be no larger than the
// what the condition allows.
if (f.payloadSize() > c.maxFulfillmentLength)
return false;
return f.condition() == c;
// Derive the condition from the given fulfillment
// and ensure that it matches the given condition.
return c == f.condition();
}
bool
@@ -60,11 +50,11 @@ validate (
Condition const& c,
Slice m)
{
return fulfills (f, c) && f.validate (m);
return match (f, c) && f.validate (m);
}
bool
validateTrigger (
validate (
Fulfillment const& f,
Condition const& c)
{
@@ -72,157 +62,100 @@ validateTrigger (
}
std::unique_ptr<Fulfillment>
loadFulfillment (std::uint16_t type, Slice payload)
Fulfillment::deserialize(
Slice s,
std::error_code& ec)
{
std::unique_ptr<Fulfillment> p;
// Per the RFC, in a fulfillment we choose a type based
// on the tag of the item we contain:
//
// Fulfillment ::= CHOICE {
// preimageSha256 [0] PreimageFulfillment ,
// prefixSha256 [1] PrefixFulfillment,
// thresholdSha256 [2] ThresholdFulfillment,
// rsaSha256 [3] RsaSha256Fulfillment,
// ed25519Sha256 [4] Ed25519Sha512Fulfillment
// }
switch (type)
if (s.empty())
{
case condition_hashlock:
p = std::make_unique<PreimageSha256>();
ec = error::buffer_empty;
return nullptr;
}
using namespace der;
auto const p = parsePreamble(s, ec);
if (ec)
return nullptr;
// All fulfillments are context-specific, constructed types
if (!isConstructed(p) || !isContextSpecific(p))
{
ec = error::malformed_encoding;
return nullptr;
}
if (p.length > s.size())
{
ec = error::buffer_underfull;
return {};
}
if (p.length < s.size())
{
ec = error::buffer_overfull;
return {};
}
if (p.length > maxSerializedFulfillment)
{
ec = error::large_size;
return {};
}
std::unique_ptr<Fulfillment> f;
switch (static_cast<Type>(p.tag))
{
case Type::preimageSha256:
f = PreimageSha256::deserialize(Slice(s.data(), p.length), ec);
if (ec)
return {};
s += p.length;
break;
case condition_prefix_sha256:
p = std::make_unique<PrefixSha256>();
case Type::prefixSha256:
ec = error::unsupported_type;
return {};
break;
case condition_rsa_sha256:
p = std::make_unique<RsaSha256>();
case Type::thresholdSha256:
ec = error::unsupported_type;
return {};
break;
case condition_ed25519:
p = std::make_unique<Ed25519>();
case Type::rsaSha256:
ec = error::unsupported_type;
return {};
break;
case Type::ed25519Sha256:
ec = error::unsupported_type;
return {};
default:
throw std::domain_error (
"Unknown cryptocondition type " +
std::to_string (type));
ec = error::unknown_type;
return {};
}
// If the payload can't be parsed, the load should
// fail.
if (p && !p->parsePayload(payload))
p.reset();
return p;
}
// Parse a condition from its string form
std::unique_ptr<Fulfillment>
loadFulfillment (std::string const& s)
{
// CHECKME: custom parser maybe? probably faster but
// more work and probability of error.
// TODO: use two regex: one that accepts anything the
// standard supports and one which accepts only what
// we support. Parse with both for improved errors?
static boost::regex const re_current (
"^" // start of line
"cf:" // 'cf' for fulfillment
"([1-9a-f][0-9a-f]{0,3}|0):" // type
"([a-zA-Z0-9_-]*)" // fulfillment payload (base64url)
"$" // end of line
, boost::regex_constants::optimize
);
try
if (!s.empty())
{
boost::smatch match;
if (!boost::regex_match (s, match, re_current))
return nullptr;
std::uint16_t const type =
parse_hexadecimal<std::uint16_t>(match[1]);
auto payload = base64url_decode (match[2]);
if (payload.size() > maxSupportedFulfillmentLength)
return nullptr;
return loadFulfillment (type, makeSlice (payload));
ec = error::trailing_garbage;
return {};
}
catch (std::exception const&)
{
return nullptr;
}
}
std::unique_ptr<Fulfillment>
loadFulfillment (Slice s)
{
if (s.empty())
return nullptr;
try
{
auto start = s.data();
auto finish = s.data() + s.size();
std::uint16_t type;
std::size_t len;
std::tie (start, type) =
oer::decode_integer<std::uint16_t> (
start, finish);
if (!isCondition (type))
return nullptr;
if (start == finish)
return nullptr;
std::tie (start, len) =
oer::decode_length(
start, finish);
if (len)
{
if (len > maxSupportedFulfillmentLength)
return nullptr;
if (std::distance (start, finish) < len)
return nullptr;
}
return loadFulfillment (type, Slice{ start, len });
}
catch (std::exception const&)
{
return nullptr;
}
}
std::string
to_string (Fulfillment const& f)
{
return std::string("cf:") + to_hex(f.type())
+ ":" + base64url_encode (f.payload());
}
std::vector<std::uint8_t>
to_blob (Fulfillment const& f)
{
// NIKB TODO optimize this
std::vector<std::uint8_t> v;
auto const p = f.payload();
oer::encode_integer (
f.type(),
std::back_inserter(v));
oer::encode_length (
p.size(), std::back_inserter(v));
oer::encode_octetstring (
p.data(),
p.data() + p.size(),
std::back_inserter(v));
return v;
return f;
}
}

View File

@@ -0,0 +1,158 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef RIPPLE_CONDITIONS_PREIMAGE_SHA256_H
#define RIPPLE_CONDITIONS_PREIMAGE_SHA256_H
#include <ripple/basics/Buffer.h>
#include <ripple/basics/Slice.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/impl/error.h>
#include <ripple/protocol/digest.h>
#include <memory>
namespace ripple {
namespace cryptoconditions {
class PreimageSha256 final
: public Fulfillment
{
public:
/** The maximum allowed length of a preimage.
The specification does not specify a minimum supported
length, nor does it require all conditions to support
the same minimum length.
While future versions of this code will never lower
this limit, they may opt to raise it.
*/
static constexpr std::size_t maxPreimageLength = 128;
/** Parse the payload for a PreimageSha256 condition
@param s A slice containing the DER encoded payload
@param ec indicates success or failure of the operation
@return the preimage, if successful; empty pointer otherwise.
*/
static
std::unique_ptr<Fulfillment>
deserialize(
Slice s,
std::error_code& ec)
{
// Per the RFC, a preimage fulfulliment is defined as
// follows:
//
// PreimageFulfillment ::= SEQUENCE {
// preimage OCTET STRING
// }
using namespace der;
auto p = parsePreamble(s, ec);
if (ec)
return nullptr;
if (!isPrimitive(p) || !isContextSpecific(p))
{
ec = error::incorrect_encoding;
return {};
}
if (p.tag != 0)
{
ec = error::unexpected_tag;
return {};
}
if (s.size() != p.length)
{
ec = error::trailing_garbage;
return {};
}
if (s.size() > maxPreimageLength)
{
ec = error::preimage_too_long;
return {};
}
auto b = parseOctetString(s, p.length, ec);
if (ec)
return {};
return std::make_unique<PreimageSha256>(std::move(b));
}
private:
Buffer payload_;
public:
PreimageSha256(Buffer&& b) noexcept
: payload_(std::move(b))
{
}
PreimageSha256(Slice s) noexcept
: payload_(s)
{
}
Type
type() const override
{
return Type::preimageSha256;
}
Buffer
fingerprint() const override
{
sha256_hasher h;
h(payload_.data(), payload_.size());
auto const d = static_cast<sha256_hasher::result_type>(h);
return{ d.data(), d.size() };
}
std::uint32_t
cost() const override
{
return static_cast<std::uint32_t>(payload_.size());
}
Condition
condition() const override
{
return { type(), cost(), fingerprint() };
}
bool
validate(Slice) const override
{
// Perhaps counterintuitively, the message isn't
// relevant.
return true;
}
};
}
}
#endif

View File

@@ -1,314 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/RsaSha256.h>
#include <ripple/conditions/impl/base64.h>
#include <ripple/protocol/digest.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/sha.h>
#include <boost/algorithm/clamp.hpp>
#include <algorithm>
#include <iterator>
namespace ripple {
namespace cryptoconditions {
namespace detail {
struct rsa_deleter
{
void operator() (RSA* rsa) const
{
RSA_free (rsa);
}
};
using RsaKey = std::unique_ptr<RSA, rsa_deleter>;
struct bn_deleter
{
void operator() (BIGNUM* bn) const
{
BN_free (bn);
}
};
using BigNum = std::unique_ptr<BIGNUM, bn_deleter>;
// Check whether the public modulus meets the length
// requirements imposed by section 4.4.1 of the RFC.
bool
checkModulusLength (int len)
{
if (len <= 0)
return false;
return len == boost::algorithm::clamp(len, 128, 512);
}
bool
signHelper (
RSA* key,
Slice message,
Buffer& modulus,
Buffer& signature)
{
int const keySize = RSA_size(key);
if (!checkModulusLength (keySize))
return false;
sha256_hasher h;
h (message.data(), message.size());
auto digest = static_cast<sha256_hasher::result_type>(h);
Buffer buf;
// Pad the result (-1 -> use hash length as salt length)
if (!RSA_padding_add_PKCS1_PSS(key,
buf.alloc(keySize), digest.data(),
EVP_sha256(), -1))
return false;
// Sign - we've manually padded the input already.
auto ret = RSA_private_encrypt(keySize, buf.data(),
signature.alloc (buf.size()), key, RSA_NO_PADDING);
if (ret == -1)
return false;
BN_bn2bin (key->n, modulus.alloc(BN_num_bytes (key->n)));
return true;
}
bool
validateHelper (
RSA* key,
Slice message,
Slice signature)
{
int const keySize = RSA_size(key);
if (!checkModulusLength (keySize))
return false;
Buffer buf;
auto ret = RSA_public_decrypt(
keySize,
signature.data(),
buf.alloc (keySize),
key,
RSA_NO_PADDING);
if (ret == -1)
return false;
sha256_hasher h;
h (message.data(), message.size());
auto digest = static_cast<sha256_hasher::result_type>(h);
return RSA_verify_PKCS1_PSS(key, digest.data(), EVP_sha256(), buf.data(), -1) == 1;
}
bool
parsePayloadHelper(
Slice s,
Buffer& modulus,
Buffer& signature)
{
auto start = s.data ();
auto finish = s.data () + s.size();
std::size_t len;
std::tie (start, len) = oer::decode_length (
start, finish);
if (std::distance (start, finish) < len)
return false;
std::memcpy (modulus.alloc (len), start, len);
std::advance (start, len);
std::tie (start, len) = oer::decode_length (
start, finish);
if (std::distance (start, finish) < len)
return false;
std::memcpy (signature.alloc (len), start, len);
std::advance (start, len);
// Per 4.4.2 of the RFC we must check whether the
// signature and modulus consist of the same number
// of octets:
if (signature.size() != modulus.size())
return false;
// Enforce constraints from the RFC:
BigNum sig (BN_bin2bn (
signature.data(), signature.size(), nullptr));
BigNum mod (BN_bin2bn (
modulus.data(), modulus.size(), nullptr));
if (!sig || !mod)
return false;
// Per 4.4.1 of the RFC we are required to reject
// moduli smaller than 128 bytes or greater than
// 512 bytes.
int modBytes = BN_num_bytes (mod.get());
if (!checkModulusLength (modBytes))
return false;
// Per 4.4.2 of the RFC we must check that the signature
// is numerically less than the modulus:
return BN_cmp (sig.get(), mod.get()) < 0;
}
}
Condition
RsaSha256::condition() const
{
std::vector<std::uint8_t> m;
m.reserve (1024);
oer::encode_octetstring (
modulus_.size(),
modulus_.data(),
modulus_.data() + modulus_.size(),
std::back_inserter(m));
sha256_hasher h;
h (m.data(), m.size());
Condition cc;
cc.type = type();
cc.featureBitmask = features();
cc.maxFulfillmentLength = payloadSize();
cc.fingerprint = static_cast<sha256_hasher::result_type>(h);
return cc;
}
std::size_t
RsaSha256::payloadSize () const
{
return
oer::predict_octetstring_size (modulus_.size()) +
oer::predict_octetstring_size (signature_.size());
}
Buffer
RsaSha256::payload() const
{
Buffer b (payloadSize());
auto out = oer::encode_octetstring (
modulus_.size(),
modulus_.data(),
modulus_.data() + modulus_.size(),
b.data());
oer::encode_octetstring (
signature_.size(),
signature_.data(),
signature_.data() + modulus_.size(),
out);
return b;
}
bool
RsaSha256::validate (Slice data) const
{
if (!ok())
return false;
detail::RsaKey rsa (RSA_new());
rsa->n = BN_new();
BN_bin2bn(modulus_.data(), modulus_.size(), rsa->n);
rsa->e = BN_new();
BN_set_word (rsa->e, 65537);
return detail::validateHelper (rsa.get(), data, signature_);
}
/** Sign the given message with an RSA key */
bool
RsaSha256::sign (
std::string const& key,
Slice message)
{
// This ugly const_cast/reinterpret_cast is needed
// on some machines. Although the documentation
// suggests that the function accepts a void const*
// argument, apparently some platforms have OpenSSL
// libraries that are up-to-date but accept void*.
auto bio = BIO_new_mem_buf(
const_cast<void*>(static_cast<void const*>(key.data())),
key.size());
if (!bio)
return false;
detail::RsaKey rsa (PEM_read_bio_RSAPrivateKey(
bio, NULL, NULL, NULL));
BIO_free(bio);
if (!rsa)
return false;
if (detail::signHelper (rsa.get(), message, modulus_, signature_))
return true;
modulus_.clear();
signature_.clear();
return false;
}
bool
RsaSha256::parsePayload (Slice s)
{
// The payload may not be empty
if (!s.empty() && detail::parsePayloadHelper (s, modulus_, signature_))
return true;
// Clear the state
modulus_.clear();
signature_.clear();
return false;
}
}
}

View File

@@ -1,204 +0,0 @@
//
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_BASE64_HPP
#define BEAST_DETAIL_BASE64_HPP
#include <ripple/basics/Buffer.h>
#include <ripple/basics/Slice.h>
#include <array>
#include <cctype>
#include <string>
#include <cstdint>
#include <vector>
/*
Portions from http://www.adp-gmbh.ch/cpp/common/base64.html
Copyright notice:
base64.cpp and base64.h
Copyright (C) 2004-2008 Ren<65> Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
Ren<65> Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
namespace ripple {
namespace cryptoconditions {
// NIKB NOTE: This has *NOT* standard base64 - it's base64url,
// which replaces the `+` with a `-` and the the `/` with a `_`
// with the padding suppressed on encoding and rejected on
// decoding.
template <class = void>
std::string const&
base64url_alphabet()
{
static std::string const alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-_";
return alphabet;
}
inline
bool
is_base64url(unsigned char c)
{
return (std::isalnum(c) || (c == '-') || (c == '_'));
}
template <class = void>
std::string
base64url_encode (std::uint8_t const* data,
std::size_t in_len)
{
unsigned char c3[3], c4[4];
int i = 0;
int j = 0;
std::string ret;
ret.reserve (3 + in_len * 8 / 6);
char const* alphabet (base64url_alphabet().data());
while(in_len--)
{
c3[i++] = *(data++);
if(i == 3)
{
c4[0] = (c3[0] & 0xfc) >> 2;
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
c4[3] = c3[2] & 0x3f;
for(i = 0; (i < 4); i++)
ret += alphabet[c4[i]];
i = 0;
}
}
if(i)
{
for(j = i; j < 3; j++)
c3[j] = '\0';
c4[0] = (c3[0] & 0xfc) >> 2;
c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4);
c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6);
c4[3] = c3[2] & 0x3f;
for(j = 0; (j < i + 1); j++)
ret += alphabet[c4[j]];
}
return ret;
}
template <size_t N>
std::string
base64url_encode (std::array<std::uint8_t, N> const& d)
{
return base64url_encode (d.data(), d.size());
}
template <class = void>
std::string
base64url_encode (std::vector<std::uint8_t> const& d)
{
return base64url_encode (d.data(), d.size());
}
template <class = void>
std::string
base64url_encode (Buffer const& d)
{
return base64url_encode (d.data(), d.size());
}
template <class = void>
std::string
base64url_encode (Slice d)
{
return base64url_encode (d.data(), d.size());
}
template <class = void>
std::vector<std::uint8_t>
base64url_decode(std::string const& data)
{
int in_len = data.size();
std::uint8_t c4[4];
int i = 0;
int j = 0;
int in_ = 0;
std::vector<std::uint8_t> ret;
ret.reserve (in_len * 6 / 8);
while(in_len-- && is_base64url(data[in_]))
{
c4[i++] = data[in_]; in_++;
if(i == 4) {
for(i = 0; i < 4; i++)
c4[i] = static_cast<unsigned char>(
base64url_alphabet().find(c4[i]));
ret.push_back ((c4[0] << 2) + ((c4[1] & 0x30) >> 4));
ret.push_back (((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2));
ret.push_back (((c4[2] & 0x3) << 6) + c4[3]);
i = 0;
}
}
if(i)
{
for(j = i; j < 4; j++)
c4[j] = 0;
for(j = 0; j < 4; j++)
c4[j] = static_cast<unsigned char>(
base64url_alphabet().find(c4[j]));
std::uint8_t c3[3];
c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4);
c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2);
c3[2] = ((c4[2] & 0x3) << 6) + c4[3];
for(j = 0; (j < i - 1); j++)
ret.push_back (c3[j]);
}
return ret;
}
}
}
#endif

View File

@@ -0,0 +1,143 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/conditions/impl/error.h>
#include <system_error>
#include <string>
#include <type_traits>
namespace ripple {
namespace cryptoconditions {
namespace detail {
class cryptoconditions_error_category
: public std::error_category
{
public:
const char*
name() const noexcept override
{
return "cryptoconditions";
}
std::string
message(int ev) const override
{
switch (static_cast<error>(ev))
{
case error::unsupported_type:
return "Specification: Requested type not supported.";
case error::unsupported_subtype:
return "Specification: Requested subtype not supported.";
case error::unknown_type:
return "Specification: Requested type not recognized.";
case error::unknown_subtype:
return "Specification: Requested subtypes not recognized.";
case error::fingerprint_size:
return "Specification: Incorrect fingerprint size.";
case error::incorrect_encoding:
return "Specification: Incorrect encoding.";
case error::trailing_garbage:
return "Bad buffer: contains trailing garbage.";
case error::buffer_empty:
return "Bad buffer: no data.";
case error::buffer_overfull:
return "Bad buffer: overfull.";
case error::buffer_underfull:
return "Bad buffer: underfull.";
case error::malformed_encoding:
return "Malformed DER encoding.";
case error::unexpected_tag:
return "Malformed DER encoding: Unexpected tag.";
case error::short_preamble:
return "Malformed DER encoding: Short preamble.";
case error::long_tag:
return "Implementation limit: Overlong tag.";
case error::large_size:
return "Implementation limit: Large payload.";
case error::preimage_too_long:
return "Implementation limit: Specified preimage is too long.";
case error::generic:
default:
return "generic error";
}
}
std::error_condition
default_error_condition(int ev) const noexcept override
{
return std::error_condition{ ev, *this };
}
bool
equivalent(
int ev,
std::error_condition const& condition) const noexcept override
{
return &condition.category() == this &&
condition.value() == ev;
}
bool
equivalent(
std::error_code const& error,
int ev) const noexcept override
{
return &error.category() == this &&
error.value() == ev;
}
};
inline
std::error_category const&
get_cryptoconditions_error_category()
{
static cryptoconditions_error_category const cat{};
return cat;
}
} // detail
std::error_code
make_error_code(error ev)
{
return std::error_code {
static_cast<std::underlying_type<error>::type>(ev),
detail::get_cryptoconditions_error_category()
};
}
}
}

View File

@@ -17,67 +17,51 @@
*/
//==============================================================================
#ifndef RIPPLE_CONDITIONS_RSA_SHA256_H
#define RIPPLE_CONDITIONS_RSA_SHA256_H
#ifndef RIPPLE_CONDITIONS_ERROR_H
#define RIPPLE_CONDITIONS_ERROR_H
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/basics/Buffer.h>
#include <cstdint>
#include <system_error>
#include <string>
namespace ripple {
namespace cryptoconditions {
class RsaSha256 final
: public Fulfillment
enum class error
{
Buffer modulus_;
Buffer signature_;
public:
RsaSha256 () = default;
Condition
condition() const override;
std::uint16_t
type () const override
{
return condition_rsa_sha256;
}
std::uint32_t
features () const override
{
return feature_rsa_pss | feature_sha256;
}
bool
ok () const override
{
return !modulus_.empty() && !signature_.empty();
}
std::size_t
payloadSize () const override;
Buffer
payload() const override;
bool
validate (Slice data) const override;
/** Sign the given message with an RSA key */
bool sign (
std::string const& key,
Slice message);
bool
parsePayload (Slice s) override;
generic = 1,
unsupported_type,
unsupported_subtype,
unknown_type,
unknown_subtype,
fingerprint_size,
incorrect_encoding,
trailing_garbage,
buffer_empty,
buffer_overfull,
buffer_underfull,
malformed_encoding,
short_preamble,
unexpected_tag,
long_tag,
large_size,
preimage_too_long
};
}
std::error_code
make_error_code(error ev);
}
} // cryptoconditions
} // ripple
namespace std
{
template<>
struct is_error_code_enum<ripple::cryptoconditions::error>
{
static bool const value = true;
};
} // std
#endif

View File

@@ -21,6 +21,9 @@
#define RIPPLE_CONDITIONS_UTILS_H
#include <ripple/basics/strHex.h>
#include <ripple/conditions/impl/error.h>
#include <boost/dynamic_bitset.hpp>
#include <limits>
#include <stdexcept>
#include <string>
#include <vector>
@@ -31,338 +34,208 @@
namespace ripple {
namespace cryptoconditions {
inline
std::string
hexstr (std::vector<std::uint8_t> const& data)
// A collection of functions to decode binary blobs
// encoded with X.690 Distinguished Encoding Rules.
//
// This is a very trivial decoder and only implements
// the bare minimum needed to support PreimageSha256.
namespace der {
// The preamble encapsulates the DER identifier and
// length octets:
struct Preamble
{
std::string s;
s.reserve (data.size() * 2);
std::uint8_t type = 0;
std::size_t tag = 0;
std::size_t length = 0;
};
for (auto d : data)
{
s.push_back (charHex (d >> 4));
s.push_back (charHex (d & 15));
}
return s;
inline
bool
isPrimitive(Preamble const& p)
{
return (p.type & 0x20) == 0;
}
inline
std::vector<std::uint8_t>
hexblob (std::string const& s)
bool
isConstructed(Preamble const& p)
{
std::vector<std::uint8_t> result;
result.reserve (1 + (s.size () / 2));
auto iter = s.cbegin ();
if (s.size () & 1)
{
int c = charUnHex (*iter++);
if (c < 0)
Throw<std::runtime_error>("Invalid hex in blob");
result.push_back(c);
}
while (iter != s.cend ())
{
int cHigh = charUnHex (*iter++);
if (cHigh < 0)
Throw<std::runtime_error>("Invalid hex in blob");
int cLow = charUnHex (*iter);
if (cLow < 0)
Throw<std::runtime_error>("Invalid hex in blob");
iter++;
result.push_back (
static_cast<std::uint8_t>(cHigh << 4) |
static_cast<std::uint8_t>(cLow));
}
return result;
return !isPrimitive(p);
}
template <class T>
T parse_decimal(std::string const& s)
{
T t = 0;
for (auto const c : s)
{
if (c < '0' || c > '9')
throw std::domain_error ("invalid decimal digit");
t = (t * 10) + (c - '0');
}
return t;
}
template <class T>
T parse_hexadecimal(std::string const& s)
{
T t = 0;
for (auto const c : s)
{
if (c >= '0' && c <= '9')
t = (t * 16) + (c - '0');
else if (c >= 'a' && c <= 'f')
t = (t * 16) + 10 + (c - 'a');
else if (c >= 'A' && c <= 'F')
t = (t * 16) + 10 + (c - 'A');
else
throw std::domain_error ("invalid hexadecimal digit");
}
return t;
}
template <class Integer>
std::string
to_hex (Integer value)
{
std::stringstream ss;
ss << std::hex << value;
return ss.str();
}
template <class Integer>
std::string
to_dec (Integer value)
{
std::stringstream ss;
ss << std::dec << value;
return ss.str();
}
// ISO/IEC 8825/7 or ITU-T X.696: Octet Encoding Rules
// FIXME: This assumes a little-endian architecture!
namespace oer
{
// Simple conversion: write integer as big-endian byte stream
// This needs to be improved and optimized:
template <class Integer, class OutputIt>
void
encode_integer(Integer value, OutputIt out)
{
static_assert (
std::is_same<Integer, std::uint8_t>::value ||
std::is_same<Integer, std::uint16_t>::value ||
std::is_same<Integer, std::uint32_t>::value ||
std::is_same<Integer, std::uint64_t>::value,
"encode_integer accepts only std::uint{8,16,32,64}_t");
std::size_t n = sizeof(Integer);
while(n--)
{
*out++ = static_cast<std::uint8_t>(
(value >> (n * 8)) & 0xFF);
}
}
// Simple conversion: big-endian byte stream to integer
template <class Integer, class InputIt>
std::pair<InputIt, Integer>
decode_integer(InputIt begin, InputIt end)
{
static_assert (
std::is_same<Integer, std::uint8_t>::value ||
std::is_same<Integer, std::uint16_t>::value ||
std::is_same<Integer, std::uint32_t>::value ||
std::is_same<Integer, std::uint64_t>::value,
"decode_integer accepts only std::uint{8,16,32,64}_t");
std::size_t size = std::distance (begin, end);
if (size < sizeof(Integer))
Throw<std::length_error>("short integer: " + std::to_string(size));
Integer res = 0;
for (std::size_t i = 0; i < sizeof(Integer); ++i)
res = (res << 8) | *begin++;
return { begin, res };
}
template <class OutputIt>
inline
OutputIt
encode_length (std::size_t len, OutputIt out)
bool
isUniversal(Preamble const& p)
{
if (len <= 0x7F)
{
*out++ = static_cast<std::uint8_t>(len & 0x7F);
return out;
}
// Decide how many bytes we need:
if (len <= 0xFFFF)
{
*out++ = 0x82;
*out++ = static_cast<std::uint8_t>((len >> 8) & 0xFF);
*out++ = static_cast<std::uint8_t>(len & 0xFF);
return out;
}
if (len <= 0xFFFFFF)
{
*out++ = 0x83;
*out++ = static_cast<std::uint8_t>((len >> 16) & 0xFF);
*out++ = static_cast<std::uint8_t>((len >> 8) & 0xFF);
*out++ = static_cast<std::uint8_t>(len & 0xFF);
return out;
}
if (len <= 0xFFFFFFFF)
{
*out++ = 0x84;
*out++ = static_cast<std::uint8_t>((len >> 24) & 0xFF);
*out++ = static_cast<std::uint8_t>((len >> 16) & 0xFF);
*out++ = static_cast<std::uint8_t>((len >> 8) & 0xFF);
*out++ = static_cast<std::uint8_t>(len & 0xFF);
return out;
}
// Note: OER can represent lengths up to (2^1016) - 1,
// which is, truly, enough for everyone. We never
// exceed 2^32.
Throw<std::length_error>("overlong encoding length: " + std::to_string(len));
return (p.type & 0xC0) == 0;
}
// A "streambuf" would serve us better here - instead of the
// crazy paired return, we consume data from it and things
// just magically work.
template <class InputIt>
std::pair<InputIt, std::size_t>
decode_length (InputIt begin, InputIt end)
{
if (begin == end)
Throw<std::length_error>("empty buffer");
std::size_t bytes = *begin++;
if (bytes < 128)
return { begin, bytes };
bytes &= 0x7F;
if (bytes > 4)
Throw<std::length_error>("overlong encoded length: " + std::to_string(bytes));
std::size_t len = 0;
if (std::distance (begin, end) < bytes)
Throw<std::length_error>("short encoded length: " + std::to_string(bytes));
while (bytes--)
len = (len << 8) | *begin++;
return { begin, len };
}
/** Encode a fixed-size octet string: OER 2.6 (2) */
template <class InputIt, class OutputIt>
OutputIt
encode_octetstring(InputIt begin, InputIt end, OutputIt out)
{
while (begin != end)
*out++ = *begin++;
return out;
}
/** Encode a dynamic size octet string: OER 2.6 (1) */
inline
std::size_t
predict_octetstring_size(std::size_t size)
bool
isApplication(Preamble const& p)
{
// Alternatively, always guess 4 + size and call it a day?
if (size <= 0x7F)
return size + 1;
// Decide how many bytes we need:
if (size <= 0xFFFF)
return size + 3;
if (size <= 0xFFFFFF)
return size + 4;
if (size <= 0xFFFFFFFF)
return size + 5;
Throw<std::length_error>("overlong encoding length: " + std::to_string(size));
return (p.type & 0xC0) == 0x40;
}
/** Encode an dynamic size octet string: OER 2.6 (1) */
template <class InputIt, class OutputIt>
OutputIt
encode_octetstring(std::size_t size, InputIt begin, InputIt end, OutputIt out)
inline
bool
isContextSpecific(Preamble const& p)
{
// This will encode the length first, followed by the
// payload octets:
return encode_octetstring (
begin,
end,
oer::encode_length (size, out));
return (p.type & 0xC0) == 0x80;
}
template <class Integer, class OutputIt>
std::enable_if_t<std::is_unsigned<Integer>::value>
encode_varuint (Integer value, OutputIt out)
inline
bool
isPrivate(Preamble const& p)
{
auto count = [](Integer n)
return (p.type & 0xC0) == 0xC0;
}
inline
Preamble
parsePreamble(Slice& s, std::error_code& ec)
{
Preamble p;
if (s.size() < 2)
{
std::size_t c = 0;
ec = error::short_preamble;
return p;
}
do
p.type = s[0] & 0xE0;
p.tag = s[0] & 0x1F;
s += 1;
if (p.tag == 0x1F)
{ // Long tag form, which we do not support:
ec = error::long_tag;
return p;
}
p.length = s[0];
s += 1;
if (p.length & 0x80)
{ // Long form length:
std::size_t const cnt = p.length & 0x7F;
if (cnt == 0)
{
n >>= 8;
++c;
} while (n);
ec = error::malformed_encoding;
return p;
}
return c;
};
if (cnt > sizeof(std::size_t))
{
ec = error::large_size;
return p;
}
std::size_t c = count (value);
if (cnt > s.size())
{
ec = error::short_preamble;
return p;
}
out = encode_length (c, out);
p.length = 0;
while(c--)
{
*out++ = static_cast<std::uint8_t>(
(value >> (c * 8)) & 0xFF);
for (std::size_t i = 0; i != cnt; ++i)
p.length = (p.length << 8) + s[i];
s += cnt;
if (p.length == 0)
{
ec = error::malformed_encoding;
return p;
}
}
return p;
}
template <class Integer, class InputIt>
std::enable_if_t<std::is_unsigned<Integer>::value, std::pair<InputIt, Integer>>
decode_varuint (InputIt begin, InputIt end)
inline
Buffer
parseOctetString(Slice& s, std::uint32_t count, std::error_code& ec)
{
auto y = decode_length (begin, end);
if (count > s.size())
{
ec = error::buffer_underfull;
return {};
}
if (y.second > sizeof(Integer))
Throw<std::length_error>("Encoded integer exceeds allowable range: " + std::to_string(y.second));
if (count > 65535)
{
ec = error::large_size;
return {};
}
Integer x = 0;
for (std::size_t i = 0; i != y.second; ++i)
x = (x << 8) + *y.first++;
return { y.first, x };
Buffer b(s.data(), count);
s += count;
return b;
}
template <class Integer>
Integer
parseInteger(Slice& s, std::size_t count, std::error_code& ec)
{
Integer v{0};
if (s.empty())
{
// can never have zero sized integers
ec = error::malformed_encoding;
return v;
}
if (count > s.size())
{
ec = error::buffer_underfull;
return v;
}
const bool isSigned = std::numeric_limits<Integer>::is_signed;
// unsigned types may have a leading zero octet
const size_t maxLength = isSigned ? sizeof(Integer) : sizeof(Integer) + 1;
if (count > maxLength)
{
ec = error::large_size;
return v;
}
if (!isSigned && (s[0] & (1 << 7)))
{
// trying to decode a negative number into a positive value
ec = error::malformed_encoding;
return v;
}
if (!isSigned && count == sizeof(Integer) + 1 && s[0])
{
// since integers are coded as two's complement, the first byte may
// be zero for unsigned reps
ec = error::malformed_encoding;
return v;
}
v = 0;
for (size_t i = 0; i < count; ++i)
v = (v << 8) | (s[i] & 0xff);
if (isSigned && (s[0] & (1 << 7)))
{
for (int i = count; i < sizeof(Integer); ++i)
v |= (Integer(0xff) << (8 * i));
}
s += count;
return v;
}
}
}
} // der
} // cryptoconditions
} // ripple
#endif

View File

@@ -623,7 +623,7 @@ ApplyStateTable::threadOwners (ReadView const& base,
// Nothing to do
break;
}
case ltSUSPAY:
case ltESCROW:
{
threadTx (base, meta, (*sle)[sfAccount], mods, j);
threadTx (base, meta, (*sle)[sfDestination], mods, j);

View File

@@ -36,7 +36,6 @@ feature (const char* name);
extern uint256 const featureMultiSign;
extern uint256 const featureTickets;
extern uint256 const featureSusPay;
extern uint256 const featureTrustSetAuth;
extern uint256 const featureFeeEscalation;
extern uint256 const featureOwnerPaysFee;
@@ -47,6 +46,8 @@ extern uint256 const featureFlow;
extern uint256 const featureCryptoConditions;
extern uint256 const featureTickSize;
extern uint256 const featureRIPD1368;
extern uint256 const featureEscrow;
extern uint256 const featureCryptoConditionsSuite;
} // ripple

View File

@@ -234,9 +234,9 @@ Keylet page (uint256 const& key)
return { ltDIR_NODE, key };
}
/** A SuspendedPayment */
/** An escrow entry */
Keylet
susPay (AccountID const& source, std::uint32_t seq);
escrow (AccountID const& source, std::uint32_t seq);
/** A PaymentChannel */
Keylet

View File

@@ -78,7 +78,7 @@ enum LedgerEntryType
ltFEE_SETTINGS = 's',
ltSUSPAY = 'u',
ltESCROW = 'u',
// Simple unidirection xrp channel
ltPAYCHAN = 'x',
@@ -105,7 +105,7 @@ enum LedgerNameSpace
spaceBookDir = 'B', // Directory of order books.
spaceContract = 'c',
spaceSkipList = 's',
spaceSusPay = 'u',
spaceEscrow = 'u',
spaceAmendment = 'f',
spaceFee = 'e',
spaceTicket = 'T',

View File

@@ -35,10 +35,10 @@ enum TxType
ttINVALID = -1,
ttPAYMENT = 0,
ttSUSPAY_CREATE = 1,
ttSUSPAY_FINISH = 2,
ttESCROW_CREATE = 1,
ttESCROW_FINISH = 2,
ttACCOUNT_SET = 3,
ttSUSPAY_CANCEL = 4,
ttESCROW_CANCEL = 4,
ttREGULAR_KEY_SET = 5,
ttNICKNAME_SET = 6, // open
ttOFFER_CREATE = 7,

View File

@@ -47,7 +47,6 @@ feature (const char* name)
uint256 const featureMultiSign = feature("MultiSign");
uint256 const featureTickets = feature("Tickets");
uint256 const featureSusPay = feature("SusPay");
uint256 const featureTrustSetAuth = feature("TrustSetAuth");
uint256 const featureFeeEscalation = feature("FeeEscalation");
uint256 const featureOwnerPaysFee = feature("OwnerPaysFee");
@@ -58,5 +57,7 @@ uint256 const featureFlow = feature("Flow");
uint256 const featureCryptoConditions = feature("CryptoConditions");
uint256 const featureTickSize = feature("TickSize");
uint256 const featureRIPD1368 = feature("RIPD1368");
uint256 const featureEscrow = feature("Escrow");
uint256 const featureCryptoConditionsSuite = feature("CryptoConditionsSuite");
} // ripple

View File

@@ -313,14 +313,14 @@ Keylet page(Keylet const& root,
}
Keylet
susPay (AccountID const& source, std::uint32_t seq)
escrow (AccountID const& source, std::uint32_t seq)
{
sha512_half_hasher h;
using beast::hash_append;
hash_append(h, spaceSusPay);
hash_append(h, spaceEscrow);
hash_append(h, source);
hash_append(h, seq);
return { ltSUSPAY, static_cast<uint256>(h) };
return { ltESCROW, static_cast<uint256>(h) };
}
Keylet

View File

@@ -82,7 +82,7 @@ LedgerFormats::LedgerFormats ()
<< SOElement (sfHighQualityOut, SOE_OPTIONAL)
;
add ("SuspendedPayment", ltSUSPAY) <<
add ("Escrow", ltESCROW) <<
SOElement (sfAccount, SOE_REQUIRED) <<
SOElement (sfDestination, SOE_REQUIRED) <<
SOElement (sfAmount, SOE_REQUIRED) <<

View File

@@ -67,7 +67,7 @@ TxFormats::TxFormats ()
<< SOElement (sfDeliverMin, SOE_OPTIONAL)
;
add ("SuspendedPaymentCreate", ttSUSPAY_CREATE) <<
add ("EscrowCreate", ttESCROW_CREATE) <<
SOElement (sfDestination, SOE_REQUIRED) <<
SOElement (sfAmount, SOE_REQUIRED) <<
SOElement (sfCondition, SOE_OPTIONAL) <<
@@ -75,13 +75,13 @@ TxFormats::TxFormats ()
SOElement (sfFinishAfter, SOE_OPTIONAL) <<
SOElement (sfDestinationTag, SOE_OPTIONAL);
add ("SuspendedPaymentFinish", ttSUSPAY_FINISH) <<
add ("EscrowFinish", ttESCROW_FINISH) <<
SOElement (sfOwner, SOE_REQUIRED) <<
SOElement (sfOfferSequence, SOE_REQUIRED) <<
SOElement (sfFulfillment, SOE_OPTIONAL) <<
SOElement (sfCondition, SOE_OPTIONAL);
add ("SuspendedPaymentCancel", ttSUSPAY_CANCEL) <<
add ("EscrowCancel", ttESCROW_CANCEL) <<
SOElement (sfOwner, SOE_REQUIRED) <<
SOElement (sfOfferSequence, SOE_REQUIRED);

View File

@@ -27,6 +27,7 @@
#include <ripple/app/tx/impl/Change.cpp>
#include <ripple/app/tx/impl/CreateOffer.cpp>
#include <ripple/app/tx/impl/CreateTicket.cpp>
#include <ripple/app/tx/impl/Escrow.cpp>
#include <ripple/app/tx/impl/OfferStream.cpp>
#include <ripple/app/tx/impl/Payment.cpp>
#include <ripple/app/tx/impl/PayChan.cpp>
@@ -35,7 +36,6 @@
#include <ripple/app/tx/impl/SetSignerList.cpp>
#include <ripple/app/tx/impl/SetTrust.cpp>
#include <ripple/app/tx/impl/SignerEntries.cpp>
#include <ripple/app/tx/impl/SusPay.cpp>
#include <ripple/app/tx/impl/Taker.cpp>
#include <ripple/app/tx/impl/ApplyContext.cpp>
#include <ripple/app/tx/impl/Transactor.cpp>

View File

@@ -21,5 +21,4 @@
#include <ripple/conditions/impl/Condition.cpp>
#include <ripple/conditions/impl/Fulfillment.cpp>
#include <ripple/conditions/impl/Ed25519.cpp>
#include <ripple/conditions/impl/RsaSha256.cpp>
#include <ripple/conditions/impl/error.cpp>

View File

@@ -1,4 +1,4 @@
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012, 2013 Ripple Labs Inc.
@@ -28,67 +28,48 @@
namespace ripple {
namespace test {
struct SusPay_test : public beast::unit_test::suite
struct Escrow_test : public beast::unit_test::suite
{
// An Ed25519 conditional trigger fulfillment and its
// condition
std::array<std::uint8_t, 99> const fb1 =
// A PreimageSha256 fulfillments and its associated condition.
std::array<std::uint8_t, 4> const fb1 =
{{
0x00, 0x04, 0x60, 0x3B, 0x6A, 0x27, 0xBC, 0xCE, 0xB6, 0xA4, 0x2D, 0x62,
0xA3, 0xA8, 0xD0, 0x2A, 0x6F, 0x0D, 0x73, 0x65, 0x32, 0x15, 0x77, 0x1D,
0xE2, 0x43, 0xA6, 0x3A, 0xC0, 0x48, 0xA1, 0x8B, 0x59, 0xDA, 0x29, 0x8F,
0x89, 0x5B, 0x3C, 0xAF, 0xE2, 0xC9, 0x50, 0x60, 0x39, 0xD0, 0xE2, 0xA6,
0x63, 0x82, 0x56, 0x80, 0x04, 0x67, 0x4F, 0xE8, 0xD2, 0x37, 0x78, 0x50,
0x92, 0xE4, 0x0D, 0x6A, 0xAF, 0x48, 0x3E, 0x4F, 0xC6, 0x01, 0x68, 0x70,
0x5F, 0x31, 0xF1, 0x01, 0x59, 0x61, 0x38, 0xCE, 0x21, 0xAA, 0x35, 0x7C,
0x0D, 0x32, 0xA0, 0x64, 0xF4, 0x23, 0xDC, 0x3E, 0xE4, 0xAA, 0x3A, 0xBF,
0x53, 0xF8, 0x03,
0xA0, 0x02, 0x80, 0x00
}};
std::array<std::uint8_t, 39> const cb1 =
{{
0x00, 0x04, 0x01, 0x20, 0x20, 0x3B, 0x6A, 0x27, 0xBC, 0xCE, 0xB6, 0xA4,
0x2D, 0x62, 0xA3, 0xA8, 0xD0, 0x2A, 0x6F, 0x0D, 0x73, 0x65, 0x32, 0x15,
0x77, 0x1D, 0xE2, 0x43, 0xA6, 0x3A, 0xC0, 0x48, 0xA1, 0x8B, 0x59, 0xDA,
0x29, 0x01, 0x60
0xA0, 0x25, 0x80, 0x20, 0xE3, 0xB0, 0xC4, 0x42, 0x98, 0xFC, 0x1C, 0x14,
0x9A, 0xFB, 0xF4, 0xC8, 0x99, 0x6F, 0xB9, 0x24, 0x27, 0xAE, 0x41, 0xE4,
0x64, 0x9B, 0x93, 0x4C, 0xA4, 0x95, 0x99, 0x1B, 0x78, 0x52, 0xB8, 0x55,
0x81, 0x01, 0x00
}};
// A prefix.prefix.ed25519 conditional trigger fulfillment:
std::array<std::uint8_t, 106> const fb2 =
// Another PreimageSha256 fulfillments and its associated condition.
std::array<std::uint8_t, 7> const fb2 =
{{
0x00, 0x01, 0x67, 0x03, 0x61, 0x62, 0x63, 0x00, 0x04, 0x60, 0x76, 0xA1,
0x59, 0x20, 0x44, 0xA6, 0xE4, 0xF5, 0x11, 0x26, 0x5B, 0xCA, 0x73, 0xA6,
0x04, 0xD9, 0x0B, 0x05, 0x29, 0xD1, 0xDF, 0x60, 0x2B, 0xE3, 0x0A, 0x19,
0xA9, 0x25, 0x76, 0x60, 0xD1, 0xF5, 0xAE, 0xC6, 0xAB, 0x6A, 0x91, 0x22,
0xAF, 0xF0, 0xF7, 0xDC, 0xB9, 0x66, 0x7F, 0xF6, 0x13, 0x13, 0x68, 0x94,
0x73, 0x2B, 0x6E, 0x78, 0xC2, 0x6F, 0x5B, 0x67, 0x31, 0x01, 0xE2, 0x67,
0xFE, 0x2E, 0x2B, 0x65, 0xFA, 0x4D, 0x53, 0xDA, 0xD4, 0x78, 0xA1, 0xAD,
0xA6, 0x4D, 0x50, 0xFD, 0x1D, 0xFD, 0xB7, 0xD9, 0x49, 0x20, 0xDC, 0x3E,
0x1A, 0x56, 0x4A, 0x64, 0x7B, 0x1C, 0xBA, 0x35, 0x60, 0x01,
0xA0, 0x05, 0x80, 0x03, 0x61, 0x61, 0x61
}};
std::array<std::uint8_t, 39> const cb2 =
{{
0x00, 0x01, 0x01, 0x25, 0x20, 0x28, 0x7A, 0x8B, 0xD8, 0xAD, 0xAE, 0x8A,
0xCA, 0x0C, 0x87, 0x1C, 0xE7, 0xC2, 0x5F, 0xBA, 0xA5, 0xA8, 0xBE, 0x10,
0xD0, 0xE4, 0xDB, 0x1F, 0x56, 0xAE, 0xEE, 0x8B, 0xB3, 0xAD, 0xCE, 0xE5,
0x5B, 0x01, 0x64
0xA0, 0x25, 0x80, 0x20, 0x98, 0x34, 0x87, 0x6D, 0xCF, 0xB0, 0x5C, 0xB1,
0x67, 0xA5, 0xC2, 0x49, 0x53, 0xEB, 0xA5, 0x8C, 0x4A, 0xC8, 0x9B, 0x1A,
0xDF, 0x57, 0xF2, 0x8F, 0x2F, 0x9D, 0x09, 0xAF, 0x10, 0x7E, 0xE8, 0xF0,
0x81, 0x01, 0x03
}};
// A prefix+preimage conditional trigger fulfillment
std::array<std::uint8_t, 7> const fb3 =
// Another PreimageSha256 fulfillment and its associated condition.
std::array<std::uint8_t, 8> const fb3 =
{{
0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00,
0xA0, 0x06, 0x80, 0x04, 0x6E, 0x69, 0x6B, 0x62
}};
std::array<std::uint8_t, 39> const cb3 =
{{
0x00, 0x01, 0x01, 0x07, 0x20, 0x62, 0x36, 0xB7, 0xA8, 0x58, 0xFB, 0x35,
0x2F, 0xD5, 0xC3, 0x01, 0x3B, 0x68, 0x98, 0xCF, 0x26, 0x8B, 0x3E, 0xB8,
0x50, 0xB3, 0x4A, 0xD2, 0x65, 0x24, 0xB0, 0xF8, 0x56, 0xC3, 0x72, 0xD9,
0x73, 0x01, 0x01
0xA0, 0x25, 0x80, 0x20, 0x6E, 0x4C, 0x71, 0x45, 0x30, 0xC0, 0xA4, 0x26,
0x8B, 0x3F, 0xA6, 0x3B, 0x1B, 0x60, 0x6F, 0x2D, 0x26, 0x4A, 0x2D, 0x85,
0x7B, 0xE8, 0xA0, 0x9C, 0x1D, 0xFD, 0x57, 0x0D, 0x15, 0x85, 0x8B, 0xD4,
0x81, 0x01, 0x04
}};
static
@@ -99,7 +80,7 @@ struct SusPay_test : public beast::unit_test::suite
{
using namespace jtx;
Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentCreate";
jv[jss::TransactionType] = "EscrowCreate";
jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human();
jv[jss::Destination] = to.human();
@@ -129,7 +110,7 @@ struct SusPay_test : public beast::unit_test::suite
{
using namespace jtx;
Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentCreate";
jv[jss::TransactionType] = "EscrowCreate";
jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human();
jv[jss::Destination] = to.human();
@@ -147,7 +128,7 @@ struct SusPay_test : public beast::unit_test::suite
{
using namespace jtx;
Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentCreate";
jv[jss::TransactionType] = "EscrowCreate";
jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human();
jv[jss::Destination] = to.human();
@@ -164,7 +145,7 @@ struct SusPay_test : public beast::unit_test::suite
jtx::Account const& from, std::uint32_t seq)
{
Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentFinish";
jv[jss::TransactionType] = "EscrowFinish";
jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human();
jv["Owner"] = from.human();
@@ -179,7 +160,7 @@ struct SusPay_test : public beast::unit_test::suite
Slice condition, Slice fulfillment)
{
Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentFinish";
jv[jss::TransactionType] = "EscrowFinish";
jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human();
jv["Owner"] = from.human();
@@ -195,7 +176,7 @@ struct SusPay_test : public beast::unit_test::suite
jtx::Account const& from, std::uint32_t seq)
{
Json::Value jv;
jv[jss::TransactionType] = "SuspendedPaymentCancel";
jv[jss::TransactionType] = "EscrowCancel";
jv[jss::Flags] = tfUniversal;
jv[jss::Account] = account.human();
jv["Owner"] = from.human();
@@ -211,13 +192,7 @@ struct SusPay_test : public beast::unit_test::suite
using namespace jtx;
using namespace std::chrono;
{ // SusPay enabled
Env env(*this, features(featureSusPay));
env.fund(XRP(5000), "alice", "bob");
env(lockup("alice", "bob", XRP(1000), env.now() + 1s));
}
{ // SusPay not enabled
{ // Escrow not enabled
Env env(*this);
env.fund(XRP(5000), "alice", "bob");
env(lockup("alice", "bob", XRP(1000), env.now() + 1s), ter(temDISABLED));
@@ -225,50 +200,11 @@ struct SusPay_test : public beast::unit_test::suite
env(cancel("bob", "alice", 1), ter(temDISABLED));
}
{ // SusPay enabled, CryptoConditions disabled
Env env(*this,
features(featureSusPay));
{ // Escrow enabled
Env env(*this, features(featureEscrow));
env.fund(XRP(5000), "alice", "bob");
auto const seq = env.seq("alice");
// Fail: no cryptoconditions allowed
env(condpay("alice", "bob", XRP(1000),
makeSlice (cb1), env.now() + 1s), ter(temDISABLED));
// Succeed: doesn't have a cryptocondition
env(lockup("alice", "bob", XRP(1000),
env.now() + 1s));
// Fail: can't specify conditional finishes if
// cryptoconditions aren't allowed.
{
auto f = finish("bob", "alice", seq,
makeSlice(cb1), makeSlice(fb1));
env (f, ter(temDISABLED));
auto fnc = f;
fnc.removeMember ("Condition");
env (fnc, ter(temDISABLED));
auto fnf = f;
fnf.removeMember ("Fulfillment");
env (fnf, ter(temDISABLED));
}
// Succeeds
env(lockup("alice", "bob", XRP(1000), env.now() + 1s));
env.close();
env(finish("bob", "alice", seq));
}
{ // SusPay enabled, CryptoConditions enabled
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
env.fund(XRP(5000), "alice", "bob");
auto const seq = env.seq("alice");
@@ -287,23 +223,20 @@ struct SusPay_test : public beast::unit_test::suite
using namespace jtx;
using namespace std::chrono;
{
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
Env env(*this, features(featureEscrow));
auto const alice = Account("alice");
env.fund(XRP(5000), alice, "bob");
auto const alice = Account("alice");
env.fund(XRP(5000), alice, "bob");
auto const seq = env.seq(alice);
// set source and dest tags
env(condpay(alice, "bob", XRP(1000),
makeSlice (cb1), env.now() + 1s),
stag(1), dtag(2));
auto const sle = env.le(keylet::susPay(alice.id(), seq));
BEAST_EXPECT((*sle)[sfSourceTag] == 1);
BEAST_EXPECT((*sle)[sfDestinationTag] == 2);
}
auto const seq = env.seq(alice);
// set source and dest tags
env(condpay(alice, "bob", XRP(1000),
makeSlice (cb1), env.now() + 1s),
stag(1), dtag(2));
auto const sle = env.le(keylet::escrow(alice.id(), seq));
BEAST_EXPECT(sle);
BEAST_EXPECT((*sle)[sfSourceTag] == 1);
BEAST_EXPECT((*sle)[sfDestinationTag] == 2);
}
void
@@ -314,9 +247,7 @@ struct SusPay_test : public beast::unit_test::suite
using namespace jtx;
using namespace std::chrono;
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
Env env(*this, features(featureEscrow));
env.fund(XRP(5000), "alice", "bob");
env.close();
@@ -419,7 +350,7 @@ struct SusPay_test : public beast::unit_test::suite
using namespace std::chrono;
{ // Unconditional
Env env(*this, features(featureSusPay));
Env env(*this, features(featureEscrow));
env.fund(XRP(5000), "alice", "bob");
auto const seq = env.seq("alice");
env(lockup("alice", "alice", XRP(1000), env.now() + 1s));
@@ -434,9 +365,7 @@ struct SusPay_test : public beast::unit_test::suite
}
{ // Conditional
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
Env env(*this, features(featureEscrow));
env.fund(XRP(5000), "alice", "bob");
auto const seq = env.seq("alice");
env(lockup("alice", "alice", XRP(1000), makeSlice(cb2), env.now() + 1s));
@@ -459,9 +388,9 @@ struct SusPay_test : public beast::unit_test::suite
}
void
testCondPay()
testEscrowConditions()
{
testcase ("Conditional Payments");
testcase ("Escrow Conditions");
using namespace jtx;
using namespace std::chrono;
@@ -469,8 +398,7 @@ struct SusPay_test : public beast::unit_test::suite
{ // Test cryptoconditions
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
features(featureEscrow));
auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol");
@@ -507,7 +435,7 @@ struct SusPay_test : public beast::unit_test::suite
// Attempt to finish with the correct condition & fulfillment
env(finish("bob", "alice", seq, makeSlice(cb1), makeSlice(fb1)), fee(1500));
// SLE removed on finish
BEAST_EXPECT(! env.le(keylet::susPay(Account("alice").id(), seq)));
BEAST_EXPECT(! env.le(keylet::escrow(Account("alice").id(), seq)));
BEAST_EXPECT((*env.le("alice"))[sfOwnerCount] == 0);
env.require(balance("carol", XRP(6000)));
env(cancel("bob", "alice", seq), ter(tecNO_TARGET));
@@ -518,8 +446,7 @@ struct SusPay_test : public beast::unit_test::suite
{ // Test cancel when condition is present
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
features(featureEscrow));
auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol");
@@ -532,13 +459,11 @@ struct SusPay_test : public beast::unit_test::suite
env(cancel("bob", "alice", seq));
env.require(balance("alice", XRP(5000) - drops(10)));
// SLE removed on cancel
BEAST_EXPECT(! env.le(keylet::susPay(Account("alice").id(), seq)));
BEAST_EXPECT(! env.le(keylet::escrow(Account("alice").id(), seq)));
}
{
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
Env env(*this, features(featureEscrow));
auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol");
@@ -558,9 +483,7 @@ struct SusPay_test : public beast::unit_test::suite
}
{ // Test long & short conditions during creation
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
Env env(*this, features(featureEscrow));
auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol");
@@ -601,8 +524,7 @@ struct SusPay_test : public beast::unit_test::suite
{ // Test long and short conditions & fulfillments during finish
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
features(featureEscrow));
auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol");
@@ -687,9 +609,7 @@ struct SusPay_test : public beast::unit_test::suite
{ // Test empty condition during creation and
// empty condition & fulfillment during finish
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
Env env(*this, features(featureEscrow));
auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol");
@@ -726,6 +646,28 @@ struct SusPay_test : public beast::unit_test::suite
env.require(balance ("carol", XRP(6000)));
env.require(balance ("alice", XRP(4000) - drops(10)));
}
{ // Test a condition other than PreimageSha256, which
// would require a separate amendment
Env env(*this, features(featureEscrow));
auto T = [&env](NetClock::duration const& d)
{ return env.now() + d; };
env.fund(XRP(5000), "alice", "bob", "carol");
std::array<std::uint8_t, 45> cb =
{{
0xA2, 0x2B, 0x80, 0x20, 0x42, 0x4A, 0x70, 0x49, 0x49, 0x52,
0x92, 0x67, 0xB6, 0x21, 0xB3, 0xD7, 0x91, 0x19, 0xD7, 0x29,
0xB2, 0x38, 0x2C, 0xED, 0x8B, 0x29, 0x6C, 0x3C, 0x02, 0x8F,
0xA9, 0x7D, 0x35, 0x0F, 0x6D, 0x07, 0x81, 0x03, 0x06, 0x34,
0xD2, 0x82, 0x02, 0x03, 0xC8
}};
// FIXME: this transaction should, eventually, return temDISABLED
// instead of temMALFORMED.
env(condpay("alice", "carol", XRP(1000), makeSlice(cb), T(S{1})),
ter(temMALFORMED));
}
}
void
@@ -735,9 +677,7 @@ struct SusPay_test : public beast::unit_test::suite
using namespace jtx;
using namespace std::chrono;
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
Env env(*this, features(featureEscrow));
env.fund(XRP(5000), "alice", "bob", "carol");
env(condpay("alice", "carol", XRP(1000), makeSlice(cb1), env.now() + 1s));
@@ -751,9 +691,7 @@ struct SusPay_test : public beast::unit_test::suite
using namespace jtx;
using namespace std::chrono;
Env env(*this,
features(featureSusPay),
features(featureCryptoConditions));
Env env(*this, features(featureEscrow));
env.memoize("alice");
env.memoize("bob");
@@ -806,13 +744,13 @@ struct SusPay_test : public beast::unit_test::suite
testTags();
testFails();
testLockup();
testCondPay();
testEscrowConditions();
testMeta();
testConsequences();
}
};
BEAST_DEFINE_TESTSUITE(SusPay,app,ripple);
BEAST_DEFINE_TESTSUITE(Escrow,app,ripple);
} // test
} // ripple

View File

@@ -1,199 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/basics/strHex.h>
#include <ripple/beast/unit_test.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/Ed25519.h>
#include <ripple/protocol/PublicKey.h>
#include <ripple/protocol/SecretKey.h>
#include <vector>
namespace ripple {
namespace cryptoconditions {
class Ed25519_test : public beast::unit_test::suite
{
void
check (
std::array<std::uint8_t, 32> const& secretKey,
std::vector<std::uint8_t> const& message,
std::string const& fulfillment,
std::string const& condition)
{
SecretKey const sk { makeSlice (secretKey) };
PublicKey const pk = derivePublicKey (KeyType::ed25519, sk);
auto f = loadFulfillment (fulfillment);
auto c = loadCondition (condition);
BEAST_EXPECT (f);
BEAST_EXPECT (c);
if (f && c)
{
// Ensure that loading works correctly
BEAST_EXPECT (to_string (*f) == fulfillment);
BEAST_EXPECT (to_string (*c) == condition);
// Ensures that the fulfillment generates
// the condition correctly:
BEAST_EXPECT (f->condition() == c);
// Check fulfillment
BEAST_EXPECT (validate (*f, *c, makeSlice(message)));
// Check correct creation of fulfillment
BEAST_EXPECT (*f == Ed25519 (sk, pk, makeSlice(message)));
}
}
void testKnownVectors ()
{
testcase ("Known Vectors");
std::array<std::uint8_t, 32> sk =
{{
0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
}};
std::vector<std::uint8_t> const payload (512, 0x21);
check (sk, payload,
"cf:4:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVGfTbzglso5Uo3i2O2WVP6abH1dz5k0H5DLylizTeL5UC0VSptUN4VCkhtbwx3B00pCeWNy1H78rq6OTXzok-EH",
"cc:4:20:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
sk.fill (0x00);
check (sk, hexblob (""),
"cf:4:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2imPiVs8r-LJUGA50OKmY4JWgARnT-jSN3hQkuQNaq9IPk_GAWhwXzHxAVlhOM4hqjV8DTKgZPQj3D7kqjq_U_gD",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96");
sk.fill (0xff);
check (sk, hexblob ("616263"),
"cf:4:dqFZIESm5PURJlvKc6YE2QsFKdHfYCvjChmpJXZg0fWuxqtqkSKv8PfcuWZ_9hMTaJRzK254wm9bZzEB4mf-Litl-k1T2tR4oa2mTVD9Hf232Ukg3D4aVkpkexy6NWAB",
"cc:4:20:dqFZIESm5PURJlvKc6YE2QsFKdHfYCvjChmpJXZg0fU:96");
}
void testFulfillment ()
{
testcase ("Fulfillment");
std::array<std::uint8_t, 32> sk =
{{
0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
}};
std::vector<std::uint8_t> const v1 (512, 0x21);
std::vector<std::uint8_t> const v2 (512, 0x22);
Ed25519 const f ({ sk }, makeSlice(v1));
// First check against incorrect conditions:
char const* const ccs[] =
{
"cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256",
"cc:1:25:XkflBmyISKuevH8-850LuMrzN-HT1Ds9zKUEzaZ2Wk0:103",
"cc:2:2b:d3O4epRCo_3rj17Bf3v8hp5ig7vq84ivPok07T9Rdl0:146",
"cc:3:11:uKkFs6dhGZCwD51c69vVvHYSp25cRi9IlvXfFaxhMjo:518",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96"
};
for (auto cc : ccs)
{
auto c = loadCondition (cc);
if (BEAST_EXPECT (c))
{
BEAST_EXPECT (! validate (f, c.get(), makeSlice(v1)));
BEAST_EXPECT (! validate (f, c.get(), makeSlice(v2)));
}
}
// Now, finally, check the correct condition:
auto c = loadCondition (
"cc:4:20:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
if (BEAST_EXPECT (c))
{
BEAST_EXPECT (validate (f, c.get(), makeSlice(v1)));
BEAST_EXPECT (! validate (f, c.get(), makeSlice(v2)));
}
// Under the existing spec, multiple messages sharing
// the same key should generate the same fulfillment:
{
Ed25519 const f1 ({ sk }, makeSlice (v1));
Ed25519 const f2 ({ sk }, makeSlice (v2));
BEAST_EXPECT (f1.condition () == f2.condition ());
}
}
void testMalformedCondition ()
{
testcase ("Malformed Condition");
// This is malformed and will not load because a
// feature suite of 0 is not supported.
auto c1 = loadCondition (
"cc:4:0:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (!c1);
// The following will load but fail in different ways
auto c2 = loadCondition ( // only sha256
"cc:4:1:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (c2 && !validate(*c2));
auto c3 = loadCondition ( // only preimage
"cc:4:2:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (c3 && !validate(*c3));
auto c4 = loadCondition ( // sha256+preimage
"cc:4:3:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (c4 && !validate(*c4));
auto c5 = loadCondition ( // Ed25519+sha256+preimage
"cc:1:23:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c5 && !validate(*c5));
auto c6 = loadCondition ( // Ed25519+threshold
"cc:1:28:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c6 && !validate(*c6));
}
void run ()
{
testKnownVectors ();
testFulfillment ();
testMalformedCondition ();
}
};
BEAST_DEFINE_TESTSUITE (Ed25519, conditions, ripple);
}
}

View File

@@ -1,562 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/basics/strHex.h>
#include <ripple/beast/unit_test.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/PrefixSha256.h>
#include <ripple/conditions/PreimageSha256.h>
#include <ripple/conditions/Ed25519.h>
#include <ripple/conditions/impl/utils.h>
#include <numeric>
namespace ripple {
namespace cryptoconditions {
class PrefixSha256_test : public beast::unit_test::suite
{
void check (
Fulfillment const& f,
Condition const& c,
Slice test,
Slice good)
{
BEAST_EXPECT (validate (f, c, test) ==
((test == good) && (f.condition() == c)));
}
void testMalformedCondition ()
{
testcase ("Malformed Condition");
// This is malformed and will not load because a
// feature suite of 0 is not supported.
auto c1 = loadCondition (
"cc:1:0:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (!c1);
// The following will load but fail in different ways
auto c2 = loadCondition ( // only sha256
"cc:1:1:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c2 && !validate(*c2));
auto c3 = loadCondition ( // only preimage
"cc:1:4:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c3 && !validate(*c3));
auto c4 = loadCondition ( // only sha256+preimage
"cc:1:5:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c4 && !validate(*c4));
}
void testPrefix ()
{
testcase ("Prefix");
std::string const prefix1 = "prefix1";
std::string const prefix2 = "prefix2";
std::uint8_t msg[8];
std::iota (std::begin(msg), std::end(msg), std::uint8_t(39));
std::array<std::uint8_t, 32> const sk =
{{
0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
}};
{
PrefixSha256 f1;
f1.setPrefix(makeSlice (prefix1));
f1.setSubfulfillment(std::make_unique<PreimageSha256> (
makeSlice (prefix1)));
PrefixSha256 f2;
f2.setPrefix(makeSlice (prefix2));
f2.setSubfulfillment(std::make_unique<PreimageSha256> (
makeSlice (prefix1)));
BEAST_EXPECT (f1 != f2);
BEAST_EXPECT (f1.condition() != f2.condition());
// Validating with own condition should succeed.
BEAST_EXPECT (validate (f1, f1.condition(), {}));
BEAST_EXPECT (validate (f2, f2.condition(), {}));
for (std::size_t i = 1; i != sizeof(msg); ++i)
{
BEAST_EXPECT (validate (f1, f1.condition(),
Slice{msg, i}));
BEAST_EXPECT (validate (f2, f2.condition(),
Slice{msg, i}));
}
// The rest should fail:
BEAST_EXPECT (! validate (f1, f2.condition(), {}));
BEAST_EXPECT (! validate (f2, f1.condition(), {}));
for (std::size_t i = 1; i != sizeof(msg); ++i)
{
BEAST_EXPECT (! validate (f1, f2.condition(),
Slice{msg, i}));
BEAST_EXPECT (! validate (f2, f1.condition(),
Slice{msg, i}));
}
}
{
PrefixSha256 f1;
f1.setPrefix(makeSlice (prefix1));
f1.setSubfulfillment(std::make_unique<PreimageSha256> (
makeSlice (prefix1)));
PrefixSha256 f2;
f2.setPrefix(makeSlice (prefix2));
f2.setSubfulfillment(std::make_unique<PreimageSha256> (
makeSlice (prefix2)));
BEAST_EXPECT (f1 != f2);
BEAST_EXPECT (f1.condition() != f2.condition());
BEAST_EXPECT (validate (f1, f1.condition(), {}));
BEAST_EXPECT (validate (f2, f2.condition(), {}));
BEAST_EXPECT (! validate (f1, f2.condition(), {}));
BEAST_EXPECT (! validate (f2, f1.condition(), {}));
// For preimage conditions, the message shouldn't
// matter, so verify that it does not:
for (std::size_t i = 1; i != sizeof(msg); ++i)
{
BEAST_EXPECT (validate (f1, f1.condition(),
Slice(msg, i)));
BEAST_EXPECT (validate (f2, f2.condition(),
Slice(msg, i)));
BEAST_EXPECT (! validate (f1, f2.condition(),
Slice(msg, i)));
BEAST_EXPECT (! validate (f2, f1.condition(),
Slice(msg, i)));
}
}
{
PrefixSha256 f1;
f1.setPrefix(makeSlice (prefix1));
f1.setSubfulfillment(std::make_unique<Ed25519> (
SecretKey{ sk }, makeSlice (prefix1)));
PrefixSha256 f2;
f2.setPrefix(makeSlice (prefix2));
f2.setSubfulfillment(std::make_unique<Ed25519> (
SecretKey{ sk }, makeSlice (prefix2)));
BEAST_EXPECT (f1 != f2);
BEAST_EXPECT (f1.condition() != f2.condition());
BEAST_EXPECT (validate (f1, f1.condition(), {}));
BEAST_EXPECT (validate (f2, f2.condition(), {}));
BEAST_EXPECT (! validate (f1, f2.condition(), {}));
BEAST_EXPECT (! validate (f2, f1.condition(), {}));
// For non-prefix conditions, the message matters
// so verify that it does:
for (std::size_t i = 1; i < sizeof(msg); ++i)
{
BEAST_EXPECT (! validate (f1, f1.condition(),
Slice(msg, i)));
BEAST_EXPECT (! validate (f2, f2.condition(),
Slice(msg, i)));
BEAST_EXPECT (! validate (f1, f2.condition(),
Slice(msg, i)));
BEAST_EXPECT (! validate (f2, f1.condition(),
Slice(msg, i)));
}
}
{ // Test signing with non-empty prefix and non-empty
// message to ensure that the prefix is properly
// prepended to the message:
std::string const m = prefix1 + prefix2;
// Construct a prefix condition with the prefix
// prefix1, containing a Ed25519 signature for prefix1+prefix2
// and check that it passing prefix2 validates, while
// passing anything else fails:
PrefixSha256 f;
f.setPrefix(makeSlice (prefix1));
f.setSubfulfillment(std::make_unique<Ed25519> (
SecretKey{ sk }, makeSlice (m)));
BEAST_EXPECT (to_string(f) ==
"cf:1:B3ByZWZpeDEABGBEKZMGUASqHkxI9N0BWBlMA"
"jSbzGZM2W5ADqJpkYqNUTiaLmMYVDHrc-tKqXcmRIT"
"RFqtYxru4rMSIplCYRP71H9tD09mnfqw4eu5FAJZw1"
"wa_NOmw78ADIlB4_ENJWAo");
auto const c = f.condition();
BEAST_EXPECT (validate (f, c, makeSlice(prefix2)));
BEAST_EXPECT (! validate (f, c, {}));
BEAST_EXPECT (! validate (f, c, makeSlice(prefix1)));
BEAST_EXPECT (! validate (f, c, makeSlice(m)));
for (std::size_t i = 1; i < sizeof(msg); ++i)
BEAST_EXPECT (! validate (f, c, Slice(msg, i)));
}
}
void testKnown ()
{
testcase ("Known");
Slice const empty {};
Slice const abc { "abc", 3 };
Slice const abcd { "abcd", 4 };
Slice const vwxyz { "vwxyz", 5 };
{ // empty prefix with an empty PREIMAGE-SHA256 subfulfillment
auto f = loadFulfillment ("cf:1:AAAAAA");
BEAST_EXPECT (f);
{
auto const f2 = loadFulfillment(makeSlice(to_blob(*f)));
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
auto c = loadCondition ("cc:1:7:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c);
{
auto c1 = loadCondition (to_string(*c));
BEAST_EXPECT (c1);
BEAST_EXPECT (*c == *c1);
auto c2 = loadCondition (makeSlice(to_blob(*c)));
BEAST_EXPECT (c2);
BEAST_EXPECT (*c == *c2);
}
// Ensure that it has the correct features set
BEAST_EXPECT (f->features() ==
(feature_sha256 | feature_prefix | feature_preimage));
// Test manual construction
{
PrefixSha256 f2;
f2.setPrefix({});
f2.setSubfulfillment(loadFulfillment ("cf:0:"));
BEAST_EXPECT (f2 == *f);
BEAST_EXPECT (f2.condition() == *c);
}
// The PREIMAGE-SHA256 we contain validates for
// any message. So, this condition should work
// with any buffer:
check (*f, c.get(), empty, empty);
check (*f, c.get(), abc, abc);
check (*f, c.get(), abcd, abcd);
check (*f, c.get(), vwxyz, vwxyz);
}
{ // A PREFIX-SHA256 with an empty prefix, wrapping
// the PREFIX-SHA256 condition we created above
// which contains a PREIMAGE-SHA256
auto f = loadFulfillment ("cf:1:AAABBAAAAAA");
BEAST_EXPECT (f);
{
auto const f2 = loadFulfillment(makeSlice(to_blob(*f)));
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
auto c = loadCondition ("cc:1:7:Mp5A0CLrJOMAUMe0-qFb-_5U2C0X-iuwwfvumOT0go8:2");
BEAST_EXPECT (c);
{
auto c1 = loadCondition (to_string(*c));
BEAST_EXPECT (c1);
BEAST_EXPECT (*c == *c1);
auto c2 = loadCondition (makeSlice(to_blob(*c)));
BEAST_EXPECT (c2);
BEAST_EXPECT (*c == *c2);
}
// Ensure that it has the correct features set
BEAST_EXPECT (f->features() ==
(feature_sha256 | feature_prefix | feature_preimage));
// Test manual construction
{
PrefixSha256 f2;
f2.setPrefix({});
f2.setSubfulfillment(loadFulfillment ("cf:1:AAAAAA"));
BEAST_EXPECT (f2 == *f);
BEAST_EXPECT (f2.condition() == *c);
}
// The PREIMAGE-SHA256 we contain validates for
// any message. So, this condition should work
// with any buffer:
check (*f, c.get(), empty, empty);
check (*f, c.get(), abc, abc);
check (*f, c.get(), abcd, abcd);
check (*f, c.get(), vwxyz, vwxyz);
}
{ // A PREFIX-SHA256, with the prefix set to 'abc'
// that wraps around an ED25519 condition signing
// the message 'abc':
auto f = loadFulfillment (
"cf:1:A2FiYwAEYHahWSBEpuT1ESZbynOmBNkLBSnR32Ar4woZqSV2YNH1rsara"
"pEir_D33Llmf_YTE2iUcytueMJvW2cxAeJn_i4rZfpNU9rUeKGtpk1Q_R39t9l"
"JINw-GlZKZHscujVgAQ");
BEAST_EXPECT (f);
{
auto const f2 = loadFulfillment(makeSlice(to_blob(*f)));
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
auto c = loadCondition ("cc:1:25:KHqL2K2uisoMhxznwl-6pai-ENDk2x9Wru6Ls63O5Vs:100");
BEAST_EXPECT (c);
{
auto c1 = loadCondition (to_string(*c));
BEAST_EXPECT (c1);
BEAST_EXPECT (*c == *c1);
auto c2 = loadCondition (makeSlice(to_blob(*c)));
BEAST_EXPECT (c2);
BEAST_EXPECT (*c == *c2);
}
// Ensure that it has the correct features set
BEAST_EXPECT (f->features() ==
(feature_sha256 | feature_prefix | feature_ed25519));
// Test manual construction
{
PrefixSha256 f2;
f2.setPrefix(abc);
f2.setSubfulfillment(loadFulfillment (
"cf:4:dqFZIESm5PURJlvKc6YE2QsFKdHfYCvjChmpJXZg0fWuxqtqkSKv8"
"PfcuWZ_9hMTaJRzK254wm9bZzEB4mf-Litl-k1T2tR4oa2mTVD9Hf232Uk"
"g3D4aVkpkexy6NWAB"));
// Check the subfulfillment directly:
auto sc = f2.subcondition();
check (f2.subfulfillment(), sc, empty, abc);
check (f2.subfulfillment(), sc, abc, abc);
check (f2.subfulfillment(), sc, abcd, abc);
check (f2.subfulfillment(), sc, vwxyz, abc);
// This may seem counterintuitive, but it's
// not: the subfulfillment signed the message
// "abc"; our prefix is also "abc" so in order
// to verify this condition successfully, the
// message must be empty:
check (f2, c.get(), empty, empty);
check (f2, c.get(), abc, empty);
check (f2, c.get(), abcd, empty);
check (f2, c.get(), vwxyz, empty);
}
// Like before, the ED25519 condition we contain
// signed the message 'abc' which is our prefix
// which means that this will only validate with
// an empty message:
check (*f, c.get(), empty, empty);
check (*f, c.get(), abc, empty);
check (*f, c.get(), abcd, empty);
check (*f, c.get(), vwxyz, empty);
}
}
void testBinaryCodec()
{
testcase ("Binary Encoding");
// A sample prefix+Ed25519 fulfillment and its
// associated condition:
std::string const xf =
"cf:1:DUhlbGxvIFdvcmxkISAABGDsFyuTrV5WO_STL"
"HDhJFA0w1Rn7y79TWTr-BloNGfiv7YikfrZQy-PKYu"
"cSkiV2-KT9v_aGmja3wzN719HoMchKl_qPNqXo_TAP"
"qny6Kwc7IalHUUhJ6vboJ0bbzMcBwo";
std::string const xc =
"cc:1:25:1EMtp3YUOBZgeW3lX1lOIoAbUjx9maUty9TMJpMgXo4:110";
// The subfulfillment for the above, along with its
// associated condition:
std::string const xsf =
"cf:4:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZa"
"DRn4r-2IpH62UMvjymLnEpIldvik_b_2hpo2t8Mze9"
"fR6DHISpf6jzal6P0wD6p8uisHOyGpR1FISer26CdG"
"28zHAcK";
std::string const xsc =
"cc:4:20:7Bcrk61eVjv0kyxw4SRQNMNUZ-8u_U1k6_gZaDRn4r8:96";
auto f = loadFulfillment (xf);
BEAST_EXPECT (f);
BEAST_EXPECT (to_string(*f) == xf);
auto c = loadCondition (xc);
BEAST_EXPECT (c);
BEAST_EXPECT (to_string(*c) == xc);
BEAST_EXPECT (f->condition() == c);
BEAST_EXPECT (to_string(f->condition()) == xc);
auto subf = loadFulfillment (xsf);
BEAST_EXPECT (subf);
BEAST_EXPECT (to_string(*subf) == xsf);
auto subc = loadCondition (xsc);
BEAST_EXPECT (subc);
BEAST_EXPECT (to_string(*subc) == xsc);
// Now generate the binary versions and ensure
// that they match what we expect. Then load them
// and ensure they're identical:
{
auto const fblob1 = hexblob(
"0001710d48656c6c6f20576f726c642120000460ec172b93ad5e563bf4"
"932c70e1245034c35467ef2efd4d64ebf819683467e2bfb62291fad943"
"2f8f298b9c4a4895dbe293f6ffda1a68dadf0ccdef5f47a0c7212a5fea"
"3cda97a3f4c03ea9f2e8ac1cec86a51d452127abdba09d1b6f331c070a");
auto const fblob2 = to_blob(*f);
BEAST_EXPECT (fblob1 == fblob2);
auto f2 = loadFulfillment(makeSlice(fblob2));
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
{
auto const cblob1 = hexblob (
"0001012520d4432da77614381660796de55f594e22801b523c7d99a52d"
"cbd4cc2693205e8e016e");
auto const cblob2 = to_blob(*c);
BEAST_EXPECT (cblob1 == cblob2);
auto c2 = loadCondition(makeSlice(cblob2));
BEAST_EXPECT (c2);
BEAST_EXPECT (*c == c2);
}
}
void testNested()
{
testcase ("Nested");
std::string const abc = "abc";
std::string const def = "def";
std::string const abcdef = abc + def;
{ // prefix ("abc", prefix ("def", ed25519 (..., "abcdef")))
std::array<std::uint8_t, 32> sk =
{{
0x50, 0xd8, 0x58, 0xe0, 0x98, 0x5e, 0xcc, 0x7f,
0x60, 0x41, 0x8a, 0xaf, 0x0c, 0xc5, 0xab, 0x58,
0x7f, 0x42, 0xc2, 0x57, 0x0a, 0x88, 0x40, 0x95,
0xa9, 0xe8, 0xcc, 0xac, 0xd0, 0xf6, 0x54, 0x5c
}};
auto edf = std::make_unique<Ed25519> (
SecretKey{ sk }, makeSlice (abcdef));
// Inner
auto pif = std::make_unique<PrefixSha256>();
pif->setPrefix(makeSlice (abc));
pif->setSubfulfillment (std::move(edf));
// Outer
auto pof = std::make_unique<PrefixSha256>();
pof->setPrefix(makeSlice (def));
pof->setSubfulfillment (std::move (pif));
auto const c = pof->condition();
// The condition should validate with an empty
// message, since the nested prefixes contain
// the full message.
check (*pof, c, {}, {});
// It should fail with anything else.
check (*pof, c, makeSlice (abc), {});
check (*pof, c, makeSlice (def), {});
check (*pof, c, makeSlice (abcdef), {});
}
{ // prefix ("abc", prefix ("def", preimage (...)))
auto const v = hexblob (
"6B62BA0A77D5C7A423A5FC937EE5FF09");
auto img = std::make_unique<PreimageSha256> (
makeSlice (v));
// Inner
auto pif = std::make_unique<PrefixSha256>();
pif->setPrefix(makeSlice (abc));
pif->setSubfulfillment (std::move(img));
// Outer
auto pof = std::make_unique<PrefixSha256>();
pof->setPrefix(makeSlice (def));
pof->setSubfulfillment (std::move (pif));
auto const c = pof->condition();
// The condition should validate with any message
// since it terminates at a preimage, which
// validates for any message:
check (*pof, c, {}, {});
check (*pof, c, makeSlice (abc), makeSlice (abc));
check (*pof, c, makeSlice (def), makeSlice (def));
check (*pof, c, makeSlice (abcdef), makeSlice (abcdef));
}
}
void run ()
{
testKnown ();
testNested ();
testPrefix ();
testBinaryCodec ();
testMalformedCondition ();
}
};
BEAST_DEFINE_TESTSUITE (PrefixSha256, conditions, ripple);
}
}

View File

@@ -17,13 +17,16 @@
*/
//==============================================================================
#include <ripple/basics/Buffer.h>
#include <ripple/basics/strHex.h>
#include <ripple/basics/Slice.h>
#include <ripple/beast/unit_test.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/PreimageSha256.h>
#include <ripple/conditions/impl/PreimageSha256.h>
#include <algorithm>
#include <numeric>
#include <string>
#include <utility>
#include <vector>
namespace ripple {
@@ -31,305 +34,155 @@ namespace cryptoconditions {
class PreimageSha256_test : public beast::unit_test::suite
{
void
check (
std::vector<std::uint8_t> const& payload,
std::string const& fulfillment,
std::string const& condition)
inline
Buffer
hexblob(std::string const& s)
{
auto f = loadFulfillment (fulfillment);
auto c = loadCondition (condition);
std::vector<std::uint8_t> x;
x.reserve(s.size() / 2);
BEAST_EXPECT (f);
BEAST_EXPECT (c);
auto iter = s.cbegin();
if (f && c)
while (iter != s.cend())
{
// Ensure that loading works correctly
BEAST_EXPECT (to_string (*f) == fulfillment);
int cHigh = charUnHex(*iter++);
{
auto f2 = loadFulfillment (makeSlice(to_blob (*f)));
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
if (cHigh < 0)
return {};
BEAST_EXPECT (to_string (*c) == condition);
int cLow = charUnHex(*iter++);
{
auto c1 = loadCondition (makeSlice(to_blob (*c)));
BEAST_EXPECT (c1);
BEAST_EXPECT (*c == *c1);
if (cLow < 0)
return {};
auto c2 = loadCondition (to_string (*c));
BEAST_EXPECT (c2);
BEAST_EXPECT (*c == *c2);
}
// Ensures that the fulfillment generates
// the condition correctly:
BEAST_EXPECT (f->condition() == c);
// Ensure that the fulfillment contains the
// correct payload:
BEAST_EXPECT (f->payload() == makeSlice(payload));
// Check fulfillment
BEAST_EXPECT (validate (*f, *c, {}));
// Preimage ignores the message. Verify that it does:
std::string test = "aaabbc";
do
{
for (Slice t = makeSlice (test); !t.empty(); t += 1)
BEAST_EXPECT (validate (*f, *c, t));
} while (std::next_permutation(test.begin(), test.end()));
x.push_back(
static_cast<std::uint8_t>(cHigh << 4) |
static_cast<std::uint8_t>(cLow));
}
return { x.data(), x.size() };
}
void testKnownVectors ()
void
testKnownVectors()
{
testcase ("Known Vectors");
testcase("Known Vectors");
check (hexblob (""),
"cf:0:",
"cc:0:3:47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU:0");
check (hexblob ("00"),
"cf:0:AA",
"cc:0:3:bjQLnP-zepicpUTmu3gKLHiQHT-zNzh2hRGjBhevoB0:1");
check (hexblob ("ff"),
"cf:0:_w",
"cc:0:3:qBAK5qoZQNC2Y7sxzUZhQuu9vVGHExuS2TgYmHgy64k:1");
check (hexblob ("feff"),
"cf:0:_v8",
"cc:0:3:8ZdpKBDUV-KX_OnFZTsCWB_5mlCFI3DynX5f5H2dN-Y:2");
check (hexblob ("fffe"),
"cf:0:__4",
"cc:0:3:s9UQ7wQnXKjmmOWzy7Ds45Se-SUvDNyDnp7jR0CaIgk:2");
check (hexblob ("00ff"),
"cf:0:AP8",
"cc:0:3:But9amnuGeX733SQGNPSq_oEvL0TZdsxLrhtxxaTibg:2");
check (hexblob ("0001"),
"cf:0:AAE",
"cc:0:3:tBP0fRPuL-bIRbLuFBr4HehY307FSaWLeXC7lmRbyNI:2");
check (hexblob ("616263"),
"cf:0:YWJj",
"cc:0:3:ungWv48Bz-pBQUDeXa4iI7ADYaOWF3qctBD_YfIAFa0:3");
check (hexblob ("f1f2f3f4f5f6f7f8f9fafbfcfdfeff"),
"cf:0:8fLz9PX29_j5-vv8_f7_",
"cc:0:3:ipyQ4jcC1AbAYiuzYDZ1YkAr4O1IxOe5XBKJdJ17nPA:15");
std::vector<std::uint8_t> const v1 (256, 0x00);
check (v1,
"cf:0:"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAA",
"cc:0:3:U0HmsmRpeacOV2UwB6HzEBaUIeyb3Z8aVkj3Wt4AWvE:256");
std::vector<std::uint8_t> const v2 (256, 0xFF);
check (v2,
"cf:0:"
"__________________________________________________________________________________"
"__________________________________________________________________________________"
"__________________________________________________________________________________"
"__________________________________________________________________________________"
"_____________w",
"cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256");
std::vector<std::uint8_t> v3 (256);
std::iota (v3.begin(), v3.end(), std::uint8_t(0));
check (v3,
"cf:0:"
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD"
"0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6"
"e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7"
"i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T1"
"9vf4-fr7_P3-_w",
"cc:0:3:QK_y6dLYki5Hr9RkjmlnSXFYeF-9Hahw5xECZr-USIA:256");
std::vector<std::uint8_t> v4 (4096);
std::iota (v4.begin(), v4.end(), std::uint8_t(0));
check (v4,
"cf:0:"
"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD"
"0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6"
"e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7"
"i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T1"
"9vf4-fr7_P3-_wABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMj"
"M0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9w"
"cXJzdHV2d3h5ent8fX5_gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp-goaKjpKWmp6ipqqusra"
"6vsLGys7S1tre4ubq7vL2-v8DBwsPExcbHyMnKy8zNzs_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err"
"7O3u7_Dx8vP09fb3-Pn6-_z9_v8AAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKC"
"kqKywtLi8wMTIzNDU2Nzg5Ojs8PT4_QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVm"
"Z2hpamtsbW5vcHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo-QkZKTlJWWl5iZmpucnZ6foKGio6"
"SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr_AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3-Dh"
"4uPk5ebn6Onq6-zt7u_w8fLz9PX29_j5-vv8_f7_AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh"
"8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltc"
"XV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZ"
"qbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX"
"2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_wABAgMEBQYHCAkKCwwNDg8QERITFB"
"UWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFS"
"U1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5_gIGCg4SFhoeIiYqLjI2Oj5"
"CRkpOUlZaXmJmam5ydnp-goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2-v8DBwsPExcbHyMnKy8zN"
"zs_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err7O3u7_Dx8vP09fb3-Pn6-_z9_v8AAQIDBAUGBwgJCg"
"sMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4_QEFCQ0RFRkdI"
"SUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1-f4CBgoOEhY"
"aHiImKi4yNjo-QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr_AwcLD"
"xMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3-Dh4uPk5ebn6Onq6-zt7u_w8fLz9PX29_j5-vv8_f7_AA"
"ECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-"
"P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3"
"x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5"
"uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19v"
"f4-fr7_P3-_wABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0"
"NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcX"
"JzdHV2d3h5ent8fX5_gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp-goaKjpKWmp6ipqqusra6v"
"sLGys7S1tre4ubq7vL2-v8DBwsPExcbHyMnKy8zNzs_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err7O"
"3u7_Dx8vP09fb3-Pn6-_z9_v8AAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkq"
"KywtLi8wMTIzNDU2Nzg5Ojs8PT4_QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2"
"hpamtsbW5vcHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo-QkZKTlJWWl5iZmpucnZ6foKGio6Sl"
"pqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr_AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3-Dh4u"
"Pk5ebn6Onq6-zt7u_w8fLz9PX29_j5-vv8_f7_AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8g"
"ISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV"
"5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqb"
"nJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2N"
"na29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_wABAgMEBQYHCAkKCwwNDg8QERITFBUW"
"FxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1"
"RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5_gIGCg4SFhoeIiYqLjI2Oj5CR"
"kpOUlZaXmJmam5ydnp-goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2-v8DBwsPExcbHyMnKy8zNzs"
"_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err7O3u7_Dx8vP09fb3-Pn6-_z9_v8AAQIDBAUGBwgJCgsM"
"DQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4_QEFCQ0RFRkdISU"
"pLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaH"
"iImKi4yNjo-QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr_AwcLDxM"
"XGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3-Dh4uPk5ebn6Onq6-zt7u_w8fLz9PX29_j5-vv8_f7_AAEC"
"AwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0"
"BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9"
"fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5ur"
"u8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4"
"-fr7_P3-_wABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NT"
"Y3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJz"
"dHV2d3h5ent8fX5_gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp-goaKjpKWmp6ipqqusra6vsL"
"Gys7S1tre4ubq7vL2-v8DBwsPExcbHyMnKy8zNzs_Q0dLT1NXW19jZ2tvc3d7f4OHi4-Tl5ufo6err7O3u"
"7_Dx8vP09fb3-Pn6-_z9_v8AAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKy"
"wtLi8wMTIzNDU2Nzg5Ojs8PT4_QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hp"
"amtsbW5vcHFyc3R1dnd4eXp7fH1-f4CBgoOEhYaHiImKi4yNjo-QkZKTlJWWl5iZmpucnZ6foKGio6Slpq"
"eoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr_AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3-Dh4uPk"
"5ebn6Onq6-zt7u_w8fLz9PX29_j5-vv8_f7_AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gIS"
"IjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5f"
"YGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ"
"2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna"
"29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w",
"cc:0:3:yPXQNB1U2VGnGxNubir8sU0R7YSJp64Sao_uDfbs8ZM:4096");
}
void testOverlong ()
{
testcase ("Fulfillment Maximum Payload Length");
std::vector<std::uint8_t> v;
// As long as we don't exceed the fulfillment length
// we should succeed:
unexcept ([this, &v]()
std::pair<std::string, std::string> const known[] =
{
v.resize(maxSupportedFulfillmentLength - 1);
PreimageSha256 h1 { makeSlice(v) };
auto c1 = h1.condition();
BEAST_EXPECT (c1.maxFulfillmentLength == h1.payload().size());
v.resize(maxSupportedFulfillmentLength);
PreimageSha256 h2 { makeSlice(v) };
auto c2 = h2.condition();
BEAST_EXPECT (c2.maxFulfillmentLength == h2.payload().size());
});
except ([this, &v] ()
{
v.resize(maxSupportedFulfillmentLength + 1);
PreimageSha256 h3 { makeSlice(v) };
});
}
void testFulfillment ()
{
testcase ("Fulfillment");
std::vector<std::uint8_t> const v (256, 0x00);
PreimageSha256 const f (makeSlice(v));
// First check against incorrect conditions:
char const* const ccs[] =
{
"cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256",
"cc:1:25:XkflBmyISKuevH8-850LuMrzN-HT1Ds9zKUEzaZ2Wk0:103",
"cc:2:2b:d3O4epRCo_3rj17Bf3v8hp5ig7vq84ivPok07T9Rdl0:146",
"cc:3:11:uKkFs6dhGZCwD51c69vVvHYSp25cRi9IlvXfFaxhMjo:518",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96"
{ "A0028000",
"A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100" },
{ "A0058003616161",
"A02580209834876DCFB05CB167A5C24953EBA58C4AC89B1ADF57F28F2F9D09AF107EE8F0810103" },
};
for (auto cc : ccs)
{
auto c = loadCondition (cc);
std::error_code ec;
if (BEAST_EXPECT (c))
{
BEAST_EXPECT (! validate (f, c.get(), {}));
for (Slice m = makeSlice(v); !m.empty(); m += 1)
BEAST_EXPECT (! validate (f, c.get(), m));
}
}
auto f1 = Fulfillment::deserialize (hexblob(known[0].first), ec);
BEAST_EXPECT (f1);
BEAST_EXPECT (!ec);
// Now, finally, check the correct condition:
auto c = loadCondition (
"cc:0:3:U0HmsmRpeacOV2UwB6HzEBaUIeyb3Z8aVkj3Wt4AWvE:256");
auto c1 = Condition::deserialize (hexblob(known[0].second), ec);
BEAST_EXPECT (c1);
BEAST_EXPECT (!ec);
if (BEAST_EXPECT (c))
{
// Note that the message may not, necessarily
// have anything to do with the fulfillment, so
// we expect the validation to succeed with any
// message:
BEAST_EXPECT (validate (f, c.get(), {}));
for (Slice m = makeSlice(v); !m.empty(); m += 1)
BEAST_EXPECT (validate (f, c.get(), m));
}
auto f2 = Fulfillment::deserialize(hexblob(known[1].first), ec);
BEAST_EXPECT(f2);
BEAST_EXPECT(!ec);
auto c2 = Condition::deserialize(hexblob(known[1].second), ec);
BEAST_EXPECT(c2);
BEAST_EXPECT(!ec);
// Check equality and inequality
BEAST_EXPECT (f1->condition() == *c1);
BEAST_EXPECT (f1->condition() != *c2);
BEAST_EXPECT (f2->condition() == *c2);
BEAST_EXPECT (f2->condition() != *c1);
BEAST_EXPECT (*c1 != *c2);
BEAST_EXPECT (*c1 == *c1);
BEAST_EXPECT (f1->condition() == f1->condition());
// Should validate with the empty string
BEAST_EXPECT (validate (*f1, *c1));
BEAST_EXPECT (validate(*f2, *c2));
// And with any string - the message doesn't matter for PrefixSha256
BEAST_EXPECT (validate (*f1, *c1, makeSlice(known[0].first)));
BEAST_EXPECT (validate(*f1, *c1, makeSlice(known[0].second)));
BEAST_EXPECT (validate(*f2, *c2, makeSlice(known[0].first)));
BEAST_EXPECT (validate(*f2, *c2, makeSlice(known[0].second)));
// Shouldn't validate if the fulfillment & condition don't match
// regardless of the message.
BEAST_EXPECT (! validate(*f2, *c1));
BEAST_EXPECT (! validate(*f2, *c1, makeSlice(known[0].first)));
BEAST_EXPECT (! validate(*f2, *c1, makeSlice(known[0].second)));
BEAST_EXPECT (! validate(*f1, *c2));
BEAST_EXPECT (! validate(*f1, *c2, makeSlice(known[0].first)));
BEAST_EXPECT (! validate(*f1, *c2, makeSlice(known[0].second)));
}
void testMalformedCondition ()
void testOtherTypes()
{
testcase ("Malformed Condition");
testcase ("Other Types");
// This is malformed and will not load because a
// feature suite of 0 is not supported.
auto c1 = loadCondition (
"cc:0:0:U0HmsmRpeacOV2UwB6HzEBaUIeyb3Z8aVkj3Wt4AWvE:256");
BEAST_EXPECT (!c1);
std::pair<std::string, std::string> const others[] =
{
// PREFIX + PREIMAGE:
{ "A10B8000810100A204A0028000",
// The following will load but fail in different ways
auto c2 = loadCondition ( // only sha256
"cc:0:1:U0HmsmRpeacOV2UwB6HzEBaUIeyb3Z8aVkj3Wt4AWvE:256");
BEAST_EXPECT (c2 && !validate(*c2));
"A12A8020BB1AC5260C0141B7E54B26EC2330637C5597BF811951AC09E744AD20FF77E287810204"
"0082020780" },
// THRESHOLD:
{ "A208A004A0028000A100",
"A22A8020B4B84136DF48A71D73F4985C04C6767A778ECB65BA7023B4506823BEEE7631B9810204"
"0082020780" },
// RSA:
{ "A382020880820100E1EF8B24D6F76B09C81ED7752AA262F044F04A874D43809D31CEA612F99B0C97"
"A8B4374153E3EEF3D66616843E0E41C293264B71B6173DB1CF0D6CD558C58657706FCF097F704C48"
"3E59CBFDFD5B3EE7BC80D740C5E0F047F3E85FC0D75815776A6F3F23C5DC5E797139A6882E38336A"
"4A5FB36137620FF3663DBAE328472801862F72F2F87B202B9C89ADD7CD5B0A076F7C53E35039F67E"
"D17EC815E5B4305CC63197068D5E6E579BA6DE5F4E3E57DF5E4E072FF2CE4C66EB45233973875275"
"9639F0257BF57DBD5C443FB5158CCE0A3D36ADC7BA01F33A0BB6DBB2BF989D607112F2344D993E77"
"E563C1D361DEDF57DA96EF2CFC685F002B638246A5B309B981820100BD42D6569F6599AED455F96B"
"C0ED08ED1480BF36CD9E1467F9C6F74461C9E3A749334B2F6404AA5F9F6BAFE76C347D069250B35D"
"1C970C793059EE733A8193F30FA78FEC7CAE459E3DDFD7633805D476940D0CB53D7FB389DCDAEAF6"
"E8CF48C4B5635430E4F2BCDFE505C2C0FC17B40D93C7EDB7C261EBF43895A705E024AA0549A660F7"
"0A32150647522DBE6B63520497CFF8F8D5D74768A27C5B86E580BE3FCDC96F1976293CBA0D58DFC6"
"0B518B632A6DC1E950C43E231FE1A379AA6DDCC52C70EDF851C6C0123A964261CFDB3857CD6CD5AD"
"C37D8DA2CC924EDAE1D84CF6124587F274C1FA3697DA2901F0269F03B243C03B614E0385E1961FAC"
"5000F9BB",
"A32580204849505152535455484950515253545548495051525354554849505152535455810101" },
// ED25519:
{ "A4648020D75A980182B10AB7D54BFED3C964073A0EE172F3DAA62325AF021A68F707511A8140E556"
"4300C360AC729086E2CC806E828A84877F1EB8E5D974D873E065224901555FB8821590A33BACC61E"
"39701CF9B46BD25BF5F0595BBE24655141438E7A100B",
"A4278020799239ABA8FC4FF7EABFBC4C44E69E8BDFED993324E12ED64792ABE289CF1D5F810302"
"0000" }
};
for (auto x : others)
{
std::error_code ec;
BEAST_EXPECT (!Fulfillment::deserialize(hexblob(x.first), ec));
BEAST_EXPECT (!Condition::deserialize (hexblob(x.second), ec));
}
auto c3 = loadCondition ( // only preimage
"cc:1:2:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c3 && !validate(*c3));
auto c4 = loadCondition ( // only prefix+sha256
"cc:1:20:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c4 && !validate(*c4));
}
void run ()
{
testKnownVectors ();
testOverlong ();
testFulfillment ();
testMalformedCondition ();
testKnownVectors();
testOtherTypes();
}
};

View File

@@ -1,362 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2016 Ripple Labs Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include <ripple/basics/strHex.h>
#include <ripple/beast/unit_test.h>
#include <ripple/conditions/Condition.h>
#include <ripple/conditions/Fulfillment.h>
#include <ripple/conditions/RsaSha256.h>
#include <algorithm>
namespace ripple {
namespace cryptoconditions {
class RsaSha256_test : public beast::unit_test::suite
{
// A well-known message, its fulfillment and its condition
std::string const knownMessage = "aaa";
std::string const knownFulfillment =
"cf:3:ggEA4e-LJNb3awnIHtd1KqJi8ETwSodNQ4CdMc6mEvmbDJeotDdBU-Pu89ZmF"
"oQ-DkHCkyZLcbYXPbHPDWzVWMWGV3Bvzwl_cExIPlnL_f1bPue8gNdAxeDwR_PoX8D"
"XWBV3am8_I8XcXnlxOaaILjgzakpfs2E3Yg_zZj264yhHKAGGL3Ly-HsgK5yJrdfNW"
"woHb3xT41A59n7RfsgV5bQwXMYxlwaNXm5Xm6beX04-V99eTgcv8s5MZutFIzlzh1J"
"1ljnwJXv1fb1cRD-1FYzOCj02rce6AfM6C7bbsr-YnWBxEvI0TZk-d-VjwdNh3t9X2"
"pbvLPxoXwArY4JGpbMJuYIBAEjolF7-AHVW1b9NXySeSAj3MH4pUR0yYtrvYdiAmPm"
"qSovAYjqMl1c49l1r9FnVQ_KJ1zy8evTqOjP78-xEQER5EdcilAkeVhgzYo5Jp3LtY"
"I3mxEWVqR4-F9bPXsOyUo1j0q3WRjmJsS7sV332Rwlg32gyqdhMNg0cIXrWTIYlvbW"
"U-wraCGzey73lgNQkv5dG0vDDEoJtu7AK1otSxMt9RxVro146mByXOGN5LMgNBKGAI"
"QpSQVhltks6YXdLHTl114qYsIIe5Vyg-GMF1CUp4Q6wFc79QC-1myq7je7lKm8kR9I"
"oRgPSGc1OjPnP_dVJiInDeAtZ3WpX731zJiA";
std::string const knownCondition =
"cc:3:11:uKkFs6dhGZCwD51c69vVvHYSp25cRi9IlvXfFaxhMjo:518";
// Some RSA keys we use to check
std::string const goodKey =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIEpAIBAAKCAQEAq9QZZzSmdXaAFeSkUgK8/xuyKPQEFNkiEzatMSmmGN+DpCR7\n"
"HAK4W3wHfW6jegQFPlsvWLWbtnwgwCHhv1oW4jiL7BDD3prJIuJmhCE/w6WPKTFb\n"
"WhvxQY5sbqCDnjcd0x/adKjNLaTpSRANscR+hahbQA1vqPperHz/Z20reRPQ6aDn\n"
"w+qBL7dnVFgCPu8QrueyaZ0I5xQMIJiF0CnLXbPbU0ybxDVNgBDXsYeHE4VEM7ek\n"
"NQAMrsr6wodD5y94jynXHharEv5dzKsQFPRAGKzTvwQqWSiZr+Fgq4q1GqQW+oUa\n"
"xssbXWkGHEOxPBz6RFqLOFd8JnM9yVlWs2nWDwIDAQABAoIBAQCW9WNQCbCImBBV\n"
"q6c1qdQzaDiwxBjl3BGUwb+M5qNXTN9RkP9bj4Q6U5AdAdu7sdaNfvzsubjQrOL1\n"
"CY9UVqiuHLHJNr1uT5yP+knIoZFsqIJK1WMFmnDtgFwBISIhGRkpx91cCoUgKbcO\n"
"in0Nha0Gbe+lKWjFExmj/rlAO2grGO4yYd+P27BZ99mHBXPMQIIwQbSeRUTBLiRy\n"
"VhN7Mb60wag2m4F9zriEzhcj7pePNKHvpqNiuT5FCVoUNZW2CqFoXgEghF1VdWj5\n"
"UovZITUCN9zrGdFHWQj3Hx1LZo3UQz3auUp4XQ89dIm1GefqcZYpnzth+D43UXtC\n"
"f33nK9shAoGBANOm6aAhAh8Ahtc+52u3ykTnRGwI+H3QvgvzE7keZGIUb0yKtd/+\n"
"yuxYI3DgN/Mn69p3pLrYh/CJ9VonhELYv4oekGZfCmqEtUxmkTa9EgTKAIxIDw9W\n"
"t/jNBzzBccF22kl9w+nYHNOqo8M9yUlx0xwLnfioVX45G4jouucfPUDxAoGBAM/V\n"
"CmykuH8vIYluyobldKFglXKeFMQKlKG+Dv9wG71RSYPyamd7lu3fPgZyGcbAGd49\n"
"/Wewpq8ieagjrTuCEGlI0lhrL35axvBDKQcS+LTp+uD6vpxnFm986cxLgYRTNq0/\n"
"eMUvJy72Ms5zajZUMdjM/nqTA9zpVDofL+xb6Ib/AoGAVheU/H+wvy+VqcR6mgRe\n"
"kHyKBm/3tCXOyEmOAkTsjEDHrRjXNlAL9us7L1TlLVFVzL3SEfa2BQ/47z0XvaEw\n"
"+FvKXPnX4NAudu9ZrixmQfBxHJ7LEXAy0U+E3B/Lx+gyjqZLpLk1sJu+lVJyqB9W\n"
"whevoE/Ixtkv7BbOv+ijH+ECgYAb0WQnzpRzUZenkZDCJYxK3WajhM06wD/MtmfD\n"
"gPn1iR/R7WyYlU5KYIsoybTxiVztBlcYvehRoMev3baeNHaF4R1mgFJHE1d1aUfg\n"
"joWDkZ3m5ykEPjgejBWvJpwbXhf/cHN10S3pd0Ktp30b8IELh8S4G111ADYp4WrE\n"
"tDiXeQKBgQCROYcVuUEfkOiKeGHkBGbbsgj77KzZ1x7wUBzueKAND9+e2kX8kcc2\n"
"lX9DDkShUvZau0EJtQsFTehsZZeeBwtSvWu1A+2Wn9D19Hxe0qJCNZ6bqYQM9i2S\n"
"JjI6wG7YYl/foT3B3Zf3A3G9gUOc+P5/dnUi+6r+l7GUvQ5WnVQjDw==\n"
"-----END RSA PRIVATE KEY-----\n";
std::string const shortKey =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MGMCAQACEQCtbMmYUOdPy+XwgP+xXzfrAgMBAAECEQCFPVJ5GpdMnxfbcKFUUb2\n"
"JAgkA2e+AQuY6Ns8CCQDLtxhLU8j0JQIJAKkphE9pUUp1AghYypxPMNy09QIISr\n"
"srXHy9nPk=\n"
"-----END RSA PRIVATE KEY-----\n";
std::string const longKey =
"-----BEGIN RSA PRIVATE KEY-----\n"
"MIIJYwIBAAKCAg4Aqx3vteNh7iinTC6EEVaYmCDAeQ2oxXcjYRlx5h1m0ddHplsr\n"
"/e2QbwqExsz8zK9Wlis3EiPBoVX/CI6JxJfCkrpUjLH22jP3J2KTjZ6BkZ7hahOW\n"
"iqttFYFCta9iGirDQk04Wfubbtc4JBHexNUBCyGClCN/Ovd8Yv3KpoL3YOBma6ct\n"
"40Gf2xvG2k6OlLiGg7zvsI2KyD/a0/xFoMrC/X5wWunRFvHZYeb4LM2hqGe79LeK\n"
"NrbdYN+p8S+jzS6jo91C9EoltmS9UNXaax51JT9G83sw1U6KMIWA1jK9+3C92mry\n"
"gkLkdqJzsCsHWLWUZFJfKrZE7KAbw1TBGPSjSakTY3KOgrMghP41k3XGDsmkLMgx\n"
"JGd/F46YqEOB5vfdiZs7y0MuYYZmKY1o6DBNu+cL+sKBzNdtkvTVEskhsrL/OzPA\n"
"sN8srZdQm/Kim+2yKR+lxWz2kamcV74KCQMn2ALlMflfbFG/6RyHGzbVbiGTlW/8\n"
"gJQY9acMIDaJF0cPeSAa7aEHcQE5o1zrBcORqkWYbhZpPZtuS7GZ3gXwwYHxXvzq\n"
"tg5rMTqvcVzS2o08O+q4BopqAkU0mTF64Yh7izJQ5WGgk+g058WohNm4QB5XPETi\n"
"WlGSsEJjuzpHECq19DNoDe88HhZCwrqJSOl07MN1LPete9YptE1J+zoRasNy5wxJ\n"
"NmZRdP2a61J5AgMBAAECggIOAIdoBQwVhqUDHn+2P2PI9q9LG4OvP2IiyKhJjkvd\n"
"8EMU6+nEM6eYmbaEyFTYWSNPjGEAiW+dQ9f7SPjocjRTMvEQ6V78ZK5+eJF9++0R\n"
"BM7Kvu1F2taYmJVv1+4VfrfeJu0MVg8+ftzTCeXhDjsLouu/9Khs/n0W4iMjWX0y\n"
"HbdXWzTM8g7nGywzasPNbh5ZdnhAxhsbpjqX7P3anu6CBJK7vwTyCTby4mYKc1Bg\n"
"2A9/JsibhI+PXNcPplbor+HpiixdJmJRWk5eoUCaOWCSlXiH/gkl7pqcr9V9j1nw\n"
"hU23BUUVZBmX/Vmza4B4TDPyXB6W4B/YY+orOEz1gGfTDnN3i5QiToshpnZ7BKJH\n"
"kI+NR/m47r+Dab7DFTpl0c9xEVDRtQj0vhKehzV3/FLJOVQtxMqXC+ZjW+rUfjuN\n"
"iNBWChGMMKOllJMvN6o+/lyA07RxSF1shgcfDNQQhnTCKnfz2SbKAxqnzLwVSrWZ\n"
"LldyoyTPKra5uYfldRSXCPM5Or0dSQYAaX8t9jxaYdHq7hT5w7C5lsJdlyeUFlO1\n"
"1+jl5VZ1MT7f6g7Poo+NMdOVsGT7N8D3ERUL/Kmspx1Gpdll5iRVWxtZDpg4ct3G\n"
"7NTxaJOe+gueNg49wo0I1k49gSQ+xnTOyKmzkOGaZ+Ncbapd4OIh+5Y/wuWA5FTi\n"
"msbBxaXyAg/snkOGaY8NEQKCAQcNatn6e2wuNDbvp0qtmG7ipU033acpGzpG0Zmc\n"
"l6mPO0uis8+7cSks0L3IL1Yh3qCR8CBd5gt9JyjVKL/USq7AO2v0Uhnbyn+qbh7f\n"
"476bifVq9iU31M001KccZ3B7Ev5wBsBCAT8GnP/SMxqdQHX6VxuDWc9/bdccidui\n"
"V3wBY1bDsxGNV80Gg5/n/p3gSlkFkdjv9g7Jl6ODTUP/1M8s7siF3Ah93846PBmI\n"
"CqfUgQhm42HEJQAi9dgA+Zhc1dyT1hwnhAhzPaNAaeWHQWCD8OM6WI+/24miAJMi\n"
"kNCDwIITr3H/tz8J5rA6yqXl30lDBKE7KSpUTqSegnSC2U2+29qMUtVlvwKCAQcM\n"
"wNzxi8PSFwLO/e9FBvcuhHCSYbAw3tHMgkNEItM+0wCUw9hpkIrq3XZLwh+GKopw\n"
"9Uqzo2xBq3LeZgiU0nokgnDoixvBTcawnXsR3Y7mQijJo0eG2Nukd+g5wJ7nRp7q\n"
"Rq0KHYzfER1UPcAPI2ZL4T1JU5sdmPqIZuZA0YGSIznQ5htBiQMB4zaJeNN7m4bK\n"
"7e/eEF8AbChzbKiNFzl4am1boPbIZK3xek5cS7pLv5G5vX4R4+t6UY1Na91XQG9l\n"
"YZzYb4cIxhmvy0/zVAjeJpZCJpAQjE67+IZdieEVe+xGNe7qC1TJN+pL1YNxJdi3\n"
"ZFAf9fCYH5Ir6Es+vXCRlyFhNBOFxwKCAQcBQehX30VONzqGz0jiaAzMVO2dtLo7\n"
"0f9uL6qT0Grlr4rxHqTzTjGrr4x5vGX4GqM1yileY3bkLc1X3M/Nl4o1HdyKMz+V\n"
"J687S8K8/N0aOp2zfooSZ3Er6FoZAWC7SBZsbVWLWg6MEh6vlnaCEk58PbmoX7xg\n"
"luy4EftxhX1rq+GvyZJ1iqr+V0ufNG+bW5xoNzj7lDXiksGSRqV+znT0IxTL5sks\n"
"8tKjBormAwmjksw0yE6bUVRn8l5iCQJMgQaBHGnbEjawhjBMkyAdwvTGqMbC6xXd\n"
"xzdo5WDktmm0T1Bhg+nNK2FPDj2p5OATYQ++pipuHveGmzA2YseEk9UDdBtRV1oE\n"
"hQKCAQcBZMpAc1+xA+bArCuLxZkZskuDE73neVJAITQsrAmd4f08RLLPxoYH6K/W\n"
"054SUW/TrFq/iup3ur7Q4yGo8d97Qe4I27rqww8lmfArIaVOMIi4kGluqSBHtvrf\n"
"5Nb4u1T+kT6zzkrozbwAysbEYL/7JuBFtSdMcr1eTrB3AO5CBCt7UspDvS9g822w\n"
"VE34QiTW5G3EPNHFAAzjoEpDMPiM2kSdMNgHSklgDFen6navdH3+aGDwn5HKSkNA\n"
"5LrJoDcMQ0CavoVpRgzkkzlnhBV8AYeGLySrSkoIbL5yVnEMogBOI/K6DQb0/nFS\n"
"XEEDCnnGgOXouD3Uwg59UeN3NcipgHSbZM+FXQKCAQcCswewWvkORgMxDYT68x6P\n"
"hZtkAuy7BxAQ2H7ToYxeiVy4SBELg3xFiSCLNwhdK8En5vmSo3WnjNuWOGZ4ywUe\n"
"KmbxNu7o+zMyOblNg/I6CQMSEuo6jHoLVc9QaODscGco3du8WjRwJ2DnA3HoBJ5F\n"
"L0XftzOCfSrBQfn0Fb2ej4nsaIw1z0wEaAnuDC18/VUHQ0rHl2K2QleX4FwBiyXK\n"
"qWzhAVuxskkfWe3Xgn58IT2MODSDnFhP8j6m0vq5lklwgfIi9c6+y0rmJbhSZI4N\n"
"b/o5HSpWAxfpaSnWzw5moN5JP6DmhGQzgnctW9YL2w4OfZ9jPHl+xWMlSGUd8TD2\n"
"QIpo2Qox/w==\n"
"-----END RSA PRIVATE KEY-----\n";
void check (
Fulfillment const& f,
Condition const& c,
Slice test,
Slice good)
{
BEAST_EXPECT (validate (f, c, test) ==
((test == good) && (f.condition() == c)));
}
void testKnown ()
{
testcase ("Known");
Slice m = makeSlice (knownMessage);
std::string test = "aaabc";
// Load and test string and binary and text
// serialization & deserialization
auto const f = loadFulfillment(knownFulfillment);
BEAST_EXPECT (f);
BEAST_EXPECT (to_string (*f) == knownFulfillment);
{
auto const f2 = loadFulfillment(makeSlice(to_blob(*f)));
BEAST_EXPECT (f2);
BEAST_EXPECT (*f == *f2);
}
// Verify the condition for this fulfillment and test
// binary and text serialization & deserialization
auto const c = f->condition();
BEAST_EXPECT (to_string(c) == knownCondition);
{
auto c1 = loadCondition (knownCondition);
BEAST_EXPECT (c1);
BEAST_EXPECT (c == *c1);
auto c2 = loadCondition (makeSlice(to_blob(c)));
BEAST_EXPECT (c2);
BEAST_EXPECT (c == *c2);
}
// First check against incorrect conditions, using both
// correct, incorrect and empty buffers.
char const* const ccs[] =
{
"cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256",
"cc:1:25:XkflBmyISKuevH8-850LuMrzN-HT1Ds9zKUEzaZ2Wk0:103",
"cc:2:2b:d3O4epRCo_3rj17Bf3v8hp5ig7vq84ivPok07T9Rdl0:146",
"cc:3:11:Mjmrcm06fOo-3WOEZu9YDSNfqmn0lj4iOsTVEurtCdI:518",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96"
};
for (auto cc : ccs)
{
auto nc = loadCondition (cc);
if (BEAST_EXPECT (nc && nc != c))
{
check (*f, nc.get(), makeSlice(test), m);
check (*f, nc.get(), m, m);
check (*f, nc.get(), { }, m);
}
}
// Now try against the correct condition with various
// buffers - most are incorrect, some are correct.
do
{
for (Slice t = makeSlice (test); !t.empty(); t += 1)
check (*f, c, t, m);
} while (std::next_permutation(test.begin(), test.end()));
// And with an empty buffer:
check (*f, c, Slice { }, m);
// Under the existing spec, multiple messages sharing
// the same key should generate the same fulfillment:
{
std::array<std::uint8_t, 3> aaa {{ 'a', 'a', 'a' }};
std::array<std::uint8_t, 3> bbb {{ 'b', 'b', 'b' }};
RsaSha256 f1;
BEAST_EXPECT (f1.sign (goodKey, makeSlice (aaa)));
RsaSha256 f2;
BEAST_EXPECT (f2.sign (goodKey, makeSlice (bbb)));
BEAST_EXPECT (f1.condition () == f2.condition ());
}
}
void testDynamic ()
{
testcase ("Dynamic");
Slice m = makeSlice (knownMessage);
std::string test = "aaabc";
RsaSha256 f;
BEAST_EXPECT (f.sign (goodKey, m));
{
auto const f2 = loadFulfillment(makeSlice(to_blob(f)));
BEAST_EXPECT (f2);
BEAST_EXPECT (f == *f2);
}
// Generate and verify the condition for this fulfillment:
auto const c = f.condition();
{
auto c1 = loadCondition (to_string(c));
BEAST_EXPECT (c1);
BEAST_EXPECT (c == *c1);
auto c2 = loadCondition (makeSlice(to_blob(c)));
BEAST_EXPECT (c2);
BEAST_EXPECT (c == *c2);
}
// First check against incorrect conditions, using both
// correct, incorrect and empty buffers.
char const* const ccs[] =
{
"cc:0:3:PWh2oBRt6FdusjlahY3hIT0bksZbd53zozHP1aRYRUY:256",
"cc:1:25:XkflBmyISKuevH8-850LuMrzN-HT1Ds9zKUEzaZ2Wk0:103",
"cc:2:2b:d3O4epRCo_3rj17Bf3v8hp5ig7vq84ivPok07T9Rdl0:146",
"cc:3:11:Mjmrcm06fOo-3WOEZu9YDSNfqmn0lj4iOsTVEurtCdI:518",
"cc:4:20:O2onvM62pC1io6jQKm8Nc2UyFXcd4kOmOsBIoYtZ2ik:96"
};
for (auto cc : ccs)
{
auto nc = loadCondition (cc);
if (BEAST_EXPECT (nc && nc != c))
{
check (f, nc.get(), makeSlice(test), m);
check (f, nc.get(), m, m);
check (f, nc.get(), { }, m);
}
}
// Now try against the correct condition with various
// buffers - most are incorrect, some are correct.
do
{
for (Slice t = makeSlice (test); !t.empty(); t += 1)
check (f, c, t, m);
} while (std::next_permutation(test.begin(), test.end()));
// And with an empty buffer:
check (f, c, Slice { }, m);
}
void testKeySize ()
{
testcase ("Key Sizes");
RsaSha256 f1;
BEAST_EXPECT (!f1.sign (longKey, makeSlice (knownMessage)));
RsaSha256 f2;
BEAST_EXPECT (!f2.sign (shortKey, makeSlice (knownMessage)));
}
void testMalformedCondition ()
{
testcase ("Malformed Condition");
// This is malformed and will not load because a
// feature suite of 0 is not supported.
auto c1 = loadCondition (
"cc:3:0:Mjmrcm06fOo-3WOEZu9YDSNfqmn0lj4iOsTVEurtCdI:518");
BEAST_EXPECT (!c1);
// The following will load but fail in different ways
auto c2 = loadCondition ( // only sha256
"cc:3:1:Mjmrcm06fOo-3WOEZu9YDSNfqmn0lj4iOsTVEurtCdI:518");
BEAST_EXPECT (c2 && !validate(*c2));
auto c3 = loadCondition ( // only preimage
"cc:3:2:Mjmrcm06fOo-3WOEZu9YDSNfqmn0lj4iOsTVEurtCdI:518");
BEAST_EXPECT (c3 && !validate(*c3));
auto c4 = loadCondition ( // sha256+preimage
"cc:4:3:RCmTBlAEqh5MSPTdAVgZTAI0m8xmTNluQA6iaZGKjVE:96");
BEAST_EXPECT (c4 && !validate(*c4));
auto c5 = loadCondition ( // Ed25519+sha256+preimage
"cc:1:23:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c5 && !validate(*c5));
auto c6 = loadCondition ( // rsa+sha256+threshold
"cc:1:19:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c6 && !validate(*c6));
auto c7 = loadCondition ( // rsa
"cc:1:10:Yja3qFj7NS_VwwE7aJjPJos-uFCzStJlJLD4VsNy2XM:1");
BEAST_EXPECT (c7 && !validate(*c7));
}
void run ()
{
testKnown();
testDynamic();
testKeySize();
testMalformedCondition();
}
};
BEAST_DEFINE_TESTSUITE (RsaSha256, conditions, ripple);
}
}

View File

@@ -39,7 +39,7 @@
#include <test/app/SetAuth_test.cpp>
#include <test/app/SetRegularKey_test.cpp>
#include <test/app/SHAMapStore_test.cpp>
#include <test/app/SusPay_test.cpp>
#include <test/app/Escrow_test.cpp>
#include <test/app/Taker_test.cpp>
#include <test/app/Transaction_ordering_test.cpp>
#include <test/app/TrustAndBalance_test.cpp>

View File

@@ -18,6 +18,3 @@
//==============================================================================
#include <test/conditions/PreimageSha256_test.cpp>
#include <test/conditions/PrefixSha256_test.cpp>
#include <test/conditions/RsaSha256_test.cpp>
#include <test/conditions/Ed25519_test.cpp>